Compare commits

...

217 Commits

Author SHA1 Message Date
f3fab1b3f6 tinker-base_4.1.0 2020-08-07 19:21:13 +08:00
5340a41298 fix bug 2020-08-07 14:24:31 +08:00
caf2a379d9 修复xapk解压后安装包路径没有写入数据库的问题 2020-08-06 16:55:51 +08:00
c59b79427c 修复xapk解压后安装包路径没有写入数据库的问题 2020-08-06 16:42:32 +08:00
e54fcca53e xapk解压失败增加MTA统计 2020-08-06 14:55:40 +08:00
ad416c6a5d xapk解压增加备用解决方案 2020-08-06 14:07:06 +08:00
4a65c1a5c8 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-08-05 15:53:00 +08:00
878529f646 fix 2020-08-05 15:52:47 +08:00
d01bc1e2d1 移除无用的ijkplayer依赖 2020-08-04 15:59:34 +08:00
d63f5f5ab2 调整广点通激活数据的发送时机 2020-08-04 10:01:00 +08:00
ad0fb7a55a 处理游戏图标类型不一致导致的闪退问题 2020-08-03 16:25:57 +08:00
b0258eef77 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-08-03 16:06:33 +08:00
86d4df8ad9 修复新增开服默认选择时间分钟不准问题 2020-08-03 16:06:20 +08:00
bb11d984f9 处理消息中心闪退问题 2020-08-03 14:56:34 +08:00
d3f3d2ca98 下载详细过程埋点更新 https://gitlab.ghzs.com/stats/stats-issues/-/issues/188 2020-07-31 18:19:52 +08:00
0c1e712c79 编辑封面禁止左右滑动(与图片缩放会有冲突)
修复xapk提示存储不足后没有把当前解压线程移除问题
2020-07-31 15:21:33 +08:00
e193a40651 修改预约弹窗号码提示动画的持续时间 2020-07-31 14:47:42 +08:00
03f36476cb Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-31 11:45:13 +08:00
a721637be3 预约弹窗增加号码提示弹出动画 2020-07-31 11:44:58 +08:00
722fb1ad64 调整视频数据UI间隔 2020-07-31 10:32:01 +08:00
52a05c3aa4 这是环境api版本改为v4d1d0 2020-07-31 10:31:30 +08:00
bcbdac6afc Merge remote-tracking branch 'origin/dev' into dev 2020-07-30 22:29:09 +08:00
a9507af3f4 修复镜像游戏的角标问题 2020-07-30 22:29:01 +08:00
68601ca8be 修改预约弹窗点击弹窗以外区域弹窗没有消失问题 2020-07-30 22:17:00 +08:00
a7adc27896 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-30 21:23:44 +08:00
d19d0eb571 修改预约弹窗的弹出位置 2020-07-30 21:23:30 +08:00
d8e365fe08 修改游戏评论显示异常 2020-07-30 21:12:17 +08:00
b34f61ce0d Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-30 18:10:05 +08:00
1f24d16f95 h5游戏增加关闭按钮开关控制 2020-07-30 18:10:00 +08:00
738074ec00 光环助手V4.1.0-游戏评论功能强化20200730测试问题 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/925 2020-07-30 18:09:07 +08:00
c8eee33475 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-30 17:13:25 +08:00
badf9f9c20 修改编辑封面UI
防止手机登录连续点击获取验证码重复发送问题
2020-07-30 17:13:19 +08:00
0398cc4ffc 修改视频数据总览 2020-07-30 15:59:55 +08:00
b88abf6b9a link为空拦截跳转 2020-07-30 15:37:35 +08:00
a0d193bc52 web跳转视频流添加gameId参数 2020-07-30 15:24:33 +08:00
6ac635f8c6 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-30 14:38:11 +08:00
30c7d71114 外部跳转增加qq群 2020-07-30 14:38:05 +08:00
0223b3ab22 修复查看图片的bug(0730测试) https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/940 2020-07-30 11:57:48 +08:00
c9663662d5 补充遗漏的游戏图标显示优化页面 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/939 2020-07-30 11:41:17 +08:00
12bed97638 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-30 11:11:25 +08:00
a857af970d 修复游戏评分一直显示加载中问题 2020-07-30 11:11:20 +08:00
7da621583f Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-30 11:08:38 +08:00
46a32b62c2 光环前端优化汇总(2020年7月第3周)20200729测试问题补充5 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/940 2020-07-30 11:08:33 +08:00
45bcc95e7d 游戏详情玩家评论数据同步 2020-07-30 11:07:44 +08:00
7d7bcfaa1d 补充遗漏的游戏图标显示优化页面 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/939 2020-07-30 10:45:23 +08:00
3a9a561c77 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
2020-07-30 10:08:03 +08:00
e8d344256a 光环助手V4.1.0-视频上传支持剪辑封面(20200729测试) https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/941 2020-07-30 10:07:34 +08:00
1a7c1119bf 微调UI 2020-07-29 17:44:24 +08:00
0b6a2503aa Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-29 17:32:01 +08:00
459b9f65a9 修改封面尝试修复截帧失败问题 2020-07-29 17:31:49 +08:00
6c1ebe531e 更改游戏图标圆角逻辑 2020-07-29 16:47:53 +08:00
604450292b Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-29 15:53:31 +08:00
e42361e84a 1.H5游戏跳转到指趣 2.修改web跳转到视频流闪退 2020-07-29 15:53:24 +08:00
a974652f7f 处理自动打包编译时间不对的问题 2020-07-29 14:40:51 +08:00
309ab54e90 微调UI 2020-07-29 14:40:07 +08:00
486d680c26 补充游戏图标显示优化的页面 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/939 2020-07-29 14:30:22 +08:00
61a5f3a275 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-29 14:22:08 +08:00
c865417a4b 光环助手V4.1.0-游戏评论功能强化20200729测试问题:4,6,7 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/925 2020-07-29 14:22:03 +08:00
b142feaaae 微调下载管理-游戏下载UI 2020-07-29 14:19:15 +08:00
7380854133 微调UI 2020-07-29 11:29:23 +08:00
8a0f185eda 修改视频上传UI 2020-07-29 10:59:17 +08:00
bd9604a53e 微调UI 2020-07-29 10:13:44 +08:00
1aeab51f94 修复上传视频后视频投稿页面无法及时刷新问题 2020-07-29 09:56:54 +08:00
b16a6fedc6 修改列表分页结束数量限制 2020-07-28 17:54:26 +08:00
b97c381ed4 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-28 17:43:04 +08:00
acb8c723a3 微调UI 2020-07-28 17:42:51 +08:00
4492307e23 处理刷新列表MarkerView会显示问题 2020-07-28 17:42:29 +08:00
e31224b332 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-28 17:13:22 +08:00
6933d6c590 修复预约弹窗键盘弹出延迟问题 2020-07-28 17:13:09 +08:00
3d58841ce5 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-28 16:59:45 +08:00
0654b0a25f 更改评论弹窗动画实现方式 2020-07-28 16:59:41 +08:00
44dde3f91b Merge branch 'video_poster' 2020-07-28 16:27:56 +08:00
abad30595f 光环助手V4.1.0-视频上传支持剪辑封面 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/941 2020-07-28 15:14:51 +08:00
c4df411560 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-28 11:36:14 +08:00
2974fa4562 修改视频数据图表自定义MarkerView 2020-07-28 11:36:10 +08:00
b086b1cb0c 完成接入腾讯企点会话接待组件(0727测试4) https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/928 2020-07-28 11:24:12 +08:00
22cf026335 删除无用注释 2020-07-28 11:03:48 +08:00
90bd53fe61 修复选择游戏弹窗的闪退问题 2020-07-28 11:03:16 +08:00
9c580a356e 腾讯企点唤起改成应用内 2020-07-28 10:30:05 +08:00
7198c28e6f 补充遗漏的游戏图标角标位置 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/939 2020-07-28 10:29:33 +08:00
7bf074fddc 光环助手4.1.0-前端新增视频数据统计20200727测试1,4 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/936 2020-07-28 09:59:46 +08:00
bea1a336e9 视频编辑封面支持线上视频链接截取 2020-07-27 21:10:11 +08:00
97a9e03192 光环助手V4.1.0-视频上传支持剪辑封面(初步完成,后续细节尚需调整) https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/941 2020-07-27 18:05:42 +08:00
244d57b6bc 更改判断应用前后台状态的实现逻辑 2020-07-27 17:44:57 +08:00
3cbd484147 更改判断应用前后台状态的实现逻辑 2020-07-27 17:43:56 +08:00
8b81819c30 修改大图触发下拉消失的滑动阈值为大于60度 2020-07-27 16:31:53 +08:00
1873ac5d4e 光环前端优化汇总(2020年7月第3周)0727测试12 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/940 2020-07-27 16:29:06 +08:00
cf70c1e7fe 光环助手4.1.0-前端新增视频数据统计20200727UI测试 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/936 2020-07-27 15:49:05 +08:00
6f6b26ea4d Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-27 15:19:09 +08:00
e2708d9078 对接专区视频流接口 2020-07-27 15:19:05 +08:00
fb5a40c6e4 修复游戏图标的占位图显示问题 2020-07-27 12:03:03 +08:00
e2a10c1410 游戏评论显示折叠评论按钮 2020-07-27 12:01:08 +08:00
5461d3d548 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-27 11:45:26 +08:00
c47365e626 光环助手V4.1.0-游戏评论功能强化 20200725测试问题2,20200727UI测试 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/925 2020-07-27 11:45:22 +08:00
3476f8df3a 修复一些闪退问题 2020-07-27 11:14:48 +08:00
f812c1e5c8 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-27 09:24:51 +08:00
391f196005 修改游戏大事件时间转换错误 2020-07-27 09:24:46 +08:00
097dbca26e 修复客服消息列表页复制ID按钮不显示的问题 2020-07-24 18:34:29 +08:00
a147118381 完成游戏图标显示优化 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/939 2020-07-24 16:31:58 +08:00
e6bdde8273 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-24 15:27:27 +08:00
747757faa7 处理视频数据图标显示问题 2020-07-24 15:27:21 +08:00
6f21b9d0ae 腾讯企点改由内部 webview 跳转 2020-07-24 14:47:20 +08:00
e505554aac 修改视频数据图表配置 2020-07-24 11:18:18 +08:00
3323cce890 删除本地导入的videocache 2020-07-24 09:41:22 +08:00
ffd468db1e 格式化视频数据 2020-07-23 18:29:52 +08:00
e98e0d1522 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-23 17:12:07 +08:00
13a4f2014c 光环助手4.1.0-前端新增视频数据统计 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/936 2020-07-23 17:12:01 +08:00
8cbfe6450a Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-23 11:44:09 +08:00
bcc866888e 删除无用代码 2020-07-23 11:44:02 +08:00
60911d5dcb 移除无用的 glide 依赖 2020-07-23 11:15:35 +08:00
7e77d5749e 处理编译问题 2020-07-23 10:36:06 +08:00
6c3be5627d Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-23 10:02:41 +08:00
f6a9585700 彻底移除用数据库存储登录信息的代码 2020-07-23 10:02:30 +08:00
06fddb7dcd 完成游戏管理apk包提供海外下载地址 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/930 2020-07-22 18:21:01 +08:00
3a3f9a625b 删除部分测试代码 2020-07-22 17:49:54 +08:00
3702b104fc 光环助手V4.1.0-安装游戏支持解压xapk文件(20200722测试) https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/896 2020-07-22 17:39:46 +08:00
3e023089a1 Merge remote-tracking branch 'origin/dev' into dev 2020-07-22 16:24:37 +08:00
7455674f6b 完成7月第3周优化(3,11,14) https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/940 2020-07-22 16:24:26 +08:00
c93c0f2fc6 修改列表分页结束数量限制 2020-07-22 16:23:56 +08:00
727f02e571 光环助手V4.1.0-预约游戏自动填写手机号(20200722测试) https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/926 2020-07-22 15:15:25 +08:00
784fae1f5d Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-22 14:54:56 +08:00
42ef075912 光环助手V4.1.0-游戏名称点号处理(20200722补充) https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/921
修改dev api host
2020-07-22 14:54:44 +08:00
15307c5223 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-22 14:18:38 +08:00
aad3b48883 光环助手V4.1.0-视频详情功能优化20200722测试 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/935 2020-07-22 14:18:31 +08:00
d65d8f1c4c 删除多余刷新动画图片 2020-07-22 11:46:02 +08:00
c581496975 Merge branch 'refresh' into dev 2020-07-22 11:41:41 +08:00
d5481a8888 修改问答-推荐的下拉刷新动画 2020-07-22 11:24:08 +08:00
e0a61278fc fix import 2020-07-22 10:25:57 +08:00
2c36283833 Merge branch 'dev_4.1.0' into 'dev'
Dev 4.1.0

See merge request halo/assistant-android!21
2020-07-22 10:17:38 +08:00
1b5a8f3a7e Merge branch 'dev' into 'dev_4.1.0'
# Conflicts:
#   app/src/main/java/com/gh/common/DefaultUrlHandler.kt
#   app/src/main/java/com/gh/common/util/ShareUtils.java
#   dependencies.gradle
2020-07-22 10:17:19 +08:00
95c918b4e3 光环助手V4.1.0-游戏大事件功能优化 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/938 2020-07-22 09:47:57 +08:00
f32cc1673c 调整查看大图过渡动画 2020-07-21 18:32:32 +08:00
f0fc2f06da Merge branch 'dev_4.1.0' of gitlab.ghzhushou.com:halo/assistant-android into dev_4.1.0 2020-07-21 18:23:58 +08:00
b6c6abaa5b 调整大图动画 2020-07-21 18:23:33 +08:00
6ed67c911c Merge branch 'dev_4.1.0' of gitlab.ghzs.com:halo/assistant-android into dev_4.1.0 2020-07-21 18:14:44 +08:00
1eda223a1e 光环助手V4.1.0-预约游戏自动填写手机号 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/926 2020-07-21 18:14:27 +08:00
316c0c28ab 扩大安利墙列表的部分点击区域 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/940 2020-07-21 17:27:18 +08:00
895d4d5cf1 tinker_base 4.0.3 2020-07-21 17:18:49 +08:00
877df95e02 添加安利墙列表的部分点击区域 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/940 2020-07-21 17:15:57 +08:00
801f0b95e7 版本改为4.0.3 2020-07-21 17:01:07 +08:00
9019f555b5 Merge branch 'dev_4.1.0' of gitlab.ghzs.com:halo/assistant-android into dev_4.1.0 2020-07-21 16:52:00 +08:00
106b03a316 下载文件格式兼容xapk 2020-07-21 16:51:44 +08:00
d8faa554be 优化查看图片交互 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/940 2020-07-21 16:44:59 +08:00
93080a74a7 Merge branch 'dev_4.1.0' of gitlab.ghzs.com:halo/assistant-android into dev_4.1.0 2020-07-21 15:54:08 +08:00
3ea2ede0cb 视频上传增加视频宽高字段 2020-07-21 15:53:59 +08:00
1242848b6f Merge branch 'dev_4.1.0' of gitlab.ghzs.com:halo/assistant-android into dev_4.1.0 2020-07-21 15:35:50 +08:00
24d44a2c90 光环助手V4.1.0-游戏评论功能强化MTA数据统计 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/925 2020-07-21 15:35:45 +08:00
971779a529 Merge branch 'dev_4.1.0' of gitlab.ghzs.com:halo/assistant-android into dev_4.1.0 2020-07-21 15:28:59 +08:00
14f561c237 扩大下载管理删除按钮的点击范围 2020-07-21 15:28:38 +08:00
3a38e746f6 Merge branch 'dev_4.1.0' of gitlab.ghzs.com:halo/assistant-android into dev_4.1.0 2020-07-21 14:38:05 +08:00
dd3bc9d39d 光环助手V4.1.0-游戏评论功能强化1-5,6(1) https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/925 2020-07-21 14:38:01 +08:00
39a8062aef 光环后台功能迁移测试汇总0717测试-1 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/923 2020-07-21 14:35:52 +08:00
55b6ccb760 merge xapk branch 2020-07-21 14:35:01 +08:00
a8a55eb9bd 修复首页游戏隐藏评论后还能显示评分问题 2020-07-21 14:30:15 +08:00
3eb73439aa 修改页面loading动画 2020-07-21 11:30:29 +08:00
466e118579 尝试替换SwipeRefreshLayout的下拉动画 2020-07-20 17:58:51 +08:00
b38032074b 按帧导入下拉刷新动画,SwipeRefreshLayout以源码的方式引入到主工程 2020-07-20 17:54:56 +08:00
9ee771e528 Merge branch 'dev_4.1.0' of gitlab.ghzs.com:halo/assistant-android into dev_4.1.0 2020-07-20 17:04:11 +08:00
9253ed47e6 微信浏览器跳转到app 2020-07-20 17:04:03 +08:00
bc82263286 更改获取 bitmap 的实现 2020-07-20 16:41:46 +08:00
11979240ab Merge branch 'dev_4.0.1_bugfix' into dev
# Conflicts:
#	app/src/main/java/com/gh/common/DefaultUrlHandler.kt
#	dependencies.gradle
#	gradle.properties
2020-07-20 15:57:50 +08:00
255e6182a9 更改评论弹窗点击更多按钮交互方式 2020-07-20 15:41:39 +08:00
f170abb7ea 修复分享内容含 gif 图片时会分享失败的问题 2020-07-20 11:00:44 +08:00
eb80deb413 升级应用版本号为 4.1.0, 正式环境接口退回 4.0.2 (正式环境 4.1.0 API 暂未开启) 2020-07-20 10:58:15 +08:00
10ba5a9ba5 修改注释 2020-07-17 17:15:23 +08:00
94c49cba8b 修复裁剪控件图片过大显示异常问题 2020-07-17 17:13:54 +08:00
354dca8b04 非视频作者也可以删除自己的评论 2020-07-17 16:56:51 +08:00
034488ff34 非视频作者也可以删除自己的评论 2020-07-17 16:53:30 +08:00
59c70e23dd 光环前端优化汇总(2020年7月第3周)(5, 8, 12) https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/940 2020-07-17 16:04:45 +08:00
7d98a842f1 完成接入腾讯企点会话接待组件 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/928 2020-07-17 14:37:15 +08:00
5f8006dc5a Merge branch 'dev_4.1.0' of gitlab.ghzs.com:halo/assistant-android into dev_4.1.0 2020-07-17 11:24:10 +08:00
239bd69580 update LGLibrary version 2020-07-17 11:24:07 +08:00
bc96f102a1 光环前端优化汇总(2020年7月第3周)(2,4,6,7)https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/940 2020-07-17 11:19:13 +08:00
e2ef3f4d01 尝试解决登录失效后,无法打开登录界面问题 2020-07-17 10:47:07 +08:00
5c20bbf5e4 光环助手V4.1.0-视频详情功能优化 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/935 2020-07-16 18:09:54 +08:00
b07edd256a 增加登录异常MTA事件 2020-07-16 17:15:34 +08:00
1478d37889 Merge branch 'dev_4.1.0' of gitlab.ghzs.com:halo/assistant-android into dev_4.1.0
# Conflicts:
#	app/src/main/res/values/colors.xml
2020-07-16 16:40:24 +08:00
7151b56de3 光环助手V4.1.0-预约游戏自动填写手机号 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/926 2020-07-16 16:37:55 +08:00
220fd9528b 去掉部分页面游戏图标右上角的礼包角标 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/939 2020-07-16 14:43:10 +08:00
1c26c35571 完成首页轮播图数据调整 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/933 2020-07-16 11:03:35 +08:00
78eb6b7b02 Merge branch 'dev_4.1.0' of gitlab.ghzs.com:halo/assistant-android into dev_4.1.0 2020-07-16 09:11:10 +08:00
9dc9add896 折叠评论UI 2020-07-16 09:11:05 +08:00
2d09f8c008 游戏专区外部跳转 2020-07-16 09:07:14 +08:00
166e1e77ec 完成首页弹窗数据统计 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/881 2020-07-15 18:32:07 +08:00
ea782d002b 光环助手V4.1.0-资讯文章配置的超链接跳转逻辑优化2(1,2,3) 3 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/927 2020-07-15 17:27:10 +08:00
f525a3c46d 接口版本改为v4d1d0 2020-07-15 17:23:22 +08:00
bef6cbb212 tinker_base 4.0.2-183 2020-07-15 17:00:17 +08:00
5e0af8654a 完成新首页轮播图数据上报调整 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/933 2020-07-15 16:56:04 +08:00
032a89e0cd versionCode 改为183 2020-07-15 16:48:28 +08:00
c9afb6df02 Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev 2020-07-15 16:30:50 +08:00
b8092447ff 游戏列表默认不显示评分 2020-07-15 16:30:43 +08:00
bff20bea49 修复 7.1.1 系统上 toast 可能出现的闪退问题 2020-07-15 16:29:34 +08:00
87f2d9c85f Merge branch 'dev' of gitlab.ghzs.com:halo/assistant-android into dev
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
2020-07-15 15:04:43 +08:00
154dfc8538 DsBridge原生Api支持无参方法 2020-07-15 15:03:56 +08:00
59c4176983 尝试根据视频帧数获取视频截图 2020-07-15 14:40:10 +08:00
fddcdfb3aa 更改今日头条SDK的初始化位置 2020-07-15 09:41:29 +08:00
cdbf7d39a5 光环助手V4.1.0-资讯文章配置的超链接跳转逻辑优化(1) https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/927 2020-07-14 17:56:18 +08:00
6a55821d4d 完成一些todo 2020-07-14 10:16:45 +08:00
a305db7b13 tinker_base 4.0.2-182 2020-07-13 17:14:15 +08:00
58b1cd4b12 versionCode 改为182 2020-07-13 16:45:19 +08:00
5e7559e43f 修复一些闪退问题 2020-07-13 16:42:03 +08:00
2a74e35388 修复 7.1.1 系统上 toast 可能出现的闪退问题 2020-07-13 16:41:22 +08:00
5b9bef79da 修复一些闪退问题 2020-07-13 16:01:11 +08:00
e89750c364 光环助手V4.1.0-游戏名称点号处理 https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/921 2020-07-13 15:38:06 +08:00
9da6cbf097 修复 7.1.1 系统上 toast 可能出现的闪退问题 2020-07-13 15:23:08 +08:00
f83f719283 光环助手V4.1.0-安装游戏支持解压xapk文件(20200710补充) https://gitlab.ghzs.com/pm/halo-app-issues/-/issues/896 2020-07-13 11:16:31 +08:00
4a1c81ffb4 预留方法供网页端确定是否已安装某应用 2020-07-13 09:49:51 +08:00
be26f5168b 增加视频上传urlscheme参数 2020-07-11 21:03:55 +08:00
49b0b982f5 基本完成Xapk解压安装,还有部分细节可能要调整 2020-07-09 18:31:27 +08:00
e63a374da1 重新整理Xapk解压部分以及增加取消操作 2020-07-09 09:28:57 +08:00
6564de8a72 整理安装/卸载相关代码,尽量做到统一处理 2020-07-07 14:57:06 +08:00
d57ac57f43 尝试将Xapk安装部分接入到原有的下载体系 2020-07-03 17:09:50 +08:00
942291d7c5 确定xapk的命名和存放路径问题 2020-07-02 16:59:57 +08:00
de597bdd36 初步完成Xapk解压部分 2020-07-01 16:06:03 +08:00
e1fc23a1bb tinker_base-4.0.1_bugfix 2020-06-17 17:51:22 +08:00
2d551a3f73 VersionCode 改为171 2020-06-17 17:42:37 +08:00
9b205366f7 1.修改视频流广告gif不播放 2.添加网页跳转支付宝微信支付 2020-06-16 18:29:20 +08:00
487 changed files with 11518 additions and 5778 deletions

View File

@ -266,6 +266,7 @@ dependencies {
// for video streaming
implementation("com.shuyu:gsyVideoPlayer-java:$gsyVideo", {
exclude module: "gsyvideoplayer-androidvideocache"
exclude group: "tv.danmaku.ijk.media"
})
implementation "com.shuyu:GSYVideoPlayer-exo2:$gsyVideo"
@ -288,9 +289,14 @@ dependencies {
implementation "com.airbnb.android:lottie:$lottie"
implementation "net.lingala.zip4j:zip4j:${zip4j}"
implementation("com.github.piasy:BigImageViewer:$bigImageViewer", {
exclude group: 'com.squareup.okhttp3'
exclude group: 'androidx.swiperefreshlayout'
exclude group: 'com.github.bumptech.glide'
})
implementation "com.github.PhilJay:MPAndroidChart:${chart}"
implementation project(':libraries:LGLibrary')
implementation project(':libraries:MTA')
@ -300,7 +306,6 @@ dependencies {
// implementation project(':libraries:WechatShare')
implementation project(':libraries:im')
implementation project(':libraries:Matisse')
implementation project(path: ':libraries:gsyVideoPlayer-proxy_cache')
}
File propFile = file('sign.properties')
if (propFile.exists()) {

View File

@ -113,7 +113,9 @@
android:screenOrientation="portrait" />
<!--android:theme = "@android:style/Theme.Black.NoTitleBar.Fullscreen" 退出时屏幕抖动 -->
<activity android:name="com.gh.gamecenter.ViewImageActivity" />
<activity
android:name="com.gh.gamecenter.ImageViewerActivity"
android:theme="@style/Theme.Transparent" />
<activity
android:name="com.gh.gamecenter.SearchActivity"
@ -177,9 +179,9 @@
android:screenOrientation="portrait" />
<activity
android:theme="@style/TransparentStatusBarAndNavigationBar"
android:name="com.gh.gamecenter.MessageDetailActivity"
android:screenOrientation="portrait" />
android:screenOrientation="portrait"
android:theme="@style/TransparentStatusBarAndNavigationBar" />
<activity
android:name="com.gh.gamecenter.LibaoActivity"
@ -208,8 +210,8 @@
<activity
android:name="com.gh.gamecenter.CommentDetailActivity"
android:theme="@style/TransparentStatusBarAndNavigationBar"
android:screenOrientation="portrait" />
android:screenOrientation="portrait"
android:theme="@style/TransparentStatusBarAndNavigationBar" />
<activity
android:name="com.gh.gamecenter.mygame.MyGameActivity"
@ -419,9 +421,9 @@
android:screenOrientation="portrait" />
<activity
android:theme="@style/TransparentStatusBarAndNavigationBar"
android:name="com.gh.gamecenter.gamedetail.rating.RatingReplyActivity"
android:screenOrientation="portrait" />
android:screenOrientation="portrait"
android:theme="@style/TransparentStatusBarAndNavigationBar" />
<activity
android:name="com.gh.gamecenter.history.HistoryActivity"
@ -514,6 +516,20 @@
<activity
android:name=".qa.answer.draft.AnswerDraftActivity"
android:screenOrientation="portrait" />
<activity
android:name=".gamedetail.rating.RatingFoldActivity"
android:screenOrientation="portrait" />
<activity
android:name=".video.data.VideoDataActivity"
android:screenOrientation="portrait" />
<activity
android:name=".video.poster.PosterEditActivity"
android:screenOrientation="portrait" />
<activity
android:name=".video.poster.PosterClipActivity"
android:screenOrientation="portrait" />
<!-- 使用小米/华为推送弹窗功能提高推送成功率-->
<activity
@ -607,7 +623,9 @@
</intent-filter>
</receiver>
<meta-data android:name="com.huawei.hms.client.appid" android:value="@string/huawei_push_appid"/>
<meta-data
android:name="com.huawei.hms.client.appid"
android:value="@string/huawei_push_appid" />
<service
android:name="com.gh.base.GHUmengNotificationService"

View File

@ -0,0 +1,159 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.swiperefreshlayout.widget;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.view.View;
import android.view.animation.Animation;
import android.widget.ImageView;
import androidx.core.content.ContextCompat;
import androidx.core.view.ViewCompat;
/**
* Private class created to work around issues with AnimationListeners being
* called before the animation is actually complete and support shadows on older
* platforms.
*/
class CircleImageView extends ImageView {
private static final int KEY_SHADOW_COLOR = 0x1E000000;
private static final int FILL_SHADOW_COLOR = 0x3D000000;
// PX
private static final float X_OFFSET = 0f;
private static final float Y_OFFSET = 1.75f;
private static final float SHADOW_RADIUS = 3.5f;
private static final int SHADOW_ELEVATION = 4;
private Animation.AnimationListener mListener;
int mShadowRadius;
CircleImageView(Context context, int color) {
super(context);
final float density = getContext().getResources().getDisplayMetrics().density;
final int shadowYOffset = (int) (density * Y_OFFSET);
final int shadowXOffset = (int) (density * X_OFFSET);
mShadowRadius = (int) (density * SHADOW_RADIUS);
ShapeDrawable circle;
if (elevationSupported()) {
circle = new ShapeDrawable(new OvalShape());
ViewCompat.setElevation(this, SHADOW_ELEVATION * density);
} else {
OvalShape oval = new OvalShadow(mShadowRadius);
circle = new ShapeDrawable(oval);
setLayerType(View.LAYER_TYPE_SOFTWARE, circle.getPaint());
circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset,
KEY_SHADOW_COLOR);
final int padding = mShadowRadius;
// set padding so the inner image sits correctly within the shadow.
setPadding(padding, padding, padding, padding);
}
circle.getPaint().setColor(color);
ViewCompat.setBackground(this, circle);
}
private boolean elevationSupported() {
return android.os.Build.VERSION.SDK_INT >= 21;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!elevationSupported()) {
setMeasuredDimension(getMeasuredWidth() + mShadowRadius * 2, getMeasuredHeight()
+ mShadowRadius * 2);
}
}
public void setAnimationListener(Animation.AnimationListener listener) {
mListener = listener;
}
@Override
public void onAnimationStart() {
super.onAnimationStart();
if (mListener != null) {
mListener.onAnimationStart(getAnimation());
}
}
@Override
public void onAnimationEnd() {
super.onAnimationEnd();
if (mListener != null) {
mListener.onAnimationEnd(getAnimation());
}
}
/**
* Update the background color of the circle image view.
*
* @param colorRes Id of a color resource.
*/
public void setBackgroundColorRes(int colorRes) {
setBackgroundColor(ContextCompat.getColor(getContext(), colorRes));
}
@Override
public void setBackgroundColor(int color) {
if (getBackground() instanceof ShapeDrawable) {
((ShapeDrawable) getBackground()).getPaint().setColor(color);
}
}
private class OvalShadow extends OvalShape {
private RadialGradient mRadialGradient;
private Paint mShadowPaint;
OvalShadow(int shadowRadius) {
super();
mShadowPaint = new Paint();
mShadowRadius = shadowRadius;
updateRadialGradient((int) rect().width());
}
@Override
protected void onResize(float width, float height) {
super.onResize(width, height);
updateRadialGradient((int) width);
}
@Override
public void draw(Canvas canvas, Paint paint) {
final int viewWidth = CircleImageView.this.getWidth();
final int viewHeight = CircleImageView.this.getHeight();
canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth / 2, mShadowPaint);
canvas.drawCircle(viewWidth / 2, viewHeight / 2, viewWidth / 2 - mShadowRadius, paint);
}
private void updateRadialGradient(int diameter) {
mRadialGradient = new RadialGradient(diameter / 2, diameter / 2,
mShadowRadius, new int[] { FILL_SHADOW_COLOR, Color.TRANSPARENT },
null, Shader.TileMode.CLAMP);
mShadowPaint.setShader(mRadialGradient);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -18,12 +18,17 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import com.gh.base.fragment.BaseFragment;
import com.gh.common.constant.Constants;
import com.gh.common.util.DialogUtils;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.MtaHelper;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.PackageInstaller;
import com.gh.common.util.RunningUtils;
import com.gh.common.util.ShareUtils;
import com.gh.common.util.StringUtils;
@ -44,10 +49,6 @@ import org.json.JSONObject;
import java.lang.ref.WeakReference;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.Lifecycle;
import butterknife.ButterKnife;
import pub.devrel.easypermissions.EasyPermissions;
@ -206,7 +207,7 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy
if (FileUtils.isEmptyFile(showDialog.getPath())) {
toast(R.string.install_failure_hint);
} else {
startActivity(PackageUtils.getUninstallIntent(BaseActivity.this, showDialog.getPath()));
PackageInstaller.uninstall(BaseActivity.this, showDialog.getPath());
}
});
} else if (LOGIN_EXCEPTION.equals(showDialog.getType())) {

View File

@ -14,6 +14,13 @@ import android.view.Window;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
import com.gh.common.util.DisplayUtils;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.DownloadManagerActivity;
@ -31,13 +38,6 @@ import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProviders;
/**
* 需要用到工具栏的页面使用
* <p>
@ -49,7 +49,7 @@ public abstract class ToolBarActivity extends BaseActivity implements ToolbarCon
@Nullable
private PackageViewModel mPackageViewModel;
protected RelativeLayout mToolbarContainer;
protected View mToolbarContainer;
protected Toolbar mToolbar;

View File

@ -5,6 +5,7 @@ import android.os.Looper
import com.gh.common.AppExecutor.ioExecutor
import com.gh.common.AppExecutor.lightWeightIoExecutor
import com.gh.common.AppExecutor.uiExecutor
import io.reactivex.schedulers.Schedulers
import java.util.concurrent.Executor
import java.util.concurrent.Executors
@ -25,6 +26,8 @@ object AppExecutor {
@JvmStatic
val ioExecutor = Executors.newCachedThreadPool() // 用 by lazy 可能影响初始化速度
val cachedScheduler by lazy { Schedulers.from(ioExecutor) }
class MainThreadExecutor : Executor {
private val mainThreadHandler = Handler(Looper.getMainLooper())

View File

@ -4,12 +4,13 @@ import android.annotation.SuppressLint
import android.content.Context
import android.webkit.JavascriptInterface
import androidx.annotation.Keep
import androidx.appcompat.app.AppCompatActivity
import com.gh.base.CurrentActivityHolder
import com.gh.common.util.*
import com.gh.common.view.dsbridge.CompletionHandler
import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.ImageViewerActivity
import com.gh.gamecenter.LoginActivity
import com.gh.gamecenter.ViewImageActivity
import com.gh.gamecenter.entity.Badge
import com.gh.gamecenter.entity.MtaEvent
import com.gh.gamecenter.manager.UserManager
@ -70,7 +71,6 @@ class DefaultJsApi(var context: Context) {
userInfoEntity.badge = null
}
UserManager.getInstance().userInfoEntity = userInfoEntity
AppDatabase.getInstance(context).userInfoDao().updateUserInfo(userInfoEntity)
}
@JavascriptInterface
@ -139,7 +139,21 @@ class DefaultJsApi(var context: Context) {
val context = CurrentActivityHolder.getCurrentActivity()
context?.startActivity(ViewImageActivity.getViewImageIntent(context, imageEvent.imageList, imageEvent.position, "浏览器"))
context?.startActivity(ImageViewerActivity.getIntent(context, imageEvent.imageList, imageEvent.position, "浏览器"))
}
@JavascriptInterface
fun isInstalled(event: Any): String {
val localInstalledPackageList = PackageUtils.getAllPackageName(HaloApp.getInstance().application)
val packageNameList: ArrayList<String> = event.toString().toObject() ?: ArrayList()
for (packageName in packageNameList) {
if (!localInstalledPackageList.contains(packageName)) {
return "false"
}
}
return "true"
}
@JavascriptInterface
@ -148,7 +162,12 @@ class DefaultJsApi(var context: Context) {
Base64ImageHolder.image = event.toString()
context?.startActivity(ViewImageActivity.getBase64ViewImageIntent(context, true))
context?.startActivity(ImageViewerActivity.getBase64Intent(context, true))
}
@JavascriptInterface
fun openNotificationSetting(msg: Any){
NotificationHelper.show(context as AppCompatActivity)
}
@Keep

View File

@ -4,15 +4,14 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.text.TextUtils
import com.gh.common.util.CheckLoginUtils
import com.gh.common.util.DialogUtils
import com.gh.common.util.DirectUtils
import com.gh.common.util.*
import com.gh.common.util.DirectUtils.directToGameDetailVideoStreaming
import com.gh.common.util.DirectUtils.directToGameVideo
import com.gh.common.util.DirectUtils.directToVideoDetail
import com.gh.common.util.EntranceUtils
import com.gh.gamecenter.*
import com.gh.gamecenter.entity.CommunityEntity
import com.gh.gamecenter.entity.SimpleGameEntity
import com.gh.gamecenter.entity.SubjectRecommendEntity
import com.gh.gamecenter.entity.VideoLinkEntity
import com.gh.gamecenter.subject.SubjectActivity
@ -50,7 +49,7 @@ object DefaultUrlHandler {
e.printStackTrace()
}
"qqqun" -> {
EntranceUtils.HOST_QQ_QUN -> {
val key = uri.getQueryParameter("key")
if (!DirectUtils.directToQqGroup(context, key)) {
Utils.toast(context, "请检查是否已经安装手机QQ")
@ -125,11 +124,11 @@ object DefaultUrlHandler {
val categoryId = uri.getQueryParameter("category_id") ?: ""
val link = uri.getQueryParameter("link") ?: ""
val linkEntity = VideoLinkEntity(title, categoryId, link)
// if (!CheckLoginUtils.isLogin()) {
// HaloApp.put(EntranceUtils.HOST_UPLOAD_VIDEO, linkEntity)
// }
val gameId = uri.getQueryParameter("gameId") ?: ""
val gameName = uri.getQueryParameter("gameName") ?: ""
val simpleGameEntity = SimpleGameEntity(gameId, gameName)
CheckLoginUtils.checkLogin(context, null, true, EntranceUtils.ENTRANCE_BROWSER) {
DirectUtils.directToVideoManager(context, linkEntity, EntranceUtils.ENTRANCE_BROWSER, "")
DirectUtils.directToVideoManager(context, linkEntity, simpleGameEntity, EntranceUtils.ENTRANCE_BROWSER, "")
}
}
EntranceUtils.HOST_USERHOME -> {
@ -140,8 +139,17 @@ object DefaultUrlHandler {
val referer = uri.getQueryParameter("referer") ?: ""
val type = uri.getQueryParameter("type") ?: ""
val act = uri.getQueryParameter("act") ?: ""
val loaction = if (TextUtils.isEmpty(act)) id else VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.value
directToVideoDetail(context, id, loaction, false, "", entrance, "", referer, type, act)
val gameId = uri.getQueryParameter("gameId") ?: ""
val fieldId = uri.getQueryParameter("fieldId") ?: ""
val sectionName = uri.getQueryParameter("sectionName") ?: ""
val location = if (!TextUtils.isEmpty(act)) {
VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.value
} else if (!TextUtils.isEmpty(fieldId)) {
VideoDetailContainerViewModel.Location.GAME_ZONE.value
} else {
id
}
directToVideoDetail(context, id, location, false, gameId, entrance, "", referer, type, act, fieldId, sectionName)
}
EntranceUtils.HOST_VIDEO_SINGLE -> {
val referer = uri.getQueryParameter("referer") ?: ""
@ -191,7 +199,7 @@ object DefaultUrlHandler {
val name = uri.getQueryParameter("name")
?: ""
val entity = SubjectRecommendEntity(link = id, name = name, text = name)
DirectUtils.directToBlock(context, entity)
DirectUtils.directToBlock(context, entity, entrance)
}
EntranceUtils.HOST_SERVER_BLOCK -> {
@ -215,14 +223,116 @@ object DefaultUrlHandler {
}
EntranceUtils.HOST_GAME_UPLOAD -> {
DirectUtils.directGameUpload(context,entrance = entrance, path = "")
DirectUtils.directGameUpload(context, entrance = entrance, path = "")
}
EntranceUtils.HOST_GAME_ZONE -> {
val zoneUrl = uri.getQueryParameter("url") ?: ""
DirectUtils.directGameZone(context, id, zoneUrl, entrance)
}
else -> DialogUtils.showLowVersionDialog(context)
}
return true
} else if ("zhiqu" == uri.scheme) {
if (PackageUtils.isInstalled(context, "com.beieryouxi.zqyxh")) {
val intent = Intent()
intent.data = Uri.parse(url)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent)
} else {
Utils.toast(context, "请安装指趣游戏盒")
}
}
if (url.startsWith("alipays:") || url.startsWith("alipay")) {
try {
context.startActivity(Intent("android.intent.action.VIEW", Uri.parse(url)))
} catch (e: Exception) {
ToastUtils.showToast("请安装支付宝客户端")
}
return true
} else if (url.startsWith("weixin")) {
try {
context.startActivity(Intent("android.intent.action.VIEW", Uri.parse(url)))
} catch (e: Exception) {
ToastUtils.showToast("请安装微信客户端")
}
return true
} else if (url.startsWith("mqqwpa")) {
try {
context.startActivity(Intent("android.intent.action.VIEW", Uri.parse(url)))
} catch (e: Exception) {
ToastUtils.showToast("请安装QQ客户端")
}
return true
}
if ("http" != uri.scheme && "https" != uri.scheme) return true
return false
}
@JvmStatic
fun transformNormalScheme(context: Context, url: String, entrance: String): Boolean {
val uri = Uri.parse(url)
if (uri.host == "www.ghzs666.com" || uri.host == "www.ghzs.com" || uri.host == "ask.ghzs.com" || uri.host == "m.ghzs.com") {
Utils.log(uri.path)
uri.path?.apply {
when {
contains("game") -> {
val gameId = uri.getQueryParameter("gameId") ?: ""
DirectUtils.directToGameDetail(context, gameId, entrance, autoDownload = false, scrollToLibao = false, traceEvent = null)
}
contains("question") -> {
val questionId = split("/")[2]
val answerId = uri.getQueryParameter("answer")
if (answerId.isNullOrEmpty()) {
DirectUtils.directToQuestionDetail(context, questionId, entrance, "")
} else {
DirectUtils.directToAnswerDetail(context, answerId, entrance, "")
}
}
contains("communities") && contains("article") -> {
var communityId = ""
var type = ""
var typeId = ""
val split = replace("/communities", "").replace(".html", "").split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
for (text in split) {
if (TextUtils.isEmpty(communityId)) {
communityId = text
continue
}
if (TextUtils.isEmpty(type)) {
type = text
continue
}
if (TextUtils.isEmpty(typeId)) {
typeId = text
}
}
if ("articles" == type) {
DirectUtils.directToCommunityArticle(
context, typeId, communityId,
entrance, "文章链接")
}
}
contains("article") -> {
val articleId = split("/")[2].replace(".html", "")
DirectUtils.directToArticle(context, articleId, entrance)
}
contains("columns") -> {
val columnsId = split("/")[3]
val id = uri.getQueryParameter("communityId") ?: ""
val name = uri.getQueryParameter("communityName") ?: ""
DirectUtils.directToCommunityColumn(context, CommunityEntity(id, name), columnsId, entrance, "")
}
contains("zone") -> {
val gameId = split("/")[2]
DirectUtils.directGameZone(context, gameId, url, entrance)
}
}
}
return true
}
return false
}
}

View File

@ -9,7 +9,6 @@ import com.gh.gamecenter.entity.TimeEntity
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.schedulers.Schedulers
import kotlin.concurrent.fixedRateTimer
object FixedRateJobHelper {
@ -31,7 +30,7 @@ object FixedRateJobHelper {
// 时间校对10分钟一次
if ((mExecuteCount * CHECKER_PERIOD) % TIME_PERIOD == 0L) {
RetrofitManager.getInstance(HaloApp.getInstance().application).api.time
.subscribeOn(Schedulers.io())
.subscribeOn(AppExecutor.cachedScheduler)
.subscribe(object : Response<TimeEntity>() {
override fun onResponse(response: TimeEntity?) {
val serverTime = response?.time

View File

@ -12,11 +12,12 @@ public class Constants {
public final static int NOT_NETWORK_CODE = 504; // 没有网络的状态码(应该是这个吧!)
public static final String LOGIN_TOKEN_ID = "userToken_id"; // 用户ID 与服务器无关
public static final String USER_TOKEN_KEY = "userTokenKey";
public static final String USER_INFO_KEY = "userInfoKey";
public static final String WELCOME_DIALOG_ID = "welcome_dialog_id";
public static final String WELCOME_DIALOG_LINK_TITLE = "welcome_dialog_link_title";
public static final String DEVICE_KEY = "deviceKey";
public static final String HAS_REQUESTED_NOTIFICATION_PERMISSIONS = "has_requested_notification_permissions";
@ -28,12 +29,12 @@ public class Constants {
public static final String XPOSED_INSTALLER_PACKAGE_NAME = "de.robv.android.xposed.installer";
public static final String EB_QUIT_LOGIN = "quit_login";
// 用于避免历史下载掺和到普通下载状态的 ID 修饰符
public static final String GAME_ID_DIVIDER = ":";
// 用于避免历史下载影响到部分依赖名字作为数据更新条件的修饰符
public static final String GAME_NAME_DECORATOR = " ";
// 游戏详情进入时的自定义栏目标签是否已经默认展开过一次的标记
public static final String SP_HAS_EXPANDED_GAME_DETAIL_TAGS = "has_expanded_game_detail_tags";
// 游戏详情进入时的自定义栏目标签是否已经显示过一次展开更多的浮窗提示
@ -42,7 +43,11 @@ public class Constants {
// 最近显示的弹窗信息
public static final String SP_LAST_OPENING_ID = "last_opening_dialog_id";
public static final String SP_LAST_OPENING_TIME = "last_opening_dialog_time";
// 游戏图标和图标角标
public static final String RAW_GAME_ICON = "raw_game_icon";
public static final String GAME_ICON_SUBSCRIPT = "game_icon_subscript";
// 新用户首次启动光环的时间
public static final String SP_INITIAL_USAGE_TIME = "initial_usage_time";
@ -108,6 +113,9 @@ public class Constants {
public static final String BADGE_ADDRESS_DEV = "http://resource.ghzs.com/page/badge_dev/index.html#/";
public static final String BADGE_ADDRESS = "http://resource.ghzs.com/page/badge_pro/index.html#/";
// 腾讯企点地址
public static final String TENCENT_QIDIAN_ADDRESS = "https://url.cn/D80iyMVV?_type=wpa&qidian=true";
//最少需要多少数据才能上传
public static final int DATA_AMOUNT = 20;

View File

@ -40,12 +40,14 @@ import com.gh.common.util.LogUtils;
import com.gh.common.util.MtaHelper;
import com.gh.common.util.NewsUtils;
import com.gh.common.util.NumberUtils;
import com.gh.common.util.PackageInstaller;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.PermissionHelper;
import com.gh.common.util.PlatformUtils;
import com.gh.common.util.ReservationHelper;
import com.gh.common.view.DownloadProgressBar;
import com.gh.common.view.DrawableView;
import com.gh.common.view.GameIconView;
import com.gh.download.DownloadManager;
import com.gh.download.dialog.DownloadDialog;
import com.gh.gamecenter.DownloadManagerActivity;
@ -313,6 +315,13 @@ public class BindingAdapters {
}
}
@BindingAdapter("game")
public static void setGame(View view, GameEntity gameEntity) {
if (gameEntity != null && view instanceof GameIconView) {
((GameIconView) view).setGameEntity(gameEntity);
}
}
@BindingAdapter("articleType")
public static void setArticleType(TextView view, String articleType) {
NewsUtils.setNewsType(view, articleType, 0, 0);
@ -427,15 +436,16 @@ public class BindingAdapters {
case PLUGIN:
if (gameEntity.getApk().size() == 1) {
ApkEntity apk = gameEntity.getApk().get(0);
DownloadDialogHelper.findAvailableDialogAndShow(v.getContext(), gameEntity, apk,
() -> {
CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> {
DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> {
DialogUtils.checkDownload(v.getContext(), apk.getSize(),
isSubscribe -> download(progressBar, gameEntity, traceEvent, isSubscribe, entrance, location));
});
DownloadDialogHelper.findAvailableDialogAndShow(v.getContext(), gameEntity, apk, () -> {
CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> {
DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> {
DialogUtils.showOverseaDownloadDialog(v.getContext(), gameEntity, () -> {
DialogUtils.checkDownload(v.getContext(), apk.getSize(),
isSubscribe -> download(progressBar, gameEntity, traceEvent, isSubscribe, entrance, location));
});
});
});
});
} else {
CertificationDialog.showCertificationDialog(v.getContext(), gameEntity, () -> {
DialogUtils.showVersionNumberDialog(v.getContext(), gameEntity, () -> {
@ -467,7 +477,7 @@ public class BindingAdapters {
if (gameEntity.getApk().size() == 1) {
DownloadEntity downloadEntity = DownloadManager.getInstance(progressBar.getContext()).getDownloadEntityByUrl(gameEntity.getApk().get(0).getUrl());
if (downloadEntity != null) {
PackageUtils.launchSetup(v.getContext(), downloadEntity);
PackageInstaller.install(v.getContext(), downloadEntity);
}
}
break;
@ -506,7 +516,7 @@ public class BindingAdapters {
HistoryHelper.insertGameEntity(gameEntity);
}
Intent i = new Intent(WebActivity.getIntentForWebGame(progressBar.getContext(), linkEntity.getLink(), gameEntity.getName(), isPlay));
Intent i = new Intent(WebActivity.getIntentForWebGame(progressBar.getContext(), linkEntity.getLink(), gameEntity.getName(), isPlay,linkEntity.getCloseButton()));
progressBar.getContext().startActivity(i);
break;
}
@ -775,4 +785,17 @@ public class BindingAdapters {
}
view.setText(span);
}
@BindingAdapter({"setVideoData"})
public static void setVideoData(TextView view, int count) {
if (count > 0) {
view.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(view.getContext(), R.drawable.ic_video_data_up), null, null, null);
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.text_EA3333));
view.setText(count + "");
} else {
view.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
view.setTextColor(ContextCompat.getColor(view.getContext(), R.color.text_999999));
view.setText("-");
}
}
}

View File

@ -4,13 +4,13 @@ import android.annotation.SuppressLint
import android.app.Application
import android.os.Bundle
import android.text.Html
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.*
import android.view.animation.AnimationUtils
import android.widget.EditText
import android.widget.TextView
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import butterknife.BindView
import butterknife.ButterKnife
import butterknife.OnClick
@ -24,31 +24,58 @@ import com.gh.gamecenter.entity.GameEntity
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.retrofit.BiResponse
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import org.json.JSONArray
import org.json.JSONObject
// 预约弹窗
class ReserveDialogFragment
: BaseDialogFragment() {
: BaseDialogFragment(), KeyboardHeightObserver {
// : BaseTrackableDialogFragment() {
@BindView(R.id.reserve_hint_tv)
lateinit var reserveHintTv: TextView
@BindView(R.id.reserve_content_tv)
lateinit var reserveContentTv: TextView
@BindView(R.id.reserve_completed_content_tv)
lateinit var reserveCompletedContentTv: TextView
@BindView(R.id.mobile_et)
lateinit var mobileEt: EditText
@BindView(R.id.reserve_container)
lateinit var reserveContainer: View
@BindView(R.id.reserve_completed_container)
lateinit var reserveCompletedContainer: View
@BindView(R.id.customizable_btn)
lateinit var customizableBtn: TextView
@BindView(R.id.content_container)
lateinit var contentContainer: View
@BindView(R.id.mobile_index_container)
lateinit var mobileIndexContainer: View
@BindView(R.id.mobile_index_reserve)
lateinit var mobileIndexReserve: TextView
@BindView(R.id.mobile_index_user)
lateinit var mobileIndexUser: TextView
@BindView(R.id.mobile_et_delete)
lateinit var mobileEtDelete: View
@BindView(R.id.layout_container)
lateinit var layoutContainer: View
private lateinit var mViewModel: ReserveViewModel
private var mSuccessCallback: SuccessCallback? = null
@ -57,10 +84,19 @@ class ReserveDialogFragment
private var mGameId: String = ""
private var mGameName: String = ""
private var mKeyboardHeightProvider: KeyboardHeightProvider? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mViewModel = viewModelProvider()
mKeyboardHeightProvider = KeyboardHeightProvider(activity)
mKeyboardHeightProvider?.start()
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
dialog?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
@ -81,11 +117,14 @@ class ReserveDialogFragment
super.onViewCreated(view, savedInstanceState)
ButterKnife.bind(this, view)
val reserveContent = "游戏上线,您将<font color='#ff4147'>免费</font>收到短信提醒"
val reserveContent = "游戏上线,您将收到<font color='#1383EB'>免费短信</font>提醒"
reserveContentTv.text = Html.fromHtml(reserveContent)
mobileEt.setText(UserManager.getInstance().userInfoEntity.mobile)
mobileEt.setSelection(mobileEt.text.length)
mobileEt.setTextChangedListener { s, _, _, _ ->
mobileIndexContainer.visibility = View.GONE
mobileEtDelete.goneIf(s.trim().isEmpty())
}
mViewModel.reservation.observeNonNull(this) {
if (it.success) {
@ -94,6 +133,11 @@ class ReserveDialogFragment
HistoryHelper.insertGameEntity(mGame!!)
}
}
mViewModel.reserveMobile.observe(viewLifecycleOwner, Observer {
setMobileIndexHint(it)
})
dialog?.setCanceledOnTouchOutside(true)
}
@ -123,18 +167,57 @@ class ReserveDialogFragment
}
}
private fun setMobileIndexHint(reserveMobile: String?) {
var userMobile = UserManager.getInstance().userInfoEntity.mobile
if (reserveMobile == userMobile) userMobile = null
if (!reserveMobile.isNullOrEmpty()) {
mobileIndexReserve.visibility = View.VISIBLE
mobileIndexReserve.text = reserveMobile
} else {
mobileIndexReserve.visibility = View.GONE
}
if (!userMobile.isNullOrEmpty()) {
mobileIndexUser.visibility = View.VISIBLE
mobileIndexUser.text = userMobile
} else {
mobileIndexUser.visibility = View.GONE
}
mobileIndexContainer.goneIf(mobileIndexUser.visibility == View.GONE && mobileIndexReserve.visibility == View.GONE)
if (mobileIndexContainer.visibility ==View.VISIBLE) {
mobileIndexContainer.animation = AnimationUtils.loadAnimation(requireContext(), R.anim.reserve_dialog_index_anim)
}
}
@OnClick(R.id.reserve_with_mobile_btn,
R.id.reserve_without_mobile_btn,
R.id.content_container,
R.id.close_btn,
R.id.customizable_btn)
R.id.customizable_btn,
R.id.mobile_index_reserve,
R.id.mobile_index_user,
R.id.mobile_et_delete,
R.id.mobile_et,
R.id.layout_container)
fun onClick(view: View) {
when (view.id) {
R.id.reserve_without_mobile_btn -> {
// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击无手机号预约")
if (mobileIndexContainer.visibility == View.VISIBLE) {
mobileIndexContainer.visibility = View.GONE
return
}
mViewModel.reserve(gameId = mGameId, gameName = mGameName)
}
R.id.reserve_with_mobile_btn -> {
if (mobileIndexContainer.visibility == View.VISIBLE) {
mobileIndexContainer.visibility = View.GONE
return
}
val mobile = mobileEt.text.toString()
if (mobile.length < 11 || !mobile.startsWith("1")) {
Utils.toast(context, "手机号格式错误,请检查并重新输入")
@ -149,6 +232,61 @@ class ReserveDialogFragment
// MtaHelper.onEvent("预约游戏", "预约功能操作", "点击关闭")
dismissAllowingStateLoss()
}
R.id.content_container -> {
mobileIndexContainer.visibility = View.GONE
}
R.id.mobile_index_reserve -> {
mobileEt.setText(mobileIndexReserve.text.toString())
mobileEt.setSelection(mobileEt.text.length)
mobileIndexContainer.visibility = View.GONE
}
R.id.mobile_index_user -> {
mobileEt.setText(mobileIndexUser.text.toString())
mobileEt.setSelection(mobileEt.text.length)
mobileIndexContainer.visibility = View.GONE
}
R.id.mobile_et_delete -> {
mobileEt.setText("")
}
R.id.mobile_et -> {
mobileIndexContainer.visibility = View.GONE
}
R.id.layout_container -> {
dismissAllowingStateLoss()
}
}
}
override fun onResume() {
super.onResume()
if (HaloApp.getInstance().mCacheKeyboardHeight > 0) {
val attributes = dialog?.window?.attributes
val heightPixels = requireContext().resources.displayMetrics.heightPixels
val mCacheKeyboardHeight = HaloApp.getInstance().mCacheKeyboardHeight
val statusBarHeight = DisplayUtils.getStatusBarHeight(requireContext().resources)
dialog?.window?.attributes?.height = heightPixels - mCacheKeyboardHeight - statusBarHeight
attributes?.gravity = Gravity.TOP
dialog?.window?.attributes = attributes
}
mKeyboardHeightProvider?.setKeyboardHeightObserver(this)
}
override fun onPause() {
super.onPause()
mKeyboardHeightProvider?.setKeyboardHeightObserver(null)
}
override fun onDestroy() {
super.onDestroy()
mKeyboardHeightProvider?.close()
}
override fun onKeyboardHeightChanged(height: Int, orientation: Int) {
if (height > 0) {
val attributes = dialog?.window?.attributes
attributes?.gravity = Gravity.CENTER
dialog?.window?.attributes = attributes
HaloApp.getInstance().mCacheKeyboardHeight = height
}
}
@ -170,6 +308,12 @@ class ReserveDialogFragment
class ReserveViewModel(application: Application) : AndroidViewModel(application) {
val reservation = MutableLiveData<Reservation>()
val reserveMobile = MutableLiveData<String>()
init {
getAppointmentMobile()
}
@SuppressLint("CheckResult")
fun reserve(gameId: String, gameName: String, mobile: String = "") {
@ -201,5 +345,30 @@ class ReserveViewModel(application: Application) : AndroidViewModel(application)
})
}
@SuppressLint("CheckResult")
private fun getAppointmentMobile() {
RetrofitManager.getInstance(getApplication()).api
.getAppointmentMobile(UserManager.getInstance().userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BiResponse<ResponseBody>() {
override fun onSuccess(data: ResponseBody) {
var mobile: String? = null
tryCatchInRelease {
val jsonArray = JSONArray(data.string())
if (jsonArray.length() > 0) {
mobile = jsonArray.get(0).toString()
}
}
reserveMobile.postValue(mobile)
}
override fun onFailure(exception: Exception) {
reserveMobile.postValue(null)
}
})
}
class Reservation(var success: Boolean = false, var withMobile: Boolean = false, var boundWechat: Boolean = false)
}

View File

@ -15,5 +15,11 @@ data class ExposureEntity(
val sequence: Int? = 0,
val platform: String? = "",
val downloadType: String? = "",
val downloadCompleteType: String? = ""
val downloadCompleteType: String? = "",
// 统计启动弹窗相关数据用的 (ugly)
@SerializedName("dialog_id")
var welcomeDialogId: String? = "",
@SerializedName("link_title")
var welcomeDialogLinkTitle: String? = ""
) : Parcelable

View File

@ -39,7 +39,10 @@ data class ExposureEvent(
sequence = gameEntity?.sequence,
platform = gameEntity?.platform,
downloadType = gameEntity?.downloadType,
downloadCompleteType = gameEntity?.downloadCompleteType),
downloadCompleteType = gameEntity?.downloadCompleteType,
// ugly
welcomeDialogId = gameEntity?.welcomeDialogId ?: eTrace?.firstOrNull()?.payload?.welcomeDialogId,
welcomeDialogLinkTitle = gameEntity?.welcomeDialogTitle ?: eTrace?.firstOrNull()?.payload?.welcomeDialogLinkTitle),
source = source,
eTrace = eTrace,
event = event).apply { gameEntity?.exposureEvent = this }

View File

@ -1,6 +1,7 @@
package com.gh.common.filter
import android.annotation.SuppressLint
import com.gh.common.AppExecutor
import com.gh.common.constant.Constants
import com.gh.common.util.SPUtils
import com.gh.common.util.toJson
@ -60,10 +61,11 @@ object RegionSettingHelper {
@SuppressLint("CheckResult")
@JvmStatic
fun getRegionSetting() {
// 使用默认的 Schdulers.io() 可能会触发 OOM
RetrofitManager.getInstance(HaloApp.getInstance().application)
.sensitiveApi
.getRegionSetting(HaloApp.getInstance().channel)
.subscribeOn(Schedulers.io())
.subscribeOn(AppExecutor.cachedScheduler)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BiResponse<RegionSetting>() {
override fun onSuccess(data: RegionSetting) {

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 = 6, exportSchema = false)
@Database(entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class], version = 7, exportSchema = false)
@TypeConverters(CountConverter::class,
CommunityConverter::class,
TimeConverter::class,
@ -63,12 +63,19 @@ abstract class HistoryDatabase : RoomDatabase() {
}
}
val MIGRATION_6_7: Migration = object : Migration(6, 7) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("Alter TABLE HistoryGameEntity add iconSubscript TEXT DEFAULT ''")
}
}
val instance by lazy {
Room.databaseBuilder(HaloApp.getInstance().application, HistoryDatabase::class.java, "USER_TRACK_HISTORY_DATABASE")
.addMigrations(MIGRATION_2_3)
.addMigrations(MIGRATION_3_4)
.addMigrations(MIGRATION_4_5)
.addMigrations(MIGRATION_5_6)
.addMigrations(MIGRATION_6_7)
.build()
}
}

View File

@ -41,7 +41,8 @@ object HistoryHelper {
historyGame.id = updateEntity.id
historyGame.brief = updateEntity.brief
historyGame.des = ""
historyGame.icon = updateEntity.icon
historyGame.icon = updateEntity.rawIcon ?: updateEntity.icon
historyGame.iconSubscript = historyGame.iconSubscript
historyGame.name = updateEntity.name
historyGame.tagStyle = updateEntity.tagStyle
historyGame.tag = updateEntity.tag
@ -55,11 +56,11 @@ object HistoryHelper {
historyGame.id = gameEntity.id
historyGame.brief = gameEntity.brief
historyGame.des = gameEntity.des
historyGame.icon = gameEntity.icon
historyGame.icon = gameEntity.rawIcon ?: gameEntity.icon
historyGame.iconSubscript = gameEntity.iconSubscript
historyGame.name = gameEntity.name
historyGame.tagStyle = gameEntity.tagStyle
historyGame.tag = gameEntity.getTag()
historyGame.isLibaoExist = gameEntity.isLibaoExists
return historyGame
}

View File

@ -1,8 +1,11 @@
package com.gh.common.loghub
import android.app.Application
import androidx.annotation.Keep
import com.aliyun.sls.android.sdk.model.Log
import com.aliyun.sls.android.sdk.model.LogGroup
import com.gh.common.exposure.ExposureEntity
import com.gh.common.exposure.meta.Meta
import com.gh.loghub.LoghubHelper
import org.json.JSONObject
import java.util.concurrent.Executors
@ -43,6 +46,23 @@ object LoghubUtils {
}
}
@JvmStatic
fun log(jsonString: String, logStore: String, forcedUpload: Boolean) {
loghubEventExecutor.execute {
try {
val event = LoghubEvent(time = (System.currentTimeMillis() / 1000L).toString(), content = jsonString, logStore = logStore)
loghubEventSet.add(event)
loghubEventDao.insert(event)
} catch (e: Exception) {
e.printStackTrace()
}
if (forcedUpload || loghubEventSet.size >= STORE_SIZE) {
commitSavedLoghubEvents()
}
}
}
private fun uploadLogGroup(logGroup: LogGroup, logStore: String) {
LoghubHelper.getInstance().uploadLogGroup(logGroup, logStore)
}
@ -86,4 +106,12 @@ object LoghubUtils {
}
}
}
}
@Keep
data class SimpleLogContainerEntity(
var event: String? = null,
var action: String? = null,
var meta: Meta? = null,
var payload: ExposureEntity? = null,
var timestamp: Long? = 0)

View File

@ -0,0 +1,6 @@
package com.gh.common.util
interface BiCallback<FIRST, SECOND> {
fun onFirst(first: FIRST)
fun onSecond(second: SECOND)
}

View File

@ -1,14 +1,19 @@
package com.gh.common.util
import android.content.Context
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import com.gh.common.util.CommentUtils.copyText
import com.gh.common.view.BugFixedPopupWindow
import com.gh.gamecenter.CommentDetailActivity
import com.gh.gamecenter.MessageDetailActivity
import com.gh.gamecenter.adapter.OnCommentCallBackListener
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.CommentEntity
import com.gh.gamecenter.entity.MeEntity
import com.gh.gamecenter.entity.Permissions
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.qa.comment.OnCommentOptionClickListener
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.lightgame.utils.Utils
@ -23,13 +28,13 @@ import retrofit2.HttpException
object CommentHelper {
@JvmStatic
fun showCommunityArticleCommentOptions(context: Context,
fun showCommunityArticleCommentOptions(view: View,
commentEntity: CommentEntity,
showConversation: Boolean,
articleId: String,
communityId: String,
listener: OnCommentCallBackListener?) {
showCommentOptions(context = context,
listener: OnCommentOptionClickListener?) {
showCommentOptions(view = view,
commentEntity = commentEntity,
showConversation = showConversation,
articleId = articleId,
@ -38,12 +43,12 @@ object CommentHelper {
}
@JvmStatic
fun showAnswerCommentOptions(context: Context,
fun showAnswerCommentOptions(view: View,
commentEntity: CommentEntity,
showConversation: Boolean,
answerId: String,
listener: OnCommentCallBackListener?) {
showCommentOptions(context = context,
listener: OnCommentOptionClickListener?) {
showCommentOptions(view = view,
commentEntity = commentEntity,
showConversation = showConversation,
answerId = answerId,
@ -51,27 +56,34 @@ object CommentHelper {
}
@JvmStatic
fun showVideoCommentOptions(context: Context,
fun showVideoCommentOptions(view: View,
commentEntity: CommentEntity,
showConversation: Boolean,
videoId: String,
listener: OnCommentCallBackListener?) {
showCommentOptions(context = context,
isVideoAuthor: Boolean,
listener: OnCommentOptionClickListener?) {
showCommentOptions(view = view,
commentEntity = commentEntity,
showConversation = showConversation,
videoId = videoId,
isVideoAuthor = isVideoAuthor,
listener = listener)
}
private fun showCommentOptions(context: Context,
private fun showCommentOptions(view: View,
commentEntity: CommentEntity,
showConversation: Boolean,
articleId: String? = null,
communityId: String? = null,
answerId: String? = null,
videoId: String? = null,
listener: OnCommentCallBackListener? = null) {
isVideoAuthor: Boolean = false,
listener: OnCommentOptionClickListener? = null) {
val context = view.context
val dialogOptions = ArrayList<String>()
if (isVideoAuthor || (videoId != null && commentEntity.user.id == UserManager.getInstance().userId)) {
dialogOptions.add("删除评论")
}
dialogOptions.add("复制")
dialogOptions.add("投诉")
@ -88,55 +100,75 @@ object CommentHelper {
dialogOptions.add("查看对话")
}
DialogUtils.showListDialog(context, dialogOptions, null) {
when (it) {
"管理" -> showControlDialog(context, answerId, articleId, communityId, commentEntity, commentEntity.me!!)
val inflater = LayoutInflater.from(context)
val layout = inflater.inflate(R.layout.comment_more_option, null)
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.comment_more_option_item, container, false)
container.addView(item)
"复制" -> copyText(commentEntity.content, context)
val hitText = item.findViewById<TextView>(R.id.hint_text)
hitText.text = text
"投诉" -> {
context.ifLogin("回答详情-评论-投诉") {
showReportTypeDialog(context) { reportType ->
item.setOnClickListener {
popupWindow.dismiss()
listener?.onCommentOptionClick(commentEntity, text)
when (text) {
"管理" -> showControlDialog(context, answerId, articleId, communityId, commentEntity, commentEntity.me!!)
val commentListener = object : PostCommentUtils.PostCommentListener {
override fun postSuccess(response: JSONObject?) {
Utils.toast(context, "感谢您的投诉")
}
"复制" -> copyText(commentEntity.content, context)
override fun postFailed(error: Throwable?) {
if (error == null) {
Utils.toast(context, "投诉失败,请稍后重试")
} else {
Utils.toast(context, "投诉失败,${error.message}")
"投诉" -> {
context.ifLogin("回答详情-评论-投诉") {
showReportTypeDialog(context) { reportType ->
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}")
}
}
}
}
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)
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)
}
}
}
}
}
"查看对话" -> {
if (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))
} else {
context.startActivity(CommentDetailActivity
.getVideoCommentIntent(context, commentEntity.id, videoId, null))
"查看对话" -> {
if (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))
} else {
context.startActivity(CommentDetailActivity
.getVideoCommentIntent(context, commentEntity.id, videoId, isVideoAuthor, null))
}
}
}
}
}
popupWindow.isTouchable = true
popupWindow.isFocusable = true
popupWindow.showAutoOrientation(view)
}
private fun showControlDialog(context: Context,

View File

@ -6,7 +6,7 @@ import android.widget.LinearLayout;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.gamecenter.ViewImageActivity;
import com.gh.gamecenter.ImageViewerActivity;
import java.util.ArrayList;
import java.util.List;
@ -93,7 +93,7 @@ public class ConcernContentUtils {
break;
}
imageView.setOnClickListener(v -> {
Intent checkIntent = ViewImageActivity.getViewImageIntent(context, (ArrayList<String>) list, position, entrance);
Intent checkIntent = ImageViewerActivity.getIntent(context, (ArrayList<String>) list, position, entrance);
context.startActivity(checkIntent);
});
return imageView;

View File

@ -20,12 +20,13 @@ import java.util.Map;
public class DataLogUtils {
// 轮播图
public static void uploadLunbotuLog(Context context, String type, String text, String index, String source) {
public static void uploadLunbotuLog(Context context, String type, String text, String title, String index, String source) {
Map<String, Object> map = new HashMap<>();
map.put("index", index);
map.put("type", type);
map.put("text", text);
map.put("source", source);
map.put("slide_title", title);
uploadLog(context, "slide", map);
}

View File

@ -6,6 +6,8 @@ import android.view.View;
import com.gh.common.constant.Config;
import com.gh.common.repository.ReservationRepository;
import com.gh.common.view.DownloadProgressBar;
import com.gh.common.xapk.XapkInstaller;
import com.gh.common.xapk.XapkUnzipStatus;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.R;
import com.gh.gamecenter.adapter.viewholder.DetailViewHolder;
@ -13,7 +15,6 @@ import com.gh.gamecenter.entity.LinkEntity;
import com.gh.gamecenter.entity.PluginLocation;
import com.gh.gamecenter.manager.PackagesManager;
import com.lightgame.download.DownloadEntity;
import com.lightgame.utils.Utils;
/**
* Created by khy on 27/06/17.
@ -112,8 +113,18 @@ public class DetailDownloadUtils {
}
public static void detailInvalidate(DetailViewHolder viewHolder) {
viewHolder.mDownloadPb.setProgress((int) (viewHolder.downloadEntity.getPercent() * 10));
DownloadEntity downloadEntity = viewHolder.downloadEntity;
String xapkStatus = downloadEntity.getMeta().get(XapkInstaller.XAPK_UNZIP_STATUS);
if (XapkUnzipStatus.UNZIPPING.name().equals(xapkStatus)) {
String percent = downloadEntity.getMeta().get(XapkInstaller.XAPK_UNZIP_PERCENT);
viewHolder.mDownloadPb.setText("解压中(" + percent + "%");
viewHolder.mDownloadPb.setProgress((int) (Float.valueOf(percent) * 10));
viewHolder.mDownloadPb.setDownloadType(DownloadProgressBar.DownloadType.XAPK_UNZIPPING);
return;
}
viewHolder.mDownloadPb.setProgress((int) (viewHolder.downloadEntity.getPercent() * 10));
switch (downloadEntity.getStatus()) {
case downloading:
case pause:

View File

@ -11,6 +11,8 @@ import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import com.halo.assistant.HaloApp;
import com.lightgame.utils.Util_System_Phone_State;
import com.tencent.stat.StatConfig;
@ -222,8 +224,7 @@ public class DeviceUtils {
// get sim
public static String getSim(Context context) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
String imsi = tm.getSubscriberId();
String imsi = getTelephonySubscriberId(context);
if (imsi == null) {
return "";
}
@ -239,6 +240,12 @@ public class DeviceUtils {
return "";
}
@Nullable
public static String getTelephonySubscriberId(Context context) {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return tm.getSubscriberId();
}
// ping domain
public static String ping(String domain) {
try {

View File

@ -39,8 +39,10 @@ import com.gh.common.view.LimitHeightLinearLayout;
import com.gh.common.view.MaxHeightNestedScrollView;
import com.gh.gamecenter.AboutActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.SuggestionActivity;
import com.gh.gamecenter.WebActivity;
import com.gh.gamecenter.adapter.viewholder.PrivacyPolicyItemViewHolder;
import com.gh.gamecenter.databinding.DialogOverseaConfirmationBinding;
import com.gh.gamecenter.databinding.ImprintContentItemBinding;
import com.gh.gamecenter.databinding.PrivacyItemBinding;
import com.gh.gamecenter.entity.ApkEntity;
@ -48,9 +50,11 @@ import com.gh.gamecenter.entity.GameEntity;
import com.gh.gamecenter.entity.PrivacyPolicyEntity;
import com.gh.gamecenter.entity.SettingsEntity;
import com.gh.gamecenter.entity.TrackableEntity;
import com.gh.gamecenter.suggest.SuggestType;
import com.halo.assistant.HaloApp;
import com.halo.assistant.fragment.SettingsFragment;
import com.lightgame.adapter.BaseRecyclerAdapter;
import com.lightgame.download.DownloadEntity;
import com.lightgame.utils.AppManager;
import com.lightgame.utils.Utils;
@ -1099,6 +1103,21 @@ public class DialogUtils {
final ConfirmListener cmListener,
final CancelListener clListener,
TrackableEntity trackableEntity) {
return showTrackableDialog(context, false, title, message, positive, negative, cmListener, clListener, trackableEntity);
}
/**
* @param useRoundedCornerStyle 使用圆角样式
*/
public static Dialog showTrackableDialog(Context context,
boolean useRoundedCornerStyle,
String title,
CharSequence message,
String positive,
String negative,
final ConfirmListener cmListener,
final CancelListener clListener,
TrackableEntity trackableEntity) {
context = checkDialogContext(context);
final TrackableDialog dialog = new TrackableDialog(context,
@ -1110,7 +1129,17 @@ public class DialogUtils {
trackableEntity.getKeyBackValue(),
trackableEntity.getLogShowEvent());
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_alert, null);
View contentView;
if (useRoundedCornerStyle) {
contentView = LayoutInflater.from(context).inflate(R.layout.dialog_alert_with_rounded_corner, null);
Window window = dialog.getWindow();
if (window != null) {
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
}
} else {
contentView = LayoutInflater.from(context).inflate(R.layout.dialog_alert, null);
}
TextView contentTv = contentView.findViewById(R.id.dialog_content);
TextView titleTv = contentView.findViewById(R.id.dialog_title);
TextView negativeTv = contentView.findViewById(R.id.dialog_negative);
@ -1265,6 +1294,45 @@ public class DialogUtils {
}
}
// 海外下载地址弹窗
public static void showOverseaDownloadDialog(Context context, GameEntity gameEntity, @NonNull ConfirmListener listener) {
context = checkDialogContext(context);
if (gameEntity.getOverseasAddressDialog() == null
|| gameEntity.getApk().size() == 0
|| !gameEntity.getOverseasAddressDialog().isEnable()) {
listener.onConfirm();
} else {
final Dialog dialog = new Dialog(context, R.style.GhAlertDialog);
DialogOverseaConfirmationBinding binding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.dialog_oversea_confirmation, null, false);
View contentView = binding.getRoot();
binding.setGame(gameEntity);
binding.urlTv.setText(gameEntity.getOverseasAddressDialog().getLink());
binding.closeIv.setOnClickListener(v -> dialog.dismiss());
binding.downloadBtn.setText("下载(" + gameEntity.getApk().get(0).getSize() + "");
binding.downloadBtn.setOnClickListener(v -> {
listener.onConfirm();
dialog.dismiss();
});
if ("show&download".equals(gameEntity.getOverseasAddressDialog().getStatus())) {
gameEntity.getApk().get(0).setUrl(gameEntity.getOverseasAddressDialog().getLink());
}
Window window = dialog.getWindow();
if (window != null) {
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
}
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(contentView);
dialog.show();
}
}
public static void showImprintDialog(Context context, GameEntity gameEntity, String titleName) {
context = checkDialogContext(context);
Dialog dialog = new Dialog(context, R.style.full_dialog);
@ -1341,6 +1409,41 @@ public class DialogUtils {
dialog.show();
}
public static void showStopServerExplanationDialog(Context context, String content, String gameName) {
context = checkDialogContext(context);
final Dialog dialog = new TrackableDialog(context, R.style.GhAlertDialog, "评论说明弹窗", "弹窗", gameName, null, null, true);
View contentView = LayoutInflater.from(context).inflate(R.layout.dialog_stop_service_explanation, null);
TextView contentTv = contentView.findViewById(R.id.contentTv);
MaxHeightNestedScrollView scrollView = contentView.findViewById(R.id.scrollView);
if (TextUtils.isEmpty(content)) {
content = context.getString(R.string.rating_protection);
}
contentTv.setText(content);
TextView ok = contentView.findViewById(R.id.dialog_ok);
scrollView.setScrollChangedListener((l, t, oldl, oldt) -> {
MtaHelper.onEvent("评论说明弹窗", "滑动内容", gameName);
});
ok.setOnClickListener(v -> {
MtaHelper.onEvent("评论说明弹窗", "弹窗", "点击我知道了");
MtaHelper.onEvent("评论说明弹窗", "点击我知道了", gameName);
dialog.dismiss();
});
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(contentView);
dialog.show();
Window window = dialog.getWindow();
if (window != null) {
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
WindowManager.LayoutParams params = window.getAttributes();
params.width = context.getResources().getDisplayMetrics().widthPixels - DisplayUtils.dip2px(60f);
window.setAttributes(params);
}
}
public static void showPluggableNeverRemindDialog(Context context, String nameAndPlatform, @NonNull ConfirmListener listener) {
context = checkDialogContext(context);
@ -1368,6 +1471,36 @@ public class DialogUtils {
dialog.show();
}
public static void showUnzipFailureDialog(Context context, DownloadEntity downloadEntity) {
final Context activityContext = checkDialogContext(context);
final Dialog dialog = new Dialog(activityContext, R.style.GhAlertDialog);
Window window = dialog.getWindow();
if (window != null) {
window.setBackgroundDrawableResource(android.R.color.transparent);
}
View contentView = LayoutInflater.from(activityContext).inflate(R.layout.dialog_unzip_failure, null);
View cancelBtn = contentView.findViewById(R.id.cancel);
View confirmBtn = contentView.findViewById(R.id.confirm);
cancelBtn.setOnClickListener(v -> {
dialog.dismiss();
});
confirmBtn.setOnClickListener(v -> {
dialog.dismiss();
String hint = "" + downloadEntity.getName() + "》游戏安装包解压失败,问题反馈:";
SuggestionActivity.startSuggestionActivity(activityContext, SuggestType.normal, null, hint);
});
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(contentView);
dialog.show();
}
/**
* @param context may be is application context
* @return activity context

View File

@ -30,6 +30,7 @@ import com.gh.gamecenter.game.columncollection.detail.ColumnCollectionDetailActi
import com.gh.gamecenter.game.upload.GameSubmissionActivity
import com.gh.gamecenter.gamedetail.GameDetailFragment
import com.gh.gamecenter.manager.UserManager
import com.gh.gamecenter.mvvm.Resource
import com.gh.gamecenter.mygame.PlayedGameActivity
import com.gh.gamecenter.personalhome.UserHomeActivity
import com.gh.gamecenter.qa.CommunityFragment
@ -39,17 +40,24 @@ 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.subject.CommunitySubjectActivity
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.servers.GameServersActivity
import com.gh.gamecenter.subject.SubjectActivity
import com.gh.gamecenter.suggest.SuggestType
import com.gh.gamecenter.tag.TagsActivity
import com.gh.gamecenter.video.data.VideoDataActivity
import com.gh.gamecenter.video.detail.VideoDetailActivity
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
import com.gh.gamecenter.video.game.GameVideoActivity
import com.gh.gamecenter.video.videomanager.VideoManagerActivity
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_ClipboardManager
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import retrofit2.HttpException
/**
* 跳转用的方法
@ -153,7 +161,10 @@ object DirectUtils {
"answer", "社区回答" -> directToAnswerDetail(context, linkEntity.link ?: "", entrance, path)
"community", "问答社区" -> directToCommunity(context, CommunityEntity(linkEntity.link!!, linkEntity.text!!))
"community", "问答社区" -> {
if (linkEntity.link.isNullOrEmpty()) return
directToCommunity(context, CommunityEntity(linkEntity.link!!, linkEntity.text!!))
}
"community_article", "社区文章" -> directToCommunityArticle(context, linkEntity.community!!, linkEntity.link!!, entrance, path)
@ -185,11 +196,14 @@ object DirectUtils {
"category", "分类" -> directCategoryDirectory(context, linkEntity.link!!, linkEntity.text!!)
"block", "版块" -> {
directToBlock(context, SubjectRecommendEntity(
link = linkEntity.link,
text = linkEntity.text,
name = linkEntity.name,
display = linkEntity.display ?: Display()))
if (linkEntity.link.isNullOrEmpty()) return
directToBlock(context,
SubjectRecommendEntity(
link = linkEntity.link,
text = linkEntity.text,
name = linkEntity.name,
display = linkEntity.display ?: Display()),
entrance)
}
"column_collection", "专题合集" -> directToColumnCollection(context, linkEntity.link!!, -1, entrance)
@ -244,7 +258,7 @@ object DirectUtils {
*/
@JvmStatic
fun directToQa(context: Context, text: String, id: String) {
// context.startActivity(QaActivity.getIntent(context, navigationTitle = text, qaId = id))
if (id.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_TO, QaActivity::class.java.simpleName)
bundle.putString(KEY_NAVIGATION_TITLE, text)
@ -257,7 +271,7 @@ object DirectUtils {
*/
@JvmStatic
fun directToQaCollection(context: Context, text: String, id: String) {
// context.startActivity(QaActivity.getIntent(context, navigationTitle = text, qaCollectionId = id))
if (id.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_TO, QaActivity::class.java.simpleName)
bundle.putString(KEY_NAVIGATION_TITLE, text)
@ -270,13 +284,13 @@ object DirectUtils {
*/
@JvmStatic
fun directToColumnCollection(context: Context, id: String, position: Int = -1, entrance: String, columnName: String = "") {
// context.startActivity(ColumnCollectionDetailActivity.getIntent(context, id, position, entrance, columnName))
if (id.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_TO, ColumnCollectionDetailActivity::class.java.name)
bundle.putString(KEY_ENTRANCE, entrance)
bundle.putString(KEY_COLLECTION_ID,id)
bundle.putString(KEY_COLUMNNAME,columnName)
bundle.putInt(KEY_POSITION,position)
bundle.putString(KEY_COLLECTION_ID, id)
bundle.putString(KEY_COLUMNNAME, columnName)
bundle.putInt(KEY_POSITION, position)
jumpActivity(context, bundle)
}
@ -285,6 +299,7 @@ object DirectUtils {
*/
@JvmStatic
fun directToArticle(context: Context, id: String, entrance: String? = null) {
if (id.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, NewsDetailActivity::class.java.simpleName)
@ -328,6 +343,7 @@ object DirectUtils {
*/
@JvmStatic
fun directToGameDetail(context: Context, id: String, entrance: String? = null, autoDownload: Boolean? = null, scrollToLibao: Boolean = false, traceEvent: ExposureEvent? = null) {
if (id.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, GameDetailActivity::class.java.simpleName)
@ -356,7 +372,16 @@ object DirectUtils {
bundle.putString(KEY_GAMEID, id)
bundle.putBoolean(KEY_OPEN_VIDEO_STREAMING, true)
bundle.putInt(KEY_TARGET, GameDetailFragment.INDEX_DESC)
// GameDetailActivity.startGameDetailToVideoStreaming(context, id, entrance)
jumpActivity(context, bundle)
}
@JvmStatic
fun directToGameDetail(context: Context, id: String, defaultTab: Int = GameDetailFragment.INDEX_DESC, entrance: String? = null) {
val bundle = Bundle()
bundle.putString(KEY_TO, GameDetailActivity::class.java.name)
bundle.putString(KEY_ENTRANCE, entrance)
bundle.putString(KEY_GAMEID, id)
bundle.putInt(KEY_TARGET, defaultTab)
jumpActivity(context, bundle)
}
@ -368,6 +393,7 @@ object DirectUtils {
// 专栏
@JvmStatic
fun directToSubject(context: Context, id: String, subjectName: String? = "", entrance: String? = null) {
if (id.isEmpty()) return
val bundle = Bundle()
val subjectData = SubjectData(subjectId = id, subjectName = subjectName, isOrder = false)
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
@ -444,6 +470,7 @@ object DirectUtils {
@JvmStatic
fun directToAnswerDetail(context: Context, id: String, entrance: String? = null, path: String? = null) {
if (id.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, AnswerDetailActivity::class.java.name)
@ -454,6 +481,7 @@ object DirectUtils {
@JvmStatic
fun directToQuestionDetail(context: Context, id: String, entrance: String? = null, path: String? = null) {
if (id.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, QuestionsDetailActivity::class.java.name)
@ -464,6 +492,7 @@ object DirectUtils {
@JvmStatic
fun directToWebView(context: Context, url: String, entrance: String? = null) {
if (url.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, WebActivity::class.java.simpleName)
@ -482,8 +511,9 @@ object DirectUtils {
@JvmStatic
fun directToExternalBrowser(context: Context, url: String) {
if (url.isEmpty()) return
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
if (context !is AppCompatActivity){
if (context !is AppCompatActivity) {
browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(browserIntent)
@ -507,7 +537,7 @@ object DirectUtils {
}
val str = "mqqwpa://im/chat?chat_type=$chatType&uin=$qq&version=1&src_type=web"
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(str))
if (context !is AppCompatActivity){
if (context !is AppCompatActivity) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
@ -521,12 +551,13 @@ object DirectUtils {
// 跳转 QQ 群
@JvmStatic
fun directToQqGroup(context: Context, groupNumber: String? = null): Boolean {
if (groupNumber.isNullOrEmpty()) return false
if (ShareUtils.isQQClientAvailable(context)) {
val intent = Intent()
intent.data = Uri.parse("mqqopensdkapi://bizAgent/qm/qr?url=http%3A%2F%2Fqm.qq.com%2Fcgi-bin%2Fqm%2Fqr%3Ffrom%3Dapp%26p%3Dandroid%26k%3D$groupNumber")
// 此Flag可根据具体产品需要自定义如设置则在加群界面按返回返回手Q主界面不设置按返回会返回到呼起产品界面 //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
return try {
if (context !is AppCompatActivity){
if (context !is AppCompatActivity) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
context.startActivity(intent)
@ -534,7 +565,7 @@ object DirectUtils {
} catch (e: Exception) {
false
}
}else{
} else {
Utils.toast(context, "请安装QQ客户端")
return false
}
@ -546,6 +577,7 @@ object DirectUtils {
*/
@JvmStatic
fun directToGiftDetail(context: Context, giftId: String, entrance: String? = null) {
if (giftId.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, LibaoDetailActivity::class.java.simpleName)
@ -600,6 +632,7 @@ object DirectUtils {
@JvmStatic
fun directToCommunityArticle(context: Context, community: CommunityEntity?, articleId: String?, entrance: String?, path: String?) {
if (articleId.isNullOrEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_PATH, path)
@ -614,6 +647,7 @@ object DirectUtils {
*/
@JvmStatic
fun directToCommunityColumn(context: Context, community: CommunityEntity?, subjectId: String, entrance: String?, path: String?) {
if (subjectId.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_PATH, path)
bundle.putString(KEY_TO, CommunitySubjectActivity::class.java.name)
@ -627,7 +661,8 @@ object DirectUtils {
* @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 = "") {
fun directToVideoDetail(context: Context, videoId: String, fromLocation: String, showComment: Boolean = false, gameId: String = "", entrance: String? = null,
path: String? = "", referer: String = "", type: String = "", act: String = "", fieldId: String = "", sectionName: String = "") {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
@ -640,6 +675,8 @@ object DirectUtils {
bundle.putString(KEY_REFERER, referer)
bundle.putString(KEY_TYPE, type)
bundle.putString(KEY_ACTIVITY_NAME, act)
bundle.putString(KEY_FIELD_ID, fieldId)
bundle.putString(KEY_SECTION_NAME, sectionName)
jumpActivity(context, bundle)
} else {
DialogUtils.showLowSystemVersionDialog(context)
@ -677,10 +714,11 @@ object DirectUtils {
* 跳转至上传视频
*/
@JvmStatic
fun directToVideoManager(context: Context, linkEntity: VideoLinkEntity, entrance: String? = null, path: String? = "") {
fun directToVideoManager(context: Context, linkEntity: VideoLinkEntity, simpleGameEntity: SimpleGameEntity, entrance: String? = null, path: String? = "") {
val bundle = Bundle()
bundle.putString(KEY_PATH, path)
bundle.putParcelable(VideoLinkEntity::class.java.simpleName, linkEntity)
bundle.putParcelable(SimpleGameEntity::class.java.simpleName, simpleGameEntity)
bundle.putString(KEY_TO, VideoManagerActivity::class.java.name)
bundle.putString(KEY_ENTRANCE, BaseActivity.mergeEntranceAndPath(entrance, path))
jumpActivity(context, bundle)
@ -691,6 +729,7 @@ object DirectUtils {
*/
@JvmStatic
fun directToGameVideo(context: Context, gameId: String, entrance: String? = null, path: String? = "") {
if (gameId.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_PATH, path)
bundle.putString(KEY_TO, GameVideoActivity::class.java.name)
@ -738,6 +777,7 @@ object DirectUtils {
*/
@JvmStatic
fun directCategoryDirectory(context: Context, categoryId: String, categoryTitle: String, entrance: String? = null, path: String? = "") {
if (categoryId.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_TO, CategoryDirectoryActivity::class.java.name)
bundle.putString(KEY_CATEGORY_ID, categoryId)
@ -766,6 +806,7 @@ object DirectUtils {
*/
@JvmStatic
fun directAskColumnDetail(context: Context, columnId: String, community: CommunityEntity, entrance: String? = null, path: String? = "") {
if (columnId.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_TO, AskColumnDetailActivity::class.java.name)
bundle.putString(KEY_COLUMN_ID, columnId)
@ -779,10 +820,11 @@ object DirectUtils {
* 跳转到板块
*/
@JvmStatic
fun directToBlock(context: Context, blockData: SubjectRecommendEntity) {
fun directToBlock(context: Context, blockData: SubjectRecommendEntity, entrance: String) {
val bundle = Bundle()
bundle.putString(KEY_TO, BlockActivity::class.java.name)
bundle.putParcelable(KEY_BLOCK_DATA, blockData)
bundle.putString(KEY_ENTRANCE, entrance)
jumpActivity(context, bundle)
}
@ -814,6 +856,7 @@ object DirectUtils {
*/
@JvmStatic
fun directSimpleArticleList(context: Context, sortType: String, entrance: String? = null, path: String? = "") {
if (sortType.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_TO, SimpleArticleListActivity::class.java.name)
bundle.putString(KEY_TYPE, sortType)
@ -821,4 +864,40 @@ object DirectUtils {
bundle.putString(KEY_PATH, path)
jumpActivity(context, bundle)
}
@JvmStatic
fun directGameZone(context: Context, gameId: String, url: String, entrance: String? = null) {
RetrofitManager.getInstance(HaloApp.getInstance().application)
.sensitiveApi
.getGameDigest(gameId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<GameEntity>() {
override fun onResponse(response: GameEntity?) {
response?.apply {
if (zone.status == "on") {
if (zone.style == "link") {
directToGameDetail(context, gameId, GameDetailFragment.INDEX_TRENDES, entrance)
} else {
directToWebView(context, url, entrance)
}
} else {
directToWebView(context, url, entrance)
}
}
}
override fun onFailure(e: HttpException?) {
}
})
}
@JvmStatic
fun directVideoData(context: Context, entrance: String? = null) {
val bundle = Bundle()
bundle.putString(KEY_TO, VideoDataActivity::class.java.name)
bundle.putString(KEY_ENTRANCE, entrance)
jumpActivity(context, bundle)
}
}

View File

@ -8,12 +8,6 @@ import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.collection.ArrayMap;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.gh.common.constant.Config;
import com.gh.common.dialog.CertificationDialog;
import com.gh.common.dialog.DeviceRemindDialog;
@ -21,6 +15,8 @@ import com.gh.common.dialog.ReserveDialogFragment;
import com.gh.common.exposure.ExposureEvent;
import com.gh.common.history.HistoryHelper;
import com.gh.common.repository.ReservationRepository;
import com.gh.common.xapk.XapkInstaller;
import com.gh.common.xapk.XapkUnzipStatus;
import com.gh.download.DownloadManager;
import com.gh.download.dialog.DownloadDialog;
import com.gh.gamecenter.DownloadManagerActivity;
@ -40,6 +36,12 @@ import com.lightgame.utils.Utils;
import java.util.concurrent.LinkedBlockingQueue;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.collection.ArrayMap;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
/**
* todo 下载判断不能以按钮文案为判断条件,否则按钮文案修改时又要修改判断逻辑
*/
@ -261,6 +263,7 @@ public class DownloadItemUtils {
boolean isShowPlatform, boolean isNormal) {
updateItemViewStatus(holder, true, null);
holder.gameProgressbar.setProgressDrawable(context.getResources().getDrawable(R.drawable.progressbar_bg_style));
String platform = PlatformUtils.getInstance(context).getPlatformName(downloadEntity.getPlatform());
@ -321,6 +324,17 @@ public class DownloadItemUtils {
holder.gameDownloadBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.text_downloading_style));
}
} else if (status.equals(DownloadStatus.done)) {
String xapkStatus = downloadEntity.getMeta().get(XapkInstaller.XAPK_UNZIP_STATUS);
if (XapkUnzipStatus.UNZIPPING.name().equals(xapkStatus)) {
String percent = downloadEntity.getMeta().get(XapkInstaller.XAPK_UNZIP_PERCENT);
holder.gameProgressbar.setProgressDrawable(context.getResources().getDrawable(R.drawable.progressbar_xapk_style));
holder.gameDownloadSpeed.setText(R.string.unzipping);
holder.gameProgressbar.setProgress((int) (Float.valueOf(percent) * 10));
holder.gameDownloadPercentage.setText(percent + "%");
holder.gameDownloadBtn.setText(R.string.unzipping);
return;
}
holder.gameProgressbar.setProgress(1000);
if (isShowPlatform && platform != null) {
holder.gameDownloadSpeed.setText(String.format("%s - 下载完成", platform));
@ -454,7 +468,7 @@ public class DownloadItemUtils {
HistoryHelper.insertGameEntity(gameEntity);
}
Intent i = WebActivity.getIntentForWebGame(context, gameEntity.getH5Link().getLink(), gameEntity.getName(), isPlay);
Intent i = WebActivity.getIntentForWebGame(context, gameEntity.getH5Link().getLink(), gameEntity.getName(), isPlay,linkEntity.getCloseButton());
context.startActivity(i);
});
} else if (gameEntity.getApk().size() == 1) {
@ -520,8 +534,10 @@ public class DownloadItemUtils {
// 先弹下载弹窗(如果需要的话)
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, () -> {
CertificationDialog.showCertificationDialog(context, gameEntity, () -> {
DialogUtils.checkDownload(context, apk.getSize(),
isSubscribe -> download(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent));
DialogUtils.showOverseaDownloadDialog(context, gameEntity, () -> {
DialogUtils.checkDownload(context, apk.getSize(),
isSubscribe -> download(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent));
});
});
});
@ -530,8 +546,10 @@ public class DownloadItemUtils {
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk, () -> {
CertificationDialog.showCertificationDialog(context, gameEntity, () -> {
DialogUtils.showVersionNumberDialog(context, gameEntity, () -> {
DialogUtils.checkDownload(context, apk.getSize(),
isSubscribe -> download(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent));
DialogUtils.showOverseaDownloadDialog(context, gameEntity, () -> {
DialogUtils.checkDownload(context, apk.getSize(),
isSubscribe -> download(context, gameEntity, downloadBtn, entrance, location, isSubscribe, traceEvent));
});
});
});
});
@ -559,10 +577,6 @@ public class DownloadItemUtils {
}
DataUtils.onGameLaunchEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), location);
PackageUtils.launchApplicationByPackageName(context, gameEntity.getApk().get(0).getPackageName());
} else if (str.equals(context.getString(R.string.waiting))
|| str.equals(context.getString(R.string.downloading))) {
context.startActivity(DownloadManagerActivity.getDownloadMangerIntent(context,
apk.getUrl(), entrance + "+(" + location.split(":")[0] + ")"));
} else if (str.equals(context.getString(R.string.update))) {
if (entrance.contains("我的游戏")) {
MtaHelper.onEvent("我的游戏_启动", "更新", gameEntity.getName());
@ -571,6 +585,9 @@ public class DownloadItemUtils {
DialogUtils.checkDownload(context, apk.getSize(),
isSubscribe -> update(context, gameEntity, entrance, location, isSubscribe, traceEvent));
});
} else {
context.startActivity(DownloadManagerActivity.getDownloadMangerIntent(context,
apk.getUrl(), entrance + "+(" + location.split(":")[0] + ")"));
}
}
@ -631,9 +648,9 @@ public class DownloadItemUtils {
}
adapter.notifyItemChanged(position);
} else if (PackageUtils.isCanPluggable(apkEntity)) {
DialogUtils.showPluginDialog(context, () -> context.startActivity(PackageUtils.getUninstallIntent(context, path)));
DialogUtils.showPluginDialog(context, () -> PackageInstaller.uninstall(context, path));
} else {
PackageUtils.launchSetup(context, downloadEntity);
PackageInstaller.install(context, downloadEntity);
}
}
}

View File

@ -10,6 +10,8 @@ import android.os.Build
import androidx.core.app.NotificationCompat
import com.gh.common.AppExecutor
import com.gh.common.constant.Constants
import com.gh.common.xapk.XapkInstaller
import com.gh.common.xapk.XapkUnzipStatus
import com.gh.gamecenter.R
import com.halo.assistant.HaloApp
import com.lightgame.download.DownloadEntity
@ -38,9 +40,10 @@ object DownloadNotificationHelper {
var requireUpdateNotificationGroupDelay = false
val notificationManager = getNotificationManager()
val downloadNotificationId = (entity.gameId + entity.packageName).hashCode()
val xapkStatus = entity.meta[XapkInstaller.XAPK_UNZIP_STATUS]
val intent = Intent()
if (entity.status == DownloadStatus.done) {
if (entity.status == DownloadStatus.done && xapkStatus != XapkUnzipStatus.FAILURE.name) {
intent.putExtra(EntranceUtils.KEY_DATA, entity.toJson())
intent.putExtra(EntranceUtils.KEY_PATH, entity.path)
intent.action = ACTION_INSTALL
@ -65,18 +68,23 @@ object DownloadNotificationHelper {
.setContentIntent(pendingIntent)
.setGroup(DOWNLOAD_GROUP_KEY)
.setWhen(whenTime)
.setProgress(PROGRESS_MAX, entity.percent.toInt(), false)
when (entity.status) {
DownloadStatus.downloading -> builder.setContentText(String.format("%s(剩%s)",
SpeedUtils.getSpeed(entity.speed),
SpeedUtils.getRemainTime(entity.size, entity.progress, entity.speed * 1024)))
DownloadStatus.done -> builder.setContentText("下载完成,点击立即安装")
DownloadStatus.waiting -> builder.setContentText("等待中")
DownloadStatus.subscribe,
DownloadStatus.timeout,
DownloadStatus.neterror -> builder.setContentText("已暂停连接WiFi自动下载")
else -> builder.setContentText("暂停")
if (xapkStatus == XapkUnzipStatus.FAILURE.name) {
builder.setContentText("" + entity.name + "》解压失败,点击查看详情~")
} else {
when (entity.status) {
DownloadStatus.downloading -> builder.setContentText(String.format("%s(剩%s)",
SpeedUtils.getSpeed(entity.speed),
SpeedUtils.getRemainTime(entity.size, entity.progress, entity.speed * 1024)))
DownloadStatus.done -> builder.setContentText("下载完成,点击立即安装")
DownloadStatus.waiting -> builder.setContentText("等待")
DownloadStatus.subscribe,
DownloadStatus.timeout,
DownloadStatus.neterror -> builder.setContentText("已暂停连接WiFi自动下载")
else -> builder.setContentText("暂停中")
}
builder.setProgress(PROGRESS_MAX, entity.percent.toInt(), false)
}
when {
@ -91,6 +99,11 @@ object DownloadNotificationHelper {
tryCatchInRelease {
val notification = builder.build() // 可能会抛出异常
notification.flags = notification.flags or Notification.FLAG_NO_CLEAR
if (xapkStatus == XapkUnzipStatus.FAILURE.name) {
notification.flags = notification.flags or Notification.FLAG_AUTO_CANCEL
} else {
notification.flags = notification.flags or Notification.FLAG_NO_CLEAR
}
if (entity.status == DownloadStatus.delete
|| entity.status == DownloadStatus.cancel
@ -98,7 +111,8 @@ object DownloadNotificationHelper {
|| entity.status == DownloadStatus.notfound
|| entity.status == DownloadStatus.overflow
|| (entity.status == DownloadStatus.done // 触发安装事件以后也 cancel 掉通知
&& !entity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION].isNullOrEmpty())) {
&& !entity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION].isNullOrEmpty()
&& xapkStatus != XapkUnzipStatus.FAILURE.name)) {
requireUpdateNotificationGroupDelay = true
notificationManager.cancel(entity.path, DOWNLOAD_NOTIFICATION_ID)
} else {

View File

@ -6,6 +6,8 @@ import android.preference.PreferenceManager
import com.gh.base.BaseActivity
import com.gh.common.constant.Constants
import com.gh.common.exposure.ExposureUtils
import com.gh.common.xapk.XapkInstaller
import com.gh.download.DownloadDataHelper
import com.gh.download.DownloadManager
import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.R
@ -43,11 +45,15 @@ object DownloadObserver {
fun initObserver() {
val dataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
// todo 如何处理xapk安装问题
val xapkStatus = downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS]
if (!xapkStatus.isNullOrEmpty()) return
val gameId = downloadEntity.getRealGameId(Constants.GAME_ID_DIVIDER)
val downloadManager = DownloadManager.getInstance(HaloApp.getInstance().application)
if (downloadEntity.status != DownloadStatus.downloading) {
LogUtils.uploadDownloadEvent(downloadEntity)
tryCatchInRelease {
DownloadDataHelper.uploadDownloadEvent(downloadEntity)
}
if (DownloadStatus.hijack == downloadEntity.status) {
@ -103,7 +109,7 @@ object DownloadObserver {
MtaHelper.onEvent("软件更新", "下载完成")
tryWithDefaultCatch {
// 会有 ActivityNotFoundException 异常catch 掉不管了
mApplication.startActivity(PackageUtils.getInstallIntent(mApplication, downloadEntity.path, true))
PackageInstaller.install(mApplication, downloadEntity)
}
DataLogUtils.uploadUpgradeLog(mApplication, "install") //上传更新安装数据
} else {
@ -131,7 +137,7 @@ object DownloadObserver {
if (PackageUtils.isCanLaunchSetup(mApplication, downloadEntity.path)) {
downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
tryWithDefaultCatch {
mApplication.startActivity(PackageUtils.getInstallIntent(mApplication, downloadEntity.path))
PackageInstaller.install(mApplication, downloadEntity, false)
}
} else {
// 弹出卸载提示框

View File

@ -48,6 +48,7 @@ public class EntranceUtils {
public static final String HOST_HELP = "help";//Q&A
public static final String HOST_HELP_COLLECTION = "help_collection";//Q&A合集
public static final String HOST_GAME_UPLOAD = "game_upload";//游戏上传
public static final String HOST_GAME_ZONE = "game_zone";//游戏专区
public static final String HOST_COMMUNITY_ARTICLE = "community_article";
public static final String HOST_COMMUNITY_COLUMN = "community_column";
public static final String HOST_GAME = "game";
@ -56,6 +57,7 @@ public class EntranceUtils {
public static final String HOST_WEB = "web";
public static final String HOST_QQ = "qq";
public static final String HOST_QQ_GROUP = "qqgroup";
public static final String HOST_QQ_QUN = "qqqun";
public static final String HOST_DOWNLOAD = "download";
public static final String HOST_UPDATE = "update";
public static final String HOST_LIBAO = "libao";
@ -81,6 +83,7 @@ public class EntranceUtils {
public static final String ENTRANCE_DOWNLOAD = "(下载跳转)";
public static final String ENTRANCE_RECOMMEND = "(落地页)";
public static final String ENTRANCE_BLOCK_RECOMMEND = "(推荐入口)";
public static final String ENTRANCE_AMWAY = "(安利墙)";
public static final String KEY_SUGGEST_HINT_TYPE = "suggestHintType";
public static final String KEY_PACKAGENAME = "packageName";
public static final String KEY_PLATFORM = "platform";
@ -162,6 +165,8 @@ public class EntranceUtils {
public static final String KEY_WEB_SHARE = "webShare";
public static final String KEY_ACTIVITY_NAME = "activityName";//活动名称
public static final String KEY_REQUIRE_REDIRECT = "require_redirect"; // 标记需要再跳转
public static final String KEY_FIELD_ID = "field_id"; // 专区板块id
public static final String KEY_SECTION_NAME = "section_name"; // 专区专题名
public static final String KEY_COLUMNNAME = "columnName";
public static final String KEY_QA_ID = "qaId";
public static final String KEY_QA_COLLECTION_ID = "qaCollectionId";
@ -169,6 +174,8 @@ public class EntranceUtils {
public static final String KEY_ARTICLE_OPEN_IN_NEW_PAGE = "openArticleInNewPage";
public static final String KEY_ONLY_CREATE_DRAFT = "onlyCreateDraft";
public static final String KEY_KAIFU_SELECT_TIME = "kaifuSelectTime";
public static final String KEY_POSTER_PATH = "posterPath";
public static final String KEY_BLACK_THEME = "blackTheme";
public static void jumpActivity(Context context, Bundle bundle) {
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);

View File

@ -19,6 +19,7 @@ import androidx.core.text.HtmlCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.*
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager
import com.airbnb.lottie.LottieAnimationView
@ -34,6 +35,7 @@ import com.gh.gamecenter.R
import com.gh.gamecenter.WebActivity
import com.google.gson.reflect.TypeToken
import com.halo.assistant.HaloApp
import com.lightgame.download.DownloadEntity
import com.lightgame.utils.Utils
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
@ -41,6 +43,7 @@ import io.reactivex.disposables.Disposable
import okhttp3.MediaType
import okhttp3.RequestBody
import java.net.URI
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.regex.Pattern
import kotlin.math.abs
@ -275,6 +278,15 @@ fun throwExceptionInDebug(message: String = "", predicate: Boolean = true) {
}
}
/**
* 主动抛出异常
*/
fun throwException(message: String = "", predicate: Boolean = true) {
if (predicate) {
throw RuntimeException(message)
}
}
/**
* String related
*/
@ -314,6 +326,10 @@ fun String.subStringIfPossible(length: Int): String {
}
}
fun String.countOccurrences(char: String): Int {
return StringTokenizer(" $this ", char).countTokens() - 1
}
fun String.getFirstElementDividedByDivider(divider: String): String {
if (this.contains(divider)) {
return this.split(divider.toRegex()).toTypedArray()[0]
@ -566,6 +582,17 @@ fun SimpleDraweeView.display(url: String) {
ImageUtils.display(this, url)
}
/**
* DownloadEntity extension
*/
fun DownloadEntity.addMetaExtra(key: String, value: String?) {
value?.let { meta[key] = value }
}
fun DownloadEntity.getMetaExtra(key: String) : String {
return meta[key] ?: ""
}
/**
* Process related
*/
@ -682,6 +709,17 @@ fun LottieAnimationView.doOnAnimationEnd(action: () -> Unit) {
})
}
fun String?.getExtension(): String? {
this ?: return null
tryCatchInRelease {
val lastDotIndex = this.lastIndexOf('.')
return if (lastDotIndex == -1) null else this.substring(lastDotIndex + 1)
}
return null
}
/**
* 检查内容是否一致
* @return true:相同 false:不同

View File

@ -5,6 +5,8 @@ import android.graphics.Color;
import android.text.TextUtils;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.gh.common.constant.Config;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.R;
@ -22,9 +24,6 @@ import com.lightgame.download.DownloadStatus;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.Nullable;
public class GameUtils {
/**
@ -183,6 +182,8 @@ public class GameUtils {
GameUpdateEntity gameUpdateEntity = new GameUpdateEntity();
gameUpdateEntity.setId(gameEntity.getId());
gameUpdateEntity.setIcon(gameEntity.getIcon());
gameUpdateEntity.setRawIcon(gameEntity.getRawIcon());
gameUpdateEntity.setIconSubscript(gameEntity.getIconSubscript());
gameUpdateEntity.setName(gameEntity.getName());
gameUpdateEntity.setPackageName(apkEntity.getPackageName());
gameUpdateEntity.setSize(apkEntity.getSize());
@ -199,6 +200,7 @@ public class GameUtils {
gameUpdateEntity.setDownload(gameEntity.getDownload());
gameUpdateEntity.setIndexPlugin(gameEntity.getIndexPlugin());
gameUpdateEntity.setPluginDesc(gameEntity.getPluginDesc());
gameUpdateEntity.setFormat(apkEntity.getFormat());
GameCollectionEntity pluggableCollection = getPluggableCollectionFromGameEntity(gameEntity, apkEntity.getPackageName());
if (pluggableCollection != null) {

View File

@ -1,6 +1,7 @@
package com.gh.common.util
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import org.json.JSONArray
@ -10,6 +11,7 @@ import org.json.JSONArray
*/
object GsonUtils {
val gson: Gson = Gson()
val gsonThatIgnoreNull: Gson = GsonBuilder().serializeNulls().create()
@JvmStatic
fun <T> fromJson(json: String, t: Class<T>): T {
@ -32,4 +34,9 @@ object GsonUtils {
fun toJson(any: Any?): String {
return gson.toJson(any)
}
@JvmStatic
fun toJsonIgnoreNull(any: Any?): String {
return gsonThatIgnoreNull.toJson(any)
}
}

View File

@ -1,5 +1,6 @@
package com.gh.common.util
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import android.graphics.Bitmap
@ -9,21 +10,21 @@ import android.net.Uri
import android.os.Build
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import com.facebook.common.executors.CallerThreadExecutor
import com.facebook.drawee.backends.pipeline.Fresco
import com.facebook.drawee.controller.BaseControllerListener
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.core.ImagePipeline
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber
import com.facebook.imagepipeline.image.ImageInfo
import com.facebook.imagepipeline.request.ImageRequest
import com.facebook.imagepipeline.request.ImageRequestBuilder
import com.gh.common.constant.Config
import com.gh.gamecenter.R
import com.halo.assistant.HaloApp
import com.squareup.picasso.Picasso
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.io.ByteArrayOutputStream
@ -216,17 +217,21 @@ object ImageUtils {
.build()
}
// 获取bitmap
// 获取bitmap (使用 fresco 获取 gif bitmap 会为空https://github.com/facebook/fresco/issues/241)
// 所以这里换用 picasso
@SuppressLint("CheckResult")
@JvmStatic
fun display(context: Context?, url: String?, dataSubscriber: BaseBitmapDataSubscriber) {
val imageRequest = ImageRequestBuilder
.newBuilderWithSource(Uri.parse(url))
.setProgressiveRenderingEnabled(true)
.build()
Fresco.getImagePipeline()
.fetchDecodedImage(imageRequest, context)
.subscribe(dataSubscriber, CallerThreadExecutor.getInstance())
fun getBitmap(url: String, callback: BiCallback<Bitmap, Boolean>) {
Single.just(url)
.map { Picasso.with(HaloApp.getInstance().application).load(url).priority(Picasso.Priority.HIGH).get() }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
callback.onFirst(it)
}, {
callback.onSecond(true)
it.printStackTrace()
})
}
/**
@ -327,7 +332,7 @@ object ImageUtils {
//预加载图片
@JvmStatic
fun prefetchToDiskCache(url: String){
fun prefetchToDiskCache(url: String) {
val imagePipeline = Fresco.getImagePipeline()
val imageRequest = ImageRequest.fromUri(url)
imagePipeline.prefetchToDiskCache(imageRequest, HaloApp.getInstance().application)
@ -337,4 +342,7 @@ object ImageUtils {
fun onLoadFinal(imageInfo: ImageInfo?)
}
fun getVideoSnapshot(videoUrl: String, progress: Long): String {
return "$videoUrl?x-oss-process=video/snapshot,t_$progress,f_jpg,w_0,h_0"
}
}

View File

@ -6,12 +6,16 @@ import android.os.Build;
import android.provider.Settings;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import com.gh.common.constant.Constants;
import com.gh.common.exposure.ExposureEntity;
import com.gh.common.exposure.ExposureEvent;
import com.gh.common.exposure.ExposureSource;
import com.gh.common.exposure.meta.Meta;
import com.gh.common.exposure.meta.MetaUtil;
import com.gh.common.loghub.LoghubUtils;
import com.gh.download.DownloadDataHelper;
import com.gh.common.loghub.SimpleLogContainerEntity;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.entity.CommunityEntity;
import com.gh.gamecenter.entity.GameEntity;
@ -19,7 +23,6 @@ import com.gh.gamecenter.entity.SpecialColumn;
import com.gh.gamecenter.manager.UserManager;
import com.gh.gamecenter.qa.entity.Questions;
import com.halo.assistant.HaloApp;
import com.lightgame.download.DownloadEntity;
import com.lightgame.utils.Util_System_Phone_State;
import com.lightgame.utils.Utils;
@ -29,8 +32,6 @@ import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.Nullable;
/**
* Created by khy on 2/01/18.
*/
@ -53,55 +54,6 @@ public class LogUtils {
uploadVideoStreaming(object);
}
public static void uploadDownloadEvent(DownloadEntity downloadEntity) {
Context context = HaloApp.getInstance().getApplication();
Meta meta = MetaUtil.INSTANCE.getMeta();
JSONObject object = new JSONObject();
try {
object.put("event", DownloadDataHelper.getDownloadStatusAlias(downloadEntity));
object.put("msg", downloadEntity.getError());
object.put("status", downloadEntity.getStatus().getStatus());
// payload
JSONObject payloadObject = new JSONObject();
payloadObject.put("game_id", downloadEntity.getGameId());
payloadObject.put("gameName", downloadEntity.getName());
payloadObject.put("platform", downloadEntity.getPlatform());
payloadObject.put("package", downloadEntity.getPackageName());
payloadObject.put("filename", downloadEntity.getPath().substring(downloadEntity.getPath().lastIndexOf("/") + 1));
payloadObject.put("total_size", (downloadEntity.getSize() / 1024 / 1024));
payloadObject.put("completed_size", (downloadEntity.getProgress() / 1024 / 1024));
object.put("payload", payloadObject);
// meta
JSONObject metaObject = new JSONObject();
metaObject.put("android_id", meta.getAndroid_id());
metaObject.put("android_sdk", meta.getAndroid_sdk());
metaObject.put("android_version", meta.getAndroid_version());
metaObject.put("appVersion", meta.getAppVersion());
metaObject.put("channel", meta.getChannel());
metaObject.put("gid", meta.getGid());
metaObject.put("imei", meta.getImei());
metaObject.put("mac", meta.getMac());
metaObject.put("manufacturer", meta.getManufacturer());
metaObject.put("model", meta.getModel());
metaObject.put("network", DeviceUtils.getNetwork(context));
metaObject.put("os", meta.getOs());
metaObject.put("userId", meta.getUserId());
object.put("meta", metaObject);
object.put("timestamp", System.currentTimeMillis() / 1000);
} catch (Exception e) {
e.printStackTrace();
}
if (BuildConfig.DEBUG) {
Utils.log("LogUtils->" + object.toString());
}
LoghubUtils.log(object, "download_debug", false);
}
public static void uploadCommunityArticle(String tracers, String articleId, String articleTitle, int readTime, CommunityEntity community, SpecialColumn specialColumn) {
JSONObject object = new JSONObject();
try {
@ -420,6 +372,55 @@ public class LogUtils {
uploadVideoStreaming(object);
}
public static void uploadWelcomeDialog(String action, String dialogId, String linkTitle) {
ExposureEntity payload = new ExposureEntity();
payload.setWelcomeDialogId(dialogId);
payload.setWelcomeDialogId(linkTitle);
SimpleLogContainerEntity entity = new SimpleLogContainerEntity();
entity.setEvent("dialog");
entity.setAction(action);
entity.setMeta(MetaUtil.INSTANCE.getMeta());
entity.setPayload(payload);
entity.setTimestamp(System.currentTimeMillis() / 1000);
LoghubUtils.log(GsonUtils.toJsonIgnoreNull(entity), "event", false);
}
public static void uploadLikeFromWelcomeDialog() {
String dialogId = (String) HaloApp.get(Constants.WELCOME_DIALOG_ID, false);
String linkTitle = (String) HaloApp.get(Constants.WELCOME_DIALOG_LINK_TITLE, false);
ExposureEntity payload = new ExposureEntity();
payload.setWelcomeDialogId(dialogId);
payload.setWelcomeDialogLinkTitle(linkTitle);
SimpleLogContainerEntity entity = new SimpleLogContainerEntity();
entity.setEvent("like");
entity.setMeta(MetaUtil.INSTANCE.getMeta());
entity.setPayload(payload);
entity.setTimestamp(System.currentTimeMillis() / 1000);
LoghubUtils.log(GsonUtils.toJsonIgnoreNull(entity), "event", false);
}
public static void uploadCommentFromWelcomeDialog() {
String dialogId = (String) HaloApp.get(Constants.WELCOME_DIALOG_ID, false);
String linkTitle = (String) HaloApp.get(Constants.WELCOME_DIALOG_LINK_TITLE, false);
ExposureEntity payload = new ExposureEntity();
payload.setWelcomeDialogId(dialogId);
payload.setWelcomeDialogLinkTitle(linkTitle);
SimpleLogContainerEntity entity = new SimpleLogContainerEntity();
entity.setEvent("comment");
entity.setMeta(MetaUtil.INSTANCE.getMeta());
entity.setPayload(payload);
entity.setTimestamp(System.currentTimeMillis() / 1000);
LoghubUtils.log(GsonUtils.toJsonIgnoreNull(entity), "event", false);
}
private static void uploadShare(JSONObject object) {
Meta meta = MetaUtil.INSTANCE.getMeta();
JSONObject metaObject = new JSONObject();

View File

@ -55,6 +55,7 @@ public class LoginUtils {
@Override
public void onFailure(HttpException e) {
super.onFailure(e);
listener.onCaptcha(null);
if (e == null) {
Utils.toast(context, "无法获取验证码,请检查你的网络状态");
return;

View File

@ -96,7 +96,7 @@ object NotificationHelper {
}
@SuppressLint("CheckResult")
private fun show(activity: AppCompatActivity?) {
fun show(activity: AppCompatActivity?) {
if (activity == null) return
RetrofitManager.getInstance(activity).api.bootPopup
.subscribeOn(Schedulers.io())

View File

@ -16,6 +16,18 @@ object NumberUtils {
}
return s
}
@JvmStatic
fun transSimpleLetterCount(count: Int): String {
val s: String
s = if (count > 10000) {
val number = count / 10000f
val fmt = DecimalFormat("#.0")
fmt.format(number) + "W"
} else {
count.toString()
}
return s
}
@JvmStatic
fun transSimpleUsageTime(second: Long): String {

View File

@ -0,0 +1,129 @@
package com.gh.common.util
import android.app.Application
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import androidx.core.content.FileProvider
import com.gh.common.constant.Constants
import com.gh.common.xapk.XapkInstaller
import com.gh.gamecenter.BuildConfig
import com.halo.assistant.HaloApp
import com.lightgame.download.DownloadEntity
import com.lightgame.download.FileUtils
import com.lightgame.utils.Utils
import java.io.File
object PackageInstaller {
/**
* 为了兼容java代码
*/
@JvmStatic
fun install(context: Context, downloadEntity: DownloadEntity) {
install(context, downloadEntity, true)
}
/**
* 启动安装应用程序并取消通知栏安装消息
*
* 兼容所有包安装(apk,xapk),后续可加上apks
*
* 主动点击安装如果是xapk则需要toast提示
*/
@JvmStatic
fun install(context: Context, downloadEntity: DownloadEntity, showUnzipToast: Boolean) {
// 取消状态栏下载完成的通知,若存在
downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
val pkgPath = downloadEntity.path
if (XapkInstaller.XAPK_EXTENSION_NAME == pkgPath.getExtension()) {
XapkInstaller.install(context, downloadEntity, showUnzipToast)
} else {
install(context, downloadEntity.path)
}
}
/**
* 启动安装应用程序
*
* 除非特殊情况,请勿使用此方法进行应用安装操作
* 除非你已经确定该文件一定是Apk
*/
@JvmStatic
fun install(context: Context, pkgPath: String) {
try {
if (PackageUtils.isCanLaunchSetup(context, pkgPath)) {
val installIntent = getInstallIntent(context, pkgPath)
context.startActivity(installIntent)
} else {
DialogUtils.showPluginDialog(context) {
uninstall(context, pkgPath)
}
}
} catch (e: Exception) {
Utils.toast(context, e.message)
}
}
/**
* 获取启动安装意图
*
* 除非特殊情况,请勿使用此方法进行应用安装操作
* 除非你已经确定该文件一定是Apk且该次安装无需检查签名情况
*/
@JvmStatic
fun getInstallIntent(context: Context, path: String): Intent {
var uri = Uri.fromFile(File(path))
val installIntent = Intent(Intent.ACTION_VIEW)
if ("smartisan" == Build.MANUFACTURER) {
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, File(path))
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
installIntent.setDataAndType(uri, "application/vnd.android.package-archive")
installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
} else {
// 应用内更新不加 FLAG_ACTIVITY_NEW_TASK 在模拟器上会出现安装完成后安装界面也一并消失的类似闪退的表现
// Application 上下文就更不用说了
val pkgName = PackageUtils.getPackageNameByPath(context, path)
if (pkgName == context.packageName || context is Application) {
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
installIntent.setDataAndType(uri, "application/vnd.android.package-archive")
}
InstallUtils.getInstance(context).addInstall(PackageUtils.getPackageNameByPath(context, path))
return installIntent
}
/**
* 卸载应用
*/
@JvmStatic
fun uninstall(context: Context, path: String) {
val uninstallIntent = Intent()
uninstallIntent.action = Intent.ACTION_DELETE
uninstallIntent.addCategory(Intent.CATEGORY_DEFAULT)
val packageName = PackageUtils.getPackageNameByPath(context, path)
uninstallIntent.data = Uri.parse("package:$packageName")
InstallUtils.getInstance(context).addUninstall(packageName)
context.startActivity(uninstallIntent)
}
@JvmStatic
fun getDownloadPath(gameName: String?, format: String?): String {
return FileUtils.getDownloadPath(HaloApp.getInstance().application, getDownloadFileName(gameName, format))
}
private fun getDownloadFileName(gameName: String?, format: String?): String {
var fileExtension = "apk"
if (format == XapkInstaller.XAPK_EXTENSION_NAME) {
fileExtension = XapkInstaller.XAPK_EXTENSION_NAME
}
return MD5Utils.getContentMD5(gameName + "_" + System.currentTimeMillis()) + "." + fileExtension
}
}

View File

@ -1,7 +1,6 @@
package com.gh.common.util;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
@ -13,17 +12,18 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.text.TextUtils;
import androidx.annotation.Nullable;
import com.g00fy2.versioncompare.Version;
import com.gh.common.constant.Constants;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.entity.ApkEntity;
import com.gh.gamecenter.entity.GameEntity;
import com.gh.gamecenter.entity.GameUpdateEntity;
import com.gh.gamecenter.manager.PackagesManager;
import com.halo.assistant.HaloApp;
import com.lightgame.download.DownloadEntity;
import com.lightgame.utils.Utils;
import org.json.JSONArray;
@ -31,7 +31,6 @@ import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
@ -42,9 +41,6 @@ import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import androidx.annotation.Nullable;
import androidx.core.content.FileProvider;
public class PackageUtils {
public static final String publicKey = "OpenSSLRSAPublicKey{modulus=a8c4bb5748fec8d5c35db1a7a182d41ba4721a91131a417330af79ef4ddb43f9fa0ff4907b0a613bfe152de0ed8fc1b2e6f94a908aa98a5f7adc1ce814ba7ec919d75d9910bdfd8649b4789da6a90ffb61f0d23ac4f828a78fcd0d6f6120c1c43c1f87f7498a89eb40ca8e32dfc2f9d5c10d612b95192870223674e241e53305abf320d7eed76ded398778576e4db7b17b3bc6a792f13de5e43a6a5fae4276c73e6990ce97f68dff0ec16fc9594f175c8d49cd0d7877340d9de60942ca0efc737e50b6c295dfe0713e4532b4e810e1ea11b702b4a27753e41559cbceb247e7f044ec4e3ab2e8bccd8b9fd71286e63307550bcde86deee95adb8133076269135b,publicExponent=10001}";
@ -63,6 +59,8 @@ public class PackageUtils {
updateEntity.setId(gameEntity.getId());
updateEntity.setName(gameEntity.getName());
updateEntity.setIcon(gameEntity.getIcon());
updateEntity.setRawIcon(gameEntity.getRawIcon());
updateEntity.setIconSubscript(gameEntity.getIconSubscript());
updateEntity.setPackageName(apkEntity.getPackageName());
updateEntity.setSize(apkEntity.getSize());
updateEntity.setVersion(apkEntity.getVersion());
@ -76,6 +74,7 @@ public class PackageUtils {
updateEntity.setDownload(gameEntity.getDownload());
updateEntity.setIndexPlugin(gameEntity.getIndexPlugin());
updateEntity.setPluginDesc(gameEntity.getPluginDesc());
updateEntity.setFormat(apkEntity.getFormat());
updateList.add(updateEntity);
}
}
@ -113,6 +112,7 @@ public class PackageUtils {
updateEntity.setTagStyle(gameEntity.getTagStyle());
updateEntity.setIndexPlugin(gameEntity.getIndexPlugin());
updateEntity.setPluginDesc(gameEntity.getPluginDesc());
updateEntity.setFormat(apkEntity.getFormat());
updateList.add(updateEntity);
}
}
@ -232,29 +232,6 @@ public class PackageUtils {
return ret;
}
/*
* 启动安装应用程序
*/
public static void launchSetup(final Context context, DownloadEntity downloadEntity) {
// 取消状态栏下载完成的通知,若存在
downloadEntity.getMeta().put(Constants.MARK_ALREADY_TRIGGERED_INSTALLATION, "YES");
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity);
launchSetup(context, downloadEntity.getPath());
}
private static void launchSetup(final Context context, final String path) {
try {
if (isCanLaunchSetup(context, path)) {
context.startActivity(PackageUtils.getInstallIntent(context, path));
} else {
DialogUtils.showPluginDialog(context,
() -> context.startActivity(PackageUtils.getUninstallIntent(context, path)));
}
} catch (Exception e) {
Utils.toast(context, e.getMessage());
}
}
/**
* 根据 path 获取 apk 信息确定处理方式
*
@ -291,37 +268,6 @@ public class PackageUtils {
return compareSignatureBetweenInstalledAppWithApk(context, packageName, path);
}
/*
* 根据路径,获取安装游戏的意图
*/
public static Intent getInstallIntent(Context context, String path) {
return getInstallIntent(context, path, false);
}
public static Intent getInstallIntent(Context context, String path, boolean isInAppUpdate) {
Uri uri = Uri.fromFile(new File(path));
Intent installIntent = new Intent(Intent.ACTION_VIEW);
if ("smartisan".equals(Build.MANUFACTURER)) {
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID, new File(path));
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
installIntent.setDataAndType(uri, "application/vnd.android.package-archive");
installIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
// 应用内更新不加 FLAG_ACTIVITY_NEW_TASK 在模拟器上会出现安装完成后安装界面也一并消失的类似闪退的表现
// Application 上下文就更不用说了
if (isInAppUpdate || context instanceof Application) {
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
installIntent.setDataAndType(uri, "application/vnd.android.package-archive");
}
InstallUtils.getInstance(context).addInstall(getPackageNameByPath(context, path));
return installIntent;
}
/*
* 根据包名,获取卸载游戏的意图
*/
@ -552,9 +498,10 @@ public class PackageUtils {
&& !TextUtils.isEmpty(apkEntity.getGhVersion())
&& !PackageUtils.isSignedByGh(HaloApp.getInstance().getApplication(), apkEntity.getPackageName());
}
/**
* 获取调用者的进程名
*
* @param context 调用者的上下文
* @return 进程名
*/
@ -571,5 +518,38 @@ public class PackageUtils {
}
return null;
}
/**
* 应用是否在前台运行
*/
public static boolean isAppOnForeground(Context context) {
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;
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;
}
}
return false;
}
}

View File

@ -24,6 +24,9 @@ import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
@ -54,9 +57,6 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import static com.gh.common.util.LoginHelper.WEIBO_SCOPE;
/**
@ -247,17 +247,17 @@ public class ShareUtils {
e.printStackTrace();
}
contentView.setOnClickListener(v -> popupWindow.get().dismiss());
contentView.setOnClickListener(v -> safelyDismiss());
contentView.setOnKeyListener((v, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK
&& event.getRepeatCount() == 0
&& popupWindow != null
&& popupWindow.get() != null
&& popupWindow.get().isShowing()) {
if (callBack != null) {
callBack.onCancel();
}
popupWindow.get().dismiss();
safelyDismiss();
}
return false;
});
@ -298,7 +298,7 @@ public class ShareUtils {
}
if (mShareEntrance != ShareEntrance.shareGh) {
popupWindow.get().dismiss();
safelyDismiss();
}
}
@ -336,9 +336,9 @@ public class ShareUtils {
req.message = msg;
req.scene = SendMessageToWX.Req.WXSceneSession;
loadBitMap(shareIcon, msg, req);
loadBitmapAndShare(shareIcon, msg, req);
if (mShareEntrance != ShareEntrance.shareGh) {
popupWindow.get().dismiss();
safelyDismiss();
}
}
@ -346,10 +346,10 @@ public class ShareUtils {
return (type == null) ? String.valueOf(System.currentTimeMillis()) : type + System.currentTimeMillis();
}
private void loadBitMap(final String iconUrl, final WXMediaMessage msg, final SendMessageToWX.Req req) {
ImageUtils.display(mContext, iconUrl, new BaseBitmapDataSubscriber() {
private void loadBitmapAndShare(final String iconUrl, final WXMediaMessage msg, final SendMessageToWX.Req req) {
ImageUtils.getBitmap(iconUrl, new BiCallback<Bitmap, Boolean>() {
@Override
protected void onNewResultImpl(Bitmap bitmap) {
public void onFirst(Bitmap bitmap) {
if (mShareEntrance == ShareEntrance.video) {
// 分享类型为视频时裁为正方形
int dimension = Math.min(bitmap.getWidth(), bitmap.getHeight());
@ -367,8 +367,7 @@ public class ShareUtils {
}
@Override
protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
Utils.log("分享获取bitmap失败");
public void onSecond(Boolean aBoolean) {
}
});
}
@ -454,7 +453,7 @@ public class ShareUtils {
}
if (mShareEntrance != ShareEntrance.shareGh) {
popupWindow.get().dismiss();
safelyDismiss();
}
}
@ -497,9 +496,9 @@ public class ShareUtils {
req.message = msg;
req.scene = SendMessageToWX.Req.WXSceneTimeline;
loadBitMap(shareIcon, msg, req);
loadBitmapAndShare(shareIcon, msg, req);
if (mShareEntrance != ShareEntrance.shareGh) {
popupWindow.get().dismiss();
safelyDismiss();
}
}
@ -520,7 +519,7 @@ public class ShareUtils {
if (mShareEntrance != ShareEntrance.shareGh) {
popupWindow.get().dismiss();
safelyDismiss();
}
}
@ -562,8 +561,8 @@ public class ShareUtils {
e.printStackTrace();
}
if (mShareEntrance != ShareEntrance.shareGh) {
popupWindow.get().dismiss();
if (mShareEntrance != ShareEntrance.shareGh ) {
safelyDismiss();
}
}
@ -575,7 +574,7 @@ public class ShareUtils {
cmb.setText(copyContent);
if (mShareEntrance != ShareEntrance.shareGh) {
Utils.toast(mContext, "复制成功");
popupWindow.get().dismiss();
safelyDismiss();
} else {
Utils.toast(mContext, "复制成功,请到微信/QQ粘贴分享");
}
@ -661,7 +660,7 @@ public class ShareUtils {
break;
case 7:
if (mShareEntrance != ShareEntrance.shareGh) {
popupWindow.get().dismiss();
safelyDismiss();
} else {
shareType = "copy_link";
LogUtils.uploadShareType(shareType, shareEntrance.getName(), shareUrl, mTitle, mSummary, resourceId);
@ -689,6 +688,12 @@ public class ShareUtils {
}
}
}
private void safelyDismiss() {
if (popupWindow.get() != null) {
popupWindow.get().dismiss();
}
}
interface OnItemClickListener {
void onItemClick(int position);

View File

@ -5,10 +5,9 @@ import android.graphics.Color
import android.graphics.Typeface
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.style.AbsoluteSizeSpan
import android.text.style.ForegroundColorSpan
import android.text.style.StrikethroughSpan
import android.text.style.StyleSpan
import android.text.TextPaint
import android.text.style.*
import android.view.View
import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import com.gh.common.view.CenterImageSpan
@ -65,6 +64,23 @@ class SpanBuilder(content: String) {
return this
}
fun click(start: Int, end: Int, colorRes: Int, onClick: () -> Unit): SpanBuilder {
val clickSpan = object : ClickableSpan() {
override fun updateDrawState(ds: TextPaint) {
super.updateDrawState(ds)
ds.color = ContextCompat.getColor(HaloApp.getInstance().application, colorRes)
ds.isUnderlineText = false
}
override fun onClick(widget: View) {
onClick.invoke()
}
}
spannableString.setSpan(clickSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
return this
}
fun build(): SpannableStringBuilder {
return spannableString
}

View File

@ -23,6 +23,7 @@ object TimeUtils {
fun getFormatTime(time: Long, pattern: String = "yyyy-MM-dd"): String {
val f = SimpleDateFormat(pattern, Locale.CHINA)
f.timeZone = TimeZone.getTimeZone("Asia/Shanghai")
return f.format(Date(time * 1000))
}
@ -84,4 +85,19 @@ object TimeUtils {
val second = length % 60
return String.format(Locale.CHINA, "%02d:%02d", minute, second)
}
@JvmStatic
fun getDayString(date: Long): String {
val offSet = Calendar.getInstance().timeZone.rawOffset
val today = (System.currentTimeMillis() + offSet) / 86400000
val start = (date * 1000 + offSet) / 86400000
return when ((start - today).toInt()) {
-2 -> "(前天)"
-1 -> "(昨天)"
0 -> "(今天)"
1 -> "(明天)"
2 -> "(后天)"
else -> ""
}
}
}

View File

@ -61,7 +61,7 @@ object VideoRecordUtils {
if (!UserManager.getInstance().isLoggedIn) return
val requestMap = HashMap<String, Any>()
val videoIds = videoRecordSet.toList().map { it.videoId }
requestMap["g_id"] = HaloApp.getInstance().gid
requestMap["g_id"] = HaloApp.getInstance().gid ?: ""
requestMap["time"] = time
requestMap["video_id"] = videoIds
RetrofitManager.getInstance(HaloApp.getInstance().application).api

View File

@ -5,10 +5,12 @@ import android.graphics.drawable.Animatable;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
@ -19,6 +21,7 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.gh.gamecenter.R;
import com.scwang.smartrefresh.layout.api.RefreshInternal;
import com.scwang.smartrefresh.layout.api.RefreshKernel;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
@ -74,21 +77,21 @@ public abstract class AbstractSwipeRefreshHeader<T extends AbstractSwipeRefreshH
progressView.setId(ID_IMAGE_PROGRESS);
centerLayout.setId(android.R.id.widget_frame);
LinearLayout.LayoutParams lpHeaderText = new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
centerLayout.addView(titleView, lpHeaderText);
// LinearLayout.LayoutParams lpHeaderText = new LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
// centerLayout.addView(titleView, lpHeaderText);
RelativeLayout.LayoutParams lpHeaderLayout = new RelativeLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
lpHeaderLayout.addRule(CENTER_IN_PARENT);
thisGroup.addView(centerLayout, lpHeaderLayout);
RelativeLayout.LayoutParams lpArrow = new RelativeLayout.LayoutParams(mDensityUtil.dip2px(15), mDensityUtil.dip2px(15));
RelativeLayout.LayoutParams lpArrow = new RelativeLayout.LayoutParams(mDensityUtil.dip2px(35), mDensityUtil.dip2px(35));
lpArrow.addRule(CENTER_VERTICAL);
lpArrow.addRule(LEFT_OF, android.R.id.widget_frame);
lpArrow.addRule(CENTER_IN_PARENT, android.R.id.widget_frame);
thisGroup.addView(arrowView, lpArrow);
RelativeLayout.LayoutParams lpProgress = new RelativeLayout.LayoutParams((ViewGroup.LayoutParams) lpArrow);
lpProgress.addRule(CENTER_VERTICAL);
lpProgress.addRule(LEFT_OF, android.R.id.widget_frame);
lpProgress.addRule(CENTER_IN_PARENT, android.R.id.widget_frame);
progressView.animate().setInterpolator(new LinearInterpolator());
thisGroup.addView(progressView, lpProgress);

View File

@ -190,8 +190,8 @@ class AdBannerView : LinearLayout {
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val ad = mDatas[position % mDatas.size]
val view = holder.itemView as ImageView
Picasso.with(context).load(ad.image).into(view)
val view = holder.itemView as SimpleDraweeView
ImageUtils.display(view,ad.image)
holder.itemView.setOnClickListener {
onItemClick?.invoke(position % mDatas.size)
}

View File

@ -9,6 +9,8 @@ import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import com.gh.common.util.DisplayUtils;
public class CropImageBorderView extends View {
/**
@ -28,7 +30,7 @@ public class CropImageBorderView extends View {
*/
private int mBorderWidth = 1;
private Paint mPaint, mPaintRect;
private Paint mPaint, mPaintRect, mAnglePaint;
private float mRatio = 1; // 宽高比
@ -48,7 +50,7 @@ public class CropImageBorderView extends View {
// 绘制阴影
mPaintRect = new Paint();
mPaintRect.setARGB(136, 0, 0, 0);
mPaintRect.setARGB(80, 0, 0, 0);
mPaintRect.setStyle(Style.FILL);
// 绘制边框
@ -56,7 +58,12 @@ public class CropImageBorderView extends View {
mPaint.setStyle(Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.WHITE);
mPaint.setStrokeWidth(mBorderWidth);
mPaint.setStrokeWidth(0.5F);
// 绘制边框
mAnglePaint = new Paint();
mAnglePaint.setStyle(Style.FILL);
mAnglePaint.setColor(Color.WHITE);
}
@ -86,6 +93,45 @@ public class CropImageBorderView extends View {
canvas.drawRect(mHorizontalPadding, mVerticalPadding,
mHorizontalPadding + mWidth, getHeight() - mVerticalPadding, mPaint);
// 左上
canvas.drawRect(mHorizontalPadding - DisplayUtils.dip2px(1F)
, mVerticalPadding - DisplayUtils.dip2px(1F)
, mHorizontalPadding + DisplayUtils.dip2px(16F)
, mVerticalPadding + DisplayUtils.dip2px(1F), mAnglePaint);
canvas.drawRect(mHorizontalPadding - DisplayUtils.dip2px(1F)
, mVerticalPadding - DisplayUtils.dip2px(1F)
, mHorizontalPadding + DisplayUtils.dip2px(1F)
, mVerticalPadding + DisplayUtils.dip2px(16F), mAnglePaint);
// 右上
canvas.drawRect(mHorizontalPadding + mWidth - DisplayUtils.dip2px(16F)
, mVerticalPadding - DisplayUtils.dip2px(1F)
, mHorizontalPadding + mWidth
, mVerticalPadding + DisplayUtils.dip2px(1F), mAnglePaint);
canvas.drawRect(mHorizontalPadding + mWidth - DisplayUtils.dip2px(1F)
, mVerticalPadding - DisplayUtils.dip2px(1F)
, mHorizontalPadding + mWidth + DisplayUtils.dip2px(1F)
, mVerticalPadding + DisplayUtils.dip2px(16F), mAnglePaint);
// 左下
canvas.drawRect(mHorizontalPadding - DisplayUtils.dip2px(1F)
, getHeight() - mVerticalPadding - DisplayUtils.dip2px(16F)
, mHorizontalPadding + DisplayUtils.dip2px(1F)
, getHeight() - mVerticalPadding - DisplayUtils.dip2px(1F), mAnglePaint);
canvas.drawRect(mHorizontalPadding - DisplayUtils.dip2px(1F)
, getHeight() - mVerticalPadding - DisplayUtils.dip2px(1F)
, mHorizontalPadding + DisplayUtils.dip2px(16F)
, getHeight() - mVerticalPadding + DisplayUtils.dip2px(1F), mAnglePaint);
// 右下
canvas.drawRect(mHorizontalPadding + mWidth - DisplayUtils.dip2px(16F)
, getHeight() - mVerticalPadding - DisplayUtils.dip2px(1F)
, mHorizontalPadding + mWidth
, getHeight() - mVerticalPadding + DisplayUtils.dip2px(1F), mAnglePaint);
canvas.drawRect(mHorizontalPadding + mWidth - DisplayUtils.dip2px(1F)
, getHeight() - mVerticalPadding - DisplayUtils.dip2px(16F)
, mHorizontalPadding + mWidth + DisplayUtils.dip2px(1F)
, getHeight() - mVerticalPadding + DisplayUtils.dip2px(1F), mAnglePaint);
}
public void setHorizontalPadding(int mHorizontalPadding, float ratio) {

View File

@ -2,7 +2,6 @@ package com.gh.common.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.ImageView;
@ -58,24 +57,17 @@ public class CropImageCustom extends RelativeLayout {
return mZoomImageView.clip();
}
// 用户头像压缩规则
public boolean savePicture(String path) {
float limitSize = 900;
int quality = 90;
Bitmap bitmap = mZoomImageView.clip();
File file = new File(path);
if (bitmap.getHeight() > limitSize || bitmap.getWidth() > limitSize) {
Matrix matrix = new Matrix();
matrix.setScale(limitSize / bitmap.getWidth(), limitSize / bitmap.getHeight());
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
}
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
bitmap.compress(Bitmap.CompressFormat.WEBP, quality, bos);
bos.flush();
bos.close();
Bitmap clip = mZoomImageView.clip();
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(path)));
clip.compress(Bitmap.CompressFormat.WEBP, 100, bos);
bos.flush();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
return false;

View File

@ -318,11 +318,18 @@ public class CropImageZoomView extends AppCompatImageView implements
if (dw <= getWidth() - mHorizontalPadding * 2
&& dh <= getHeight() - mVerticalPadding * 2) {// 如果图片的高度<截图区高度,并且图片宽度<截图区宽度时
float scaleW = (getWidth() * 1.0f - mHorizontalPadding * 2)
/ dw;
float scaleW = (getWidth() * 1.0f - mHorizontalPadding * 2) / dw;
float scaleH = (getHeight() * 1.0f - mVerticalPadding * 2) / dh;
scale = Math.max(scaleW, scaleH);
}
// 宽、高均超出截图区域
if (dw > getWidth() - mHorizontalPadding * 2 && dh > getHeight() - mVerticalPadding * 2) {
float scaleW = (getWidth() * 1.0f - mHorizontalPadding * 2) / dw;
float scaleH = (getHeight() * 1.0f - mVerticalPadding * 2) / dh;
scale = Math.max(scaleW, scaleH);
}
initScale = scale;
SCALE_MID = initScale * 2;
SCALE_MAX = initScale * 4;

View File

@ -0,0 +1,25 @@
package com.gh.common.view
import android.content.Context
import android.widget.TextView
import androidx.core.content.ContextCompat
import com.gh.common.util.DisplayUtils
import com.gh.common.util.dip2px
import com.gh.gamecenter.R
import com.github.mikephil.charting.components.MarkerView
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.highlight.Highlight
import com.github.mikephil.charting.utils.MPPointF
class CustomMarkerView(context: Context) : MarkerView(context, R.layout.layout_chart_markerview) {
var markerView: TextView = findViewById(R.id.markerView)
override fun refreshContent(e: Entry, highlight: Highlight?) {
markerView.text = e.y.toInt().toString()
super.refreshContent(e, highlight)
}
override fun getOffset(): MPPointF {
return MPPointF((-(width / 2)).toFloat(), (-height).toFloat() - 3f.dip2px())
}
}

View File

@ -15,12 +15,12 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.ProgressBar;
import com.gh.common.util.DisplayUtils;
import com.gh.gamecenter.R;
import androidx.annotation.StringRes;
import androidx.core.content.ContextCompat;
import com.gh.common.util.DisplayUtils;
import com.gh.gamecenter.R;
public class DownloadProgressBar extends ProgressBar {
public static final int MAX_LENGTH = 1000;
public static final int DOWNLOAD_NORMAL_STYLE = 0;
@ -38,7 +38,11 @@ public class DownloadProgressBar extends ProgressBar {
DOWNLOADING_PLUGIN,
RESERVABLE,
RESERVED,
H5_GAME
H5_GAME,
XAPK_UNZIPPING,
XAPK_SUCCESS,
XAPK_FAILURE,
}
private PorterDuffXfermode mDuffXFerMode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
@ -214,6 +218,12 @@ public class DownloadProgressBar extends ProgressBar {
setProgressDrawable(getResources().getDrawable(R.drawable.game_item_btn_pause_dn));
mDefaultColor = ContextCompat.getColor(getContext(), R.color.all_white);
break;
case XAPK_FAILURE:
case XAPK_SUCCESS:
case XAPK_UNZIPPING:
setProgressDrawable(getResources().getDrawable(R.drawable.progressbar_xapk_detail_style));
mDefaultColor = ContextCompat.getColor(getContext(), R.color.all_white);
break;
}
mDownloadType = downloadType;

View File

@ -0,0 +1,117 @@
package com.gh.common.view
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.ViewConfiguration
import com.github.piasy.biv.view.BigImageView
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
import kotlin.math.sqrt
class DraggableBigImageView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
: BigImageView(context, attrs, defStyle) {
private var mInitScale: Float? = null
private val mScaledTouchSlop by lazy { ViewConfiguration.get(context).scaledTouchSlop }
private val mDismissEdge by lazy { height * 0.12 }
private var mSingleTouch = true
private var mFakeDragOffset = 0F
private var mLastX = 0F
private var mLastY = 0F
private var mListener: DragListener? = null
fun setDragListener(listener: DragListener) {
mListener = listener
}
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
when (event.actionMasked) {
MotionEvent.ACTION_POINTER_DOWN -> {
setSingleTouch(false)
animate()
.translationX(0F).translationY(0F).scaleX(1F).scaleY(1F)
.setDuration(200).start()
}
MotionEvent.ACTION_DOWN -> if (mInitScale == null || mInitScale == 0F) mInitScale = ssiv?.scale
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> up()
MotionEvent.ACTION_MOVE -> {
if (mSingleTouch && mInitScale != 0F && ssiv?.scale == mInitScale) {
if (mLastX == 0F) mLastX = event.rawX
if (mLastY == 0F) mLastY = event.rawY
val offsetX = event.rawX - mLastX
val offsetY = event.rawY - mLastY
fakeDrag(offsetX, offsetY)
}
}
}
return super.dispatchTouchEvent(event)
}
private fun fakeDrag(offsetX: Float, offsetY: Float) {
if (mFakeDragOffset == 0F) {
// 大于60度才响应下拉消失
if (abs(offsetY) / sqrt(3F) > abs(offsetX)) {
if (offsetY > mScaledTouchSlop) {
mFakeDragOffset = mScaledTouchSlop.toFloat()
} else if (offsetY < -mScaledTouchSlop) {
mFakeDragOffset = (-mScaledTouchSlop).toFloat()
}
}
}
if (mFakeDragOffset != 0F) {
val fixedOffsetY = offsetY - mFakeDragOffset
parent?.requestDisallowInterceptTouchEvent(true)
val fraction = abs(max(-1F, min(1F, fixedOffsetY / height)))
val fakeScale = 1 - min(0.4F, fraction)
scaleX = fakeScale
scaleY = fakeScale
translationY = fixedOffsetY
translationX = offsetX / 2
mListener?.onDrag(this, fraction)
}
}
private fun up() {
parent?.requestDisallowInterceptTouchEvent(false)
setSingleTouch(true)
mFakeDragOffset = 0F
mLastX = 0F
mLastY = 0F
if (abs(translationY) > mDismissEdge) {
mListener?.onRelease(this, scaleX)
} else {
val offsetY = translationY
val fraction = min(1F, offsetY / height)
mListener?.onRestore(this, fraction)
animate().translationX(0F)
.translationY(0F)
.scaleX(1F)
.scaleY(1F)
.setDuration(200).start()
}
}
fun isDragging(): Boolean {
return mFakeDragOffset != 0F
}
private fun setSingleTouch(value: Boolean) {
mSingleTouch = value
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
animate().cancel()
}
interface DragListener {
fun onDrag(draggableBigImageView: DraggableBigImageView, fraction: Float)
fun onRelease(draggableBigImageView: DraggableBigImageView, scale: Float)
fun onRestore(draggableBigImageView: DraggableBigImageView, fraction: Float)
}
}

View File

@ -194,6 +194,8 @@ public class ExpandTextView extends AppCompatTextView {
msp.replace(expandTextStartPosition, length, finalEndText + mExpandText);
if (expandTextStartPosition + mEndText.length() >= msp.length()) return;
msp.setSpan(new ClickableSpan() {
@Override
public void updateDrawState(@NonNull TextPaint ds) {

View File

@ -0,0 +1,53 @@
package com.gh.common.view
import android.content.Context
import android.text.TextUtils
import android.util.AttributeSet
import android.view.View
import androidx.constraintlayout.widget.ConstraintLayout
import com.facebook.drawee.generic.RoundingParams
import com.facebook.drawee.view.SimpleDraweeView
import com.gh.common.util.DisplayUtils
import com.gh.common.util.ImageUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.entity.GameEntity
import kotlinx.android.synthetic.main.layout_game_icon.view.*
class GameIconView : ConstraintLayout {
private var mCornerRadius = 10
constructor(context: Context) : super(context, null)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs, 0) {
initView(attrs)
}
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initView(attrs)
}
fun initView(attrs: AttributeSet) {
View.inflate(context, R.layout.layout_game_icon, this)
val ta = context.obtainStyledAttributes(attrs, R.styleable.GameIconView)
mCornerRadius = ta.getDimensionPixelSize(R.styleable.GameIconView_gameIconCornerRadius, DisplayUtils.dip2px(10F))
ta.recycle()
val roundingParams = RoundingParams.fromCornersRadius(mCornerRadius.toFloat())
gameIconIv.hierarchy.roundingParams = roundingParams
gameIconDecoratorIv.hierarchy.roundingParams = roundingParams
}
fun setGameEntity(game: GameEntity) {
if (!TextUtils.isEmpty(game.rawIcon)) {
ImageUtils.display(gameIconIv, game.rawIcon)
ImageUtils.display(gameIconDecoratorIv, game.iconSubscript)
} else {
ImageUtils.display(gameIconIv, game.icon)
}
}
fun getIconIv(): SimpleDraweeView = gameIconIv
fun getIconDecoratorIv(): SimpleDraweeView = gameIconDecoratorIv
}

View File

@ -4,9 +4,10 @@ import android.content.Context;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
import com.gh.common.util.DisplayUtils;
public class HorizontalItemDecoration extends RecyclerView.ItemDecoration {
@ -14,6 +15,7 @@ public class HorizontalItemDecoration extends RecyclerView.ItemDecoration {
private Paint paint;
private int mInterval = 0;
private int mSize = 0;
private boolean mIncludeEnd = false;
public HorizontalItemDecoration(Context context, int interval, int size) {
paint = new Paint();
@ -22,6 +24,15 @@ public class HorizontalItemDecoration extends RecyclerView.ItemDecoration {
mSize = size;
}
public HorizontalItemDecoration(Context context, int interval, int size, boolean includeEnd) {
paint = new Paint();
paint.setColor(Color.WHITE);
mInterval = DisplayUtils.dip2px(context, interval);
mSize = size;
mIncludeEnd = includeEnd;
}
// @Override
// public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
// int top = parent.getPaddingTop();
@ -39,7 +50,7 @@ public class HorizontalItemDecoration extends RecyclerView.ItemDecoration {
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
if (parent.getChildPosition(view) == mSize - 1) {
if (!mIncludeEnd && parent.getChildPosition(view) == mSize - 1) {
outRect.set(0, 0, 0, 0);
} else {
outRect.set(0, 0, mInterval, 0);

View File

@ -9,13 +9,14 @@ import android.text.style.ClickableSpan;
import android.util.AttributeSet;
import android.view.View;
import androidx.appcompat.widget.AppCompatTextView;
import com.gh.gamecenter.R;
import java.util.ArrayList;
import java.util.List;
public class MessageSpannableTextView extends androidx.appcompat.widget.AppCompatTextView {
public class MessageSpannableTextView extends AppCompatTextView {
private OnSpannableClickListener mSpannableClickListener;

View File

@ -1,9 +1,8 @@
package com.gh.common.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.LayoutInflater;
@ -11,6 +10,9 @@ import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import com.scwang.smartrefresh.layout.R;
import com.scwang.smartrefresh.layout.api.RefreshHeader;
import com.scwang.smartrefresh.layout.api.RefreshLayout;
@ -23,6 +25,7 @@ import com.scwang.smartrefresh.layout.util.DensityUtil;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@SuppressLint("RestrictedApi")
@SuppressWarnings({"unused", "UnusedReturnValue"})
public class SwipeRefreshHeader extends AbstractSwipeRefreshHeader<SwipeRefreshHeader> implements RefreshHeader {
@ -83,7 +86,7 @@ public class SwipeRefreshHeader extends AbstractSwipeRefreshHeader<SwipeRefreshH
} else {
mArrowDrawable = new ArrowDrawable();
mArrowDrawable.setColor(0xff666666);
mArrowView.setImageDrawable(mArrowDrawable);
mArrowView.setImageResource(com.gh.gamecenter.R.drawable.refresh_icon);
}
if (ta.hasValue(R.styleable.ClassicsHeader_srlDrawableProgress)) {
@ -91,7 +94,7 @@ public class SwipeRefreshHeader extends AbstractSwipeRefreshHeader<SwipeRefreshH
} else {
mProgressDrawable = new ProgressDrawable();
mProgressDrawable.setColor(0xff666666);
mProgressView.setImageDrawable(mProgressDrawable);
mProgressView.setImageResource(com.gh.gamecenter.R.drawable.refresh_anim);
}
if (ta.hasValue(R.styleable.ClassicsHeader_srlTextSizeTitle)) {
@ -140,7 +143,7 @@ public class SwipeRefreshHeader extends AbstractSwipeRefreshHeader<SwipeRefreshH
mCenterLayout.setVisibility(VISIBLE);
mTitleText.setText(REFRESH_HEADER_PULLING);
mArrowView.setVisibility(VISIBLE);
mArrowView.animate().rotation(0);
// mArrowView.animate().rotation(0);
break;
case Refreshing:
case RefreshReleased:
@ -153,7 +156,7 @@ public class SwipeRefreshHeader extends AbstractSwipeRefreshHeader<SwipeRefreshH
successView.setVisibility(GONE);
mCenterLayout.setVisibility(VISIBLE);
mTitleText.setText(REFRESH_HEADER_RELEASE);
mArrowView.animate().rotation(180);
// mArrowView.animate().rotation(180);
break;
case Loading:
successView.setVisibility(GONE);

View File

@ -0,0 +1,37 @@
package com.gh.common.view
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.ViewConfiguration
import androidx.recyclerview.widget.RecyclerView
import kotlin.math.abs
/**
* 如非特殊情况请勿使用,会强行改变RecyclerView的拦截事件,可能会出现意料之外的问题
*/
class TouchSlopRecyclerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null)
: RecyclerView(context, attrs) {
private var mPrevX = 0f
// 触控角度,用来控制触发左右滑动的敏感度
var touchSlop = ViewConfiguration.get(context).scaledTouchSlop
// 默认关闭,否则会误杀上下滑动事件
var touchSlopEnabled = false
override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_DOWN -> mPrevX = event.x
MotionEvent.ACTION_MOVE -> {
val eventX = event.x
val xDiff = abs(eventX - mPrevX)
if (touchSlopEnabled && xDiff > touchSlop) {
return false
}
}
}
return super.onInterceptTouchEvent(event)
}
}

View File

@ -8,6 +8,7 @@ import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import com.gh.base.fragment.BaseDialogFragment
import com.gh.common.TimeElapsedHelper
import com.gh.common.constant.Constants
import com.gh.common.constant.Constants.SP_LAST_OPENING_ID
import com.gh.common.constant.Constants.SP_LAST_OPENING_TIME
import com.gh.common.util.*
@ -15,12 +16,19 @@ import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.DialogWelcomeBinding
import com.gh.gamecenter.entity.CommunityEntity
import com.gh.gamecenter.entity.WelcomeDialogEntity
import com.halo.assistant.HaloApp
class WelcomeDialog : BaseDialogFragment() {
companion object {
const val TAG = "welcome_dialog"
@JvmStatic
fun getInstance(welcomeEntity: WelcomeDialogEntity?) = WelcomeDialog().apply { mWelcomeEntity = welcomeEntity }
fun getInstance(welcomeEntity: WelcomeDialogEntity?) = WelcomeDialog().apply {
arguments = Bundle()
arguments?.putParcelable(TAG, welcomeEntity)
LogUtils.uploadWelcomeDialog("show", welcomeEntity?.id, welcomeEntity?.text)
}
}
private var mWelcomeEntity: WelcomeDialogEntity? = null
@ -34,12 +42,18 @@ class WelcomeDialog : BaseDialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mWelcomeEntity = arguments?.getParcelable(TAG)
mTimeHelper = TimeElapsedHelper(this)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.dialog_welcome, container, false)
binding.ivOpeningCover.setOnClickListener {
HaloApp.put(Constants.WELCOME_DIALOG_ID, mWelcomeEntity?.id)
HaloApp.put(Constants.WELCOME_DIALOG_LINK_TITLE, mWelcomeEntity?.text)
LogUtils.uploadWelcomeDialog("click", mWelcomeEntity?.id, mWelcomeEntity?.text)
when (mWelcomeEntity?.type) {
EntranceUtils.HOST_ARTICLE -> {
DirectUtils.directToArticle(requireContext(), mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME)
@ -51,10 +65,10 @@ class WelcomeDialog : BaseDialogFragment() {
DirectUtils.directToSubject(requireContext(), mWelcomeEntity?.link!!, null, EntranceUtils.ENTRANCE_WELCOME)
}
EntranceUtils.HOST_QUESTION -> {
DirectUtils.directToQuestionDetail(requireContext(), mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME, "首页弹窗")
DirectUtils.directToQuestionDetail(requireContext(), mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME)
}
EntranceUtils.HOST_ANSWER -> {
DirectUtils.directToAnswerDetail(requireContext(), mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME, "首页弹窗")
DirectUtils.directToAnswerDetail(requireContext(), mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME)
}
EntranceUtils.HOST_WEB -> {
DirectUtils.directToWebView(requireContext(), mWelcomeEntity?.link!!, EntranceUtils.ENTRANCE_WELCOME)
@ -67,7 +81,7 @@ class WelcomeDialog : BaseDialogFragment() {
}
// else -> DialogUtils.showLowVersionDialog(context)
else -> DirectUtils.directToLinkPage(requireContext(), mWelcomeEntity
?: WelcomeDialogEntity(), EntranceUtils.ENTRANCE_WELCOME, "首页弹窗")
?: WelcomeDialogEntity(), EntranceUtils.ENTRANCE_WELCOME, "")
}
mDismissByClickImage = true
@ -111,15 +125,9 @@ class WelcomeDialog : BaseDialogFragment() {
}
override fun onDestroy() {
val type = if (mDismissByClickImage) {
"点击图片"
} else {
"点击关闭"
}
val type = if (mDismissByClickImage) "点击图片" else "点击关闭"
DataUtils.trackTimeEvent(context, "启动弹窗",
mTimeHelper?.elapsedTime!!,
type, "No parameter.")
MtaHelper.onEventWithTime("启动弹窗", mTimeHelper?.elapsedTime!!, type, "No parameter.")
PreferenceManager.getDefaultSharedPreferences(context?.applicationContext).edit {
putString(SP_LAST_OPENING_ID, mWelcomeEntity?.id)

View File

@ -75,6 +75,12 @@ public class DWebView extends WebView {
}
}
@Keep
@JavascriptInterface
public String call(String methodName) {
return call(methodName, "");
}
@Keep
@JavascriptInterface
public String call(String methodName, String argStr) {
@ -93,7 +99,7 @@ public class DWebView extends WebView {
PrintDebugInfo(error);
return ret.toString();
}
Object arg=null;
Object arg = null;
Method method = null;
String callback = null;
@ -102,7 +108,7 @@ public class DWebView extends WebView {
if (args.has("_dscbstub")) {
callback = args.getString("_dscbstub");
}
if(args.has("data")) {
if (args.has("data")) {
arg = args.get("data");
}
} catch (JSONException e) {
@ -123,7 +129,10 @@ public class DWebView extends WebView {
try {
method = cls.getMethod(methodName, new Class[]{Object.class});
} catch (Exception ex) {
try {
method = cls.getMethod(methodName, new Class[]{});
} catch (Exception ignore) {
}
}
}
@ -187,7 +196,12 @@ public class DWebView extends WebView {
}
});
} else {
retData = method.invoke(jsb, arg);
Class<?>[] methodParameterTypes = method.getParameterTypes();
if (methodParameterTypes.length == 0) {
retData = method.invoke(jsb);
} else {
retData = method.invoke(jsb, arg);
}
ret.put("code", 0);
ret.put("data", retData);
return ret.toString();
@ -307,7 +321,10 @@ public class DWebView extends WebView {
try {
method = cls.getMethod(nameStr[1], new Class[]{Object.class});
} catch (Exception ex) {
try {
method = cls.getMethod(nameStr[1], new Class[]{});
} catch (Exception ignore) {
}
}
}
if (method != null) {
@ -336,7 +353,7 @@ public class DWebView extends WebView {
|| javascriptCloseWindowListener.onClose()) {
Context context = getContext();
if (context instanceof Activity) {
((Activity)context).onBackPressed();
((Activity) context).onBackPressed();
}
}
}
@ -359,7 +376,7 @@ public class DWebView extends WebView {
@Keep
@JavascriptInterface
public void returnValue(final Object obj){
public void returnValue(final Object obj) {
runOnMainThread(new Runnable() {
@Override
public void run() {
@ -422,9 +439,9 @@ public class DWebView extends WebView {
runOnMainThread(new Runnable() {
@Override
public void run() {
if (url != null && url.startsWith("javascript:")){
if (url != null && url.startsWith("javascript:")) {
DWebView.super.loadUrl(url);
}else{
} else {
callInfoList = new ArrayList<>();
DWebView.super.loadUrl(url);
}
@ -444,9 +461,9 @@ public class DWebView extends WebView {
runOnMainThread(new Runnable() {
@Override
public void run() {
if (url != null && url.startsWith("javascript:")){
if (url != null && url.startsWith("javascript:")) {
DWebView.super.loadUrl(url, additionalHttpHeaders);
}else{
} else {
callInfoList = new ArrayList<>();
DWebView.super.loadUrl(url, additionalHttpHeaders);
}
@ -996,7 +1013,4 @@ public class DWebView extends WebView {
mainHandler.post(runnable);
}
}

View File

@ -0,0 +1,15 @@
package com.gh.common.xapk
import com.lightgame.download.DownloadEntity
interface IXapkUnzipListener {
fun onProgress(downloadEntity: DownloadEntity, unzipPath: String, unzipSize: Long, unzipProgress: Long)
fun onNext(downloadEntity: DownloadEntity, unzipPath: String)
fun onCancel(downloadEntity: DownloadEntity)
fun onFailure(downloadEntity: DownloadEntity, exception: Exception)
fun onSuccess(downloadEntity: DownloadEntity)
}

View File

@ -0,0 +1,175 @@
package com.gh.common.xapk
import android.content.Context
import android.os.Build
import com.gh.common.AppExecutor
import com.gh.common.util.*
import com.gh.download.DownloadManager
import com.gh.gamecenter.BuildConfig
import com.halo.assistant.HaloApp
import com.lightgame.download.DataChanger
import com.lightgame.download.DownloadEntity
import com.lightgame.utils.Util_System_Phone_State
import com.lightgame.utils.Utils
import java.text.DecimalFormat
import java.util.*
/**
* 目前已知的Xapk内容是:只有一个apk包和一个或做多个obb数据包(多余文件不解压,如果存在多个apk包,则以下安装无效)
*
* 每次安装都会重新跑一遍解压安装流程
* 目前检验是否已解压的根据是存在相同文件名且大小一致(暂时想不到校验MD5值的方法)
*
* obb文件直接解压至根目录即可,无需操作文件
* apk文件这解压的gh-download文件夹中
*/
object XapkInstaller : IXapkUnzipListener {
private const val XAPK_PACKAGE_PATH_TAG = "xapk_package_path"
// private const val XAPK_DATA_PATH_TAG = "xapk_data_path"
const val XAPK_EXTENSION_NAME = "xapk"
// 通过解压过程存放于 DownloadEntity meta
const val XAPK_UNZIP_PERCENT = "unzip_percent"
const val XAPK_UNZIP_STATUS = "unzip_status"
const val XAPK_DATA_EXTENSION_NAME = "obb"
const val PACKAGE_EXTENSION_NAME = "apk"
private var mContext = HaloApp.getInstance().application.applicationContext
// 是否需要开启特定线程处理
private val mXapkUnzipThreadMap = Collections.synchronizedMap(HashMap<String, XapkUnzipThread>())
// 按并行解压
@JvmStatic
fun install(context: Context, downloadEntity: DownloadEntity, showUnzipToast: Boolean = false) {
this.mContext = context
val filePath = downloadEntity.path
if (XAPK_EXTENSION_NAME == filePath.getExtension()) {
unzipXapkFile(downloadEntity)
if (showUnzipToast) {
Utils.toast(mContext, "解压过程请勿退出光环助手!")
}
} else {
throwExceptionInDebug("如果是Apk包请使用PackageInstaller进行安装")
PackageInstaller.install(mContext, downloadEntity)
}
}
private fun unzipXapkFile(downloadEntity: DownloadEntity) {
if (mXapkUnzipThreadMap[downloadEntity.path] == null) {
val xapkUnzipThread = XapkUnzipThread(downloadEntity, this)
xapkUnzipThread.start()
mXapkUnzipThreadMap[downloadEntity.path] = xapkUnzipThread
} else {
debugOnly {
Utils.log("unzip", "重复解压,该文件解压已在队列中")
}
}
}
override fun onProgress(downloadEntity: DownloadEntity, unzipPath: String, unzipSize: Long, unzipProgress: Long) {
AppExecutor.uiExecutor.execute {
val df = DecimalFormat("#.0")
var percent = 0.0
tryCatchInRelease {
percent = df.format((unzipProgress / unzipSize.toFloat()) * 100).toDouble()
}
downloadEntity.meta[XAPK_UNZIP_PERCENT] = percent.toString()
downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.UNZIPPING.name
DataChanger.notifyDataChanged(downloadEntity)
}
debugOnly {
Utils.log("unzip", "onProgress->" + (unzipProgress / unzipSize.toFloat()))
}
}
override fun onNext(downloadEntity: DownloadEntity, unzipPath: String) {
if (PACKAGE_EXTENSION_NAME == unzipPath.getExtension()) {
downloadEntity.meta[XAPK_PACKAGE_PATH_TAG] = unzipPath
DownloadManager.getInstance(mContext).updateDownloadEntity(downloadEntity)
}
debugOnly {
Utils.log("unzip", "onNext->$unzipPath")
}
}
/**
* 取消解压回调
*
* 取消后的表现与下载完成一致
*/
override fun onCancel(downloadEntity: DownloadEntity) {
mXapkUnzipThreadMap.remove(downloadEntity.path)
AppExecutor.uiExecutor.execute {
downloadEntity.meta[XAPK_UNZIP_PERCENT] = "0.0"
downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.CANCEL.name
DataChanger.notifyDataChanged(downloadEntity)
DownloadManager.getInstance(mContext).updateDownloadEntity(downloadEntity)
}
}
override fun onFailure(downloadEntity: DownloadEntity, exception: Exception) {
mXapkUnzipThreadMap.remove(downloadEntity.path)
AppExecutor.uiExecutor.execute {
downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.FAILURE.name
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
DataChanger.notifyDataChanged(downloadEntity)
DownloadManager.getInstance(mContext).updateDownloadEntity(downloadEntity)
}
MtaHelper.onEvent("解压失败"
, "安卓版本", Build.VERSION.RELEASE
, "IMEI", Util_System_Phone_State.getDeviceId(HaloApp.getInstance().application)
, "光环版本", BuildConfig.VERSION_NAME
, "厂商", Build.MANUFACTURER
, "机型", Build.MODEL
, "游戏", downloadEntity.name
, "网络状态", DeviceUtils.getNetwork(HaloApp.getInstance().application))
debugOnly {
Utils.log("unzip", "onFailure->$exception")
}
}
override fun onSuccess(downloadEntity: DownloadEntity) {
mXapkUnzipThreadMap.remove(downloadEntity.path)
AppExecutor.uiExecutor.execute {
val pkgPath = checkNotNull(downloadEntity.meta[XAPK_PACKAGE_PATH_TAG])
PackageInstaller.install(mContext, pkgPath)
downloadEntity.meta[XAPK_UNZIP_PERCENT] = "100.0"
downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.SUCCESS.name
DataChanger.notifyDataChanged(downloadEntity)
DownloadManager.getInstance(mContext).updateDownloadEntity(downloadEntity)
}
debugOnly {
Utils.log("unzip", "onSuccess->${downloadEntity.path}")
}
}
@JvmStatic
fun cancelUnzipTask(downloadEntity: DownloadEntity) {
val xapkUnzipThread = mXapkUnzipThreadMap[downloadEntity.path]
if (xapkUnzipThread != null) {
xapkUnzipThread.canceled = true
} else {
onCancel(downloadEntity) // 刷新页面
}
}
}
enum class XapkUnzipStatus(status: String) {
UNZIPPING("unzipping"),
SUCCESS("success"),
CANCEL("cancel"),
FAILURE("failure");
}

View File

@ -0,0 +1,211 @@
package com.gh.common.xapk
import android.os.Environment
import com.gh.common.util.getExtension
import com.gh.common.util.throwException
import com.gh.common.util.throwExceptionInDebug
import com.gh.gamecenter.BuildConfig
import com.halo.assistant.HaloApp
import com.lightgame.download.DownloadEntity
import com.lightgame.download.FileUtils
import com.lightgame.utils.Utils
import net.lingala.zip4j.progress.ProgressMonitor
import java.io.File
import java.util.zip.ZipFile
class XapkUnzipThread(private var mDownloadEntity: DownloadEntity,
private var mUnzipListener: IXapkUnzipListener) : Thread() {
private val mDefaultBufferSize = 1024 * 1024
var canceled = false
override fun run() {
super.run()
try {
val path = mDownloadEntity.path
var unzipSize = 0L
try {
unzipSize = getUnzipSize(path)
} catch (e: Exception) {
planB()
return
}
val msg = FileUtils.isCanDownload(HaloApp.getInstance().application, unzipSize)
if (!msg.isNullOrEmpty()) {
// 空间不足应该不用刷新页面,保持不变就好
Utils.toast(HaloApp.getInstance().application, "设备存储空间不足,请清理后重试!")
mUnzipListener.onCancel(mDownloadEntity)
return
}
var unzipProgress = 0L
val absolutePath = Environment.getExternalStorageDirectory().absolutePath
val xapkFile = File(path)
ZipFile(xapkFile).use { zip ->
for (zipEntry in zip.entries().asSequence()) {
val outputFile = if (zipEntry.name.getExtension() == XapkInstaller.XAPK_DATA_EXTENSION_NAME) {
File(absolutePath + File.separator + zipEntry.name)
} else if (zipEntry.name.getExtension() == XapkInstaller.PACKAGE_EXTENSION_NAME) {
// apk文件名称 = xapk文件名+本身的文件名称
val fileName = xapkFile.nameWithoutExtension + "_" + zipEntry.name
File(FileUtils.getDownloadPath(HaloApp.getInstance().application, fileName))
} else continue // 暂时只需要解压xpk/obb文件
if (zipEntry.isDirectory) {
if (!outputFile.exists()) {
throwException("unzip create file path failure", !outputFile.mkdirs())
}
continue
}
if (!outputFile.parentFile.exists()) {
throwException("unzip create file path failure", !outputFile.parentFile.mkdirs())
}
// 如果存在同名且大小一致,可以认为该文件已经解压缩(在不解压缩的情况下如果如果获取压缩文件的MD5????)
if (!outputFile.exists()) {
throwException("unzip create file failure", !outputFile.createNewFile())
} else if (outputFile.length() != zipEntry.size) {
throwException("unzip delete existing file failure", !outputFile.delete())
throwException("unzip create file failure", !outputFile.createNewFile())
} else {
unzipProgress += zipEntry.size
mUnzipListener.onProgress(mDownloadEntity, outputFile.path, unzipSize, unzipProgress)
mUnzipListener.onNext(mDownloadEntity, outputFile.path)
continue
}
// unzip
zip.getInputStream(zipEntry).use { input ->
outputFile.outputStream().use { output ->
val buffer = ByteArray(mDefaultBufferSize)
var bytes = input.read(buffer)
while (bytes >= 0) {
output.write(buffer, 0, bytes)
unzipProgress += bytes
bytes = input.read(buffer)
if (canceled) {
mUnzipListener.onCancel(mDownloadEntity)
return
} else {
mUnzipListener.onProgress(mDownloadEntity, outputFile.path, unzipSize, unzipProgress)
}
}
}
}
mUnzipListener.onNext(mDownloadEntity, outputFile.path)
}
}
mUnzipListener.onSuccess(mDownloadEntity)
} catch (e: Exception) {
if (BuildConfig.DEBUG) throw e
mUnzipListener.onFailure(mDownloadEntity, e)
}
}
/**
* 部分未知情况(有可能是项目配置问题有可能时Zip包破损)原生的ZipFile无法打开压缩包,则启动以下备用方案
* 具体复现机型:小米 9(Android 9)
* 具体复现的压缩包:太空竞速2 具体可以咨询陈思雨
*
* 实现方式请参考:https://github.com/srikanth-lingala/zip4j
* 注意使用该库的ZipInputStream依然无法解压,提示头文件丢失
*
* 后续如果有需要可以直接使用该方法进行解压
*/
private fun planB() {
val zipPath = mDownloadEntity.path
val absolutePath = Environment.getExternalStorageDirectory().absolutePath
val zipFile = net.lingala.zip4j.ZipFile(zipPath)
val progressMonitor = zipFile.progressMonitor
zipFile.isRunInThread = true
val fileHeaders = zipFile.fileHeaders
var unzipSize = 0L
for (fileHeader in fileHeaders) {
unzipSize += fileHeader.uncompressedSize;
}
var unzipProgress = 0L
var completedSize = 0L
for (fileHeader in fileHeaders) {
if (canceled) {
mUnzipListener.onCancel(mDownloadEntity)
return
}
// 暂时只需要解压xpk/obb文件
val extension = fileHeader.fileName.getExtension()
if (extension != XapkInstaller.XAPK_DATA_EXTENSION_NAME && extension != XapkInstaller.PACKAGE_EXTENSION_NAME) continue
var unzipPath = ""
if (extension == XapkInstaller.XAPK_DATA_EXTENSION_NAME) {
unzipPath = absolutePath + File.separator + fileHeader.fileName
if (hasUnzipFile(unzipPath, fileHeader.uncompressedSize)) {
mUnzipListener.onNext(mDownloadEntity, unzipPath)
continue
}
zipFile.extractFile(fileHeader.fileName, absolutePath)
}
if (extension == XapkInstaller.PACKAGE_EXTENSION_NAME) {
val downloadDir = FileUtils.getDownloadDir(HaloApp.getInstance().application)
val unzipFileName = File(zipPath).nameWithoutExtension + "_" + fileHeader.fileName
unzipPath = downloadDir + File.separator + unzipFileName
if (hasUnzipFile(unzipPath, fileHeader.uncompressedSize)) {
mUnzipListener.onNext(mDownloadEntity, unzipPath)
continue
}
zipFile.extractFile(fileHeader.fileName, downloadDir, unzipFileName)
}
throwExceptionInDebug("check unzipPath", unzipPath.isEmpty())
// 回调太频繁了,变态吗? 4K回调一次,还不能改,FUCK
var filterCounter = 0
val filterInterval = 1024 * 10
while (progressMonitor.state != ProgressMonitor.State.READY) {
filterCounter++
if (filterCounter % filterInterval == 0) {
unzipProgress = completedSize + progressMonitor.workCompleted
mUnzipListener.onProgress(mDownloadEntity, unzipPath, unzipSize, unzipProgress)
if (canceled) {
progressMonitor.isCancelAllTasks = true
mUnzipListener.onCancel(mDownloadEntity)
return
}
}
}
completedSize += fileHeader.uncompressedSize
mUnzipListener.onNext(mDownloadEntity, unzipPath)
}
mUnzipListener.onSuccess(mDownloadEntity)
}
private fun hasUnzipFile(unzipPath: String, uncompressedSize: Long): Boolean {
val unzipFile = File(unzipPath)
if (unzipFile.exists() || unzipFile.length() == uncompressedSize) return true
return false
}
private fun getUnzipSize(path: String): Long {
var totalSize = 0L
for (entry in ZipFile(File(path)).entries()) {
totalSize += entry.size
}
return totalSize
}
}

View File

@ -1,11 +1,21 @@
package com.gh.download
import android.content.pm.PackageManager
import com.gh.common.exposure.meta.MetaUtil.getMeta
import com.gh.common.loghub.LoghubUtils
import com.gh.common.util.DeviceUtils
import com.gh.gamecenter.BuildConfig
import com.halo.assistant.HaloApp
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus
import com.lightgame.utils.Utils
import org.json.JSONArray
import org.json.JSONObject
object DownloadDataHelper {
private const val DOWNLOAD_SPEED_TIME = "download_speed_time"
private const val DOWNLOAD_SPEED_SIZE = "download_speed_size"
const val DOWNLOAD_RESUME_WAY = "download_resume_way"
const val DOWNLOAD_RESUME_MANUAL = "manual"
const val DOWNLOAD_RESUME_AUTO = "auto"
@ -14,9 +24,13 @@ object DownloadDataHelper {
const val DOWNLOAD_CANCEL_MANUAL = "manual"
const val DOWNLOAD_CANCEL_AUTO = "auto"
const val DOWNLOAD_FIRST_START = "download_first_start"
private val mDownloadSpeedMap = HashMap<String, MutableList<Long>>()
@JvmStatic
fun getDownloadStatusAlias(downloadEntity: DownloadEntity): String {
val status = downloadEntity.status
fun getDownloadStatusAlias(downloadEntity: DownloadEntity, downloadStatus: DownloadStatus? = null): String {
val status = downloadStatus ?: downloadEntity.status
return if (status == DownloadStatus.add) {
"开始下载"
} else if (status == DownloadStatus.pause) {
@ -53,4 +67,179 @@ object DownloadDataHelper {
"未知状态"
}
}
@JvmStatic
fun uploadDownloadEvent(downloadEntity: DownloadEntity) {
if (downloadEntity.status != DownloadStatus.downloading) {
uploadDownloadStatusEvent(downloadEntity)
}
if (downloadEntity.status == DownloadStatus.downloading) {
val startupTime = downloadEntity.meta[DownloadEntity.DOWNLOAD_STARTUP_TIME_KEY]
if (startupTime != null) {
uploadDownloadStartupTimeEvent(downloadEntity, startupTime.toInt())
downloadEntity.meta.remove(DownloadEntity.DOWNLOAD_STARTUP_TIME_KEY)
DownloadManager.getInstance(HaloApp.getInstance().application).updateDownloadEntity(downloadEntity)
}
}
if (downloadEntity.status == DownloadStatus.downloading || downloadEntity.status == DownloadStatus.done) {
val time = downloadEntity.meta[DOWNLOAD_SPEED_TIME]
val size = downloadEntity.meta[DOWNLOAD_SPEED_SIZE]
if (downloadEntity.speed == 0L || time == null || size == null) {
downloadEntity.meta[DOWNLOAD_SPEED_TIME] = System.currentTimeMillis().toString()
downloadEntity.meta[DOWNLOAD_SPEED_SIZE] = downloadEntity.progress.toString()
DownloadManager.getInstance(HaloApp.getInstance().application).updateDownloadEntity(downloadEntity)
} else {
val offset = System.currentTimeMillis() - time.toLong()
if (offset > 5000) {
//记录并重置
val downloadedSize = downloadEntity.progress - size.toLong()
val averageSpeed = downloadedSize / offset
val speedList = mDownloadSpeedMap[downloadEntity.url]
if (speedList == null) {
mDownloadSpeedMap[downloadEntity.url] = arrayListOf(averageSpeed)
} else {
speedList.add(averageSpeed)
if (speedList.size >= 6 || downloadEntity.status == DownloadStatus.done) {
uploadDownloadAverageSpeed(downloadEntity, speedList)
if (downloadEntity.status == DownloadStatus.done) {
mDownloadSpeedMap.remove(downloadEntity.url)
} else {
speedList.clear()
}
}
}
downloadEntity.meta[DOWNLOAD_SPEED_TIME] = System.currentTimeMillis().toString()
downloadEntity.meta[DOWNLOAD_SPEED_SIZE] = downloadEntity.progress.toString()
DownloadManager.getInstance(HaloApp.getInstance().application).updateDownloadEntity(downloadEntity)
}
}
}
}
private fun uploadDownloadStartupTimeEvent(downloadEntity: DownloadEntity, startupTime: Int) {
val jsonObject = JSONObject()
try {
jsonObject.put("event", "下载线程启动")
val startupStatus = downloadEntity.meta[DownloadEntity.DOWNLOAD_STARTUP_STATUS_KEY]
if (startupStatus != null) {
jsonObject.put("msg", getDownloadStatusAlias(downloadEntity, DownloadStatus.valueOf(startupStatus)))
}
jsonObject.put("meta", getMetaJson())
jsonObject.put("timestamp", System.currentTimeMillis() / 1000)
// payload
val payloadObject = JSONObject()
payloadObject.put("game_id", downloadEntity.gameId)
payloadObject.put("gameName", downloadEntity.name)
payloadObject.put("platform", downloadEntity.platform)
payloadObject.put("package", downloadEntity.packageName)
payloadObject.put("filename", downloadEntity.path.substring(downloadEntity.path.lastIndexOf("/") + 1))
payloadObject.put("launch_ms", startupTime)
jsonObject.put("payload", payloadObject)
} catch (e: Exception) {
e.printStackTrace()
}
if (BuildConfig.DEBUG) {
Utils.log("LogUtils->$jsonObject")
}
LoghubUtils.log(jsonObject, "download_debug", false)
}
private fun uploadDownloadStatusEvent(downloadEntity: DownloadEntity) {
val jsonObject = JSONObject()
try {
jsonObject.put("event", getDownloadStatusAlias(downloadEntity))
jsonObject.put("msg", downloadEntity.error)
jsonObject.put("status", downloadEntity.status.status)
jsonObject.put("meta", getMetaJson())
jsonObject.put("timestamp", System.currentTimeMillis() / 1000)
// payload
val payloadObject = JSONObject()
payloadObject.put("game_id", downloadEntity.gameId)
payloadObject.put("gameName", downloadEntity.name)
payloadObject.put("platform", downloadEntity.platform)
payloadObject.put("package", downloadEntity.packageName)
payloadObject.put("filename", downloadEntity.path.substring(downloadEntity.path.lastIndexOf("/") + 1))
payloadObject.put("total_size", downloadEntity.size / 1024 / 1024)
payloadObject.put("completed_size", downloadEntity.progress / 1024 / 1024)
if (downloadEntity.status == DownloadStatus.resume) {
if (downloadEntity.meta[DOWNLOAD_FIRST_START] == "YES") {
payloadObject.put("is_first_start", true)
} else {
payloadObject.put("is_first_start", false)
}
}
if (downloadEntity.status == DownloadStatus.resume || downloadEntity.status == DownloadStatus.add) {
downloadEntity.meta[DOWNLOAD_FIRST_START] = "NO"
DownloadManager.getInstance(HaloApp.getInstance().application).updateDownloadEntity(downloadEntity)
}
jsonObject.put("payload", payloadObject)
} catch (e: Exception) {
e.printStackTrace()
}
if (BuildConfig.DEBUG) {
Utils.log("LogUtils->$jsonObject")
}
LoghubUtils.log(jsonObject, "download_debug", false)
}
private fun uploadDownloadAverageSpeed(downloadEntity: DownloadEntity, averageSpeedList: List<Long>) {
val jsonObject = JSONObject()
try {
jsonObject.put("event", "下载进度")
jsonObject.put("msg", "")
jsonObject.put("meta", getMetaJson())
jsonObject.put("timestamp", System.currentTimeMillis() / 1000)
// payload
val payloadObject = JSONObject()
payloadObject.put("game_id", downloadEntity.gameId)
payloadObject.put("gameName", downloadEntity.name)
payloadObject.put("platform", downloadEntity.platform)
payloadObject.put("package", downloadEntity.packageName)
payloadObject.put("filename", downloadEntity.path.substring(downloadEntity.path.lastIndexOf("/") + 1))
payloadObject.put("speed_progress", JSONArray(averageSpeedList))
payloadObject.put("is_finished", downloadEntity.status == DownloadStatus.done)
jsonObject.put("payload", payloadObject)
} catch (e: Exception) {
e.printStackTrace()
}
if (BuildConfig.DEBUG) {
Utils.log("LogUtils->$jsonObject")
}
LoghubUtils.log(jsonObject, "download_debug", false)
}
private fun getMetaJson(): JSONObject {
val context = HaloApp.getInstance().application
val meta = getMeta()
// meta
val metaObject = JSONObject()
metaObject.put("android_id", meta.android_id)
metaObject.put("android_sdk", meta.android_sdk)
metaObject.put("android_version", meta.android_version)
metaObject.put("appVersion", meta.appVersion)
metaObject.put("channel", meta.channel)
metaObject.put("gid", meta.gid)
metaObject.put("imei", meta.imei)
metaObject.put("mac", meta.mac)
metaObject.put("manufacturer", meta.manufacturer)
metaObject.put("model", meta.model)
metaObject.put("network", DeviceUtils.getNetwork(context))
metaObject.put("os", meta.os)
metaObject.put("MNC", DeviceUtils.getTelephonySubscriberId(context) ?: "")
metaObject.put("userId", meta.userId)
return metaObject
}
}

View File

@ -11,10 +11,9 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.collection.ArrayMap;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.ProcessLifecycleOwner;
import com.gh.common.AppExecutor;
import com.gh.common.constant.Constants;
import com.gh.common.exposure.ExposureEvent;
import com.gh.common.exposure.ExposureUtils;
import com.gh.common.history.HistoryHelper;
@ -22,10 +21,11 @@ import com.gh.common.util.AppDebugConfig;
import com.gh.common.util.DataCollectionUtils;
import com.gh.common.util.DeviceUtils;
import com.gh.common.util.DialogUtils;
import com.gh.common.util.ExtensionsKt;
import com.gh.common.util.GdtHelper;
import com.gh.common.util.HomePluggableHelper;
import com.gh.common.util.MD5Utils;
import com.gh.common.util.NetworkUtils;
import com.gh.common.util.PackageInstaller;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.SPUtils;
import com.gh.gamecenter.entity.ApkEntity;
@ -243,8 +243,7 @@ public class DownloadManager implements DownloadStatusListener {
DownloadEntity downloadEntity = new DownloadEntity();
downloadEntity.setUrl(apkEntity.getUrl());
downloadEntity.setName(gameEntity.getName());
downloadEntity.setPath(FileUtils.getDownloadPath(context,
MD5Utils.getContentMD5(gameEntity.getName() + "_" + System.currentTimeMillis()) + ".apk"));
downloadEntity.setPath(PackageInstaller.getDownloadPath(gameEntity.getName(), apkEntity.getFormat()));
downloadEntity.setETag(apkEntity.getEtag());
downloadEntity.setIcon(gameEntity.getIcon());
downloadEntity.setPlatform(apkEntity.getPlatform());
@ -253,6 +252,8 @@ public class DownloadManager implements DownloadStatusListener {
downloadEntity.setEntrance(entrance);
downloadEntity.setLocation(location);
downloadEntity.setVersionName(apkEntity.getVersion());
ExtensionsKt.addMetaExtra(downloadEntity, Constants.RAW_GAME_ICON, gameEntity.getRawIcon());
ExtensionsKt.addMetaExtra(downloadEntity, Constants.GAME_ICON_SUBSCRIPT, gameEntity.getIconSubscript());
int installed = 0;
for (ApkEntity apk : gameEntity.getApk()) {
if (PackagesManager.INSTANCE.isInstalled(apk.getPackageName())) {
@ -645,13 +646,11 @@ public class DownloadManager implements DownloadStatusListener {
DataChanger.INSTANCE.deleteObserver(dataWatcher);
}
private void startDownloadService() {
public void startDownloadService() {
Intent serviceIntent = new Intent(mContext, DownloadService.class);
// 当满足系统版本大于 8.0 并且进程处于 CREATED 状态 (ON_STOP后的状态) 的时候才以前台服务开启
// "not allowed to start service, app is in background" 的错误概率比 "startForegroundService() did not then call Service.startForeground() " 低
// 所以还是老老实实地以 startService 为主吧
// 当满足系统版本大于 8.0 并且应用在后台运行时以前台服务开启
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED) {
&& !PackageUtils.isAppOnForeground(mContext)) {
serviceIntent.putExtra(DownloadService.KEY_SERVICE_ACTION, DownloadService.START_FOREGROUND);
mContext.startForegroundService(serviceIntent);
} else {
@ -671,10 +670,16 @@ public class DownloadManager implements DownloadStatusListener {
downloadEntity.getMeta().put(DownloadEntity.NETWORK_STATUS_KEY, network);
}
if (status == DownloadStatus.add || status == DownloadStatus.subscribe) {
if (downloadEntity.getMeta().get(DownloadDataHelper.DOWNLOAD_FIRST_START) == null) {
downloadEntity.getMeta().put(DownloadDataHelper.DOWNLOAD_FIRST_START, "YES");
}
}
Intent serviceIntent = getIntent(downloadEntity, status);
// 当满足系统版本大于 8.0 应用在后台运行时以前台服务开启
// 当满足系统版本大于 8.0 并且应用在后台运行时以前台服务开启
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
&& ProcessLifecycleOwner.get().getLifecycle().getCurrentState() == Lifecycle.State.CREATED) {
&& !PackageUtils.isAppOnForeground(mContext)) {
serviceIntent.putExtra(DownloadService.KEY_SERVICE_ACTION, DownloadService.START_FOREGROUND);
mContext.startForegroundService(serviceIntent);
} else {

View File

@ -2,6 +2,7 @@ package com.gh.download
import androidx.work.PeriodicWorkRequest
import androidx.work.WorkManager
import com.gh.common.util.tryWithDefaultCatch
import java.util.concurrent.TimeUnit
object DownloadWorkManager {
@ -22,12 +23,13 @@ object DownloadWorkManager {
.addTag(TAG_DOWNLOAD)
.build()
WorkManager.getInstance().enqueue(workerRequest)
// TODO 某些特殊场景下会出现 WorkManagerImpl.getInstance() 为空的情况,下版本更新 WorkManager
tryWithDefaultCatch { WorkManager.getInstance().enqueue(workerRequest) }
}
@JvmStatic
fun cancelWorker() {
WorkManager.getInstance().cancelAllWorkByTag(TAG_DOWNLOAD)
tryWithDefaultCatch { WorkManager.getInstance().cancelAllWorkByTag(TAG_DOWNLOAD) }
}
}

View File

@ -1,41 +0,0 @@
package com.gh.download.cache;
public class CacheInfo {
public static final long TOTAL_ERROR = -1;//获取进度失败
private String url;
private long total;
private long progress;
private String fileName;
public CacheInfo(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public long getTotal() {
return total;
}
public void setTotal(long total) {
this.total = total;
}
public long getProgress() {
return progress;
}
public void setProgress(long progress) {
this.progress = progress;
}
}

View File

@ -1,241 +0,0 @@
package com.gh.download.cache;
import com.danikula.videocache.file.FileNameGenerator;
import com.danikula.videocache.file.Md5FileNameGenerator;
import com.gh.common.AppExecutor;
import com.halo.assistant.HaloApp;
import com.shuyu.gsyvideoplayer.utils.StorageUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicReference;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class CacheManager {
private static final AtomicReference<CacheManager> INSTANCE = new AtomicReference<>();
private volatile ConcurrentHashMap<String, Call> downCalls;
private OkHttpClient mClient;
private File cacheDirectory = StorageUtils.getIndividualCacheDirectory(HaloApp.getInstance().getApplication());
private FileNameGenerator generator = new Md5FileNameGenerator();
private final String TEMP_POSTFIX = ".download";
// private final int preLength = 5 * 1024 * 1024;//预加载大小
public static CacheManager getInstance() {
for (; ; ) {
CacheManager current = INSTANCE.get();
if (current != null) {
return current;
}
current = new CacheManager();
if (INSTANCE.compareAndSet(null, current)) {
return current;
}
}
}
private CacheManager() {
downCalls = new ConcurrentHashMap<>();
mClient = new OkHttpClient.Builder().build();
}
private synchronized ConcurrentHashMap<String, Call> getDownCalls() {
return downCalls;
}
public void download(String url, CacheObserver cacheObserver) {
//当前url已下载完成则不再下载
for (File file : getAllFile()) {
if (file.getName().equals(generator.generate(url))) {
return;
}
}
Observable.just(url)
.filter(s -> !getDownCalls().containsKey(s))//call的map已经有了,就证明正在下载,则这次不下载
.flatMap(s -> Observable.just(createDownInfo(s)))
.map(this::getRealFileName)
.flatMap(cacheInfo -> Observable.create(new DownloadSubscribe(cacheInfo)))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(cacheObserver);
}
public void cancel(String url) {
Call call = getDownCalls().get(url);
if (call != null) {
call.cancel();
}
getDownCalls().remove(url);
}
/**
* 创建DownInfo
*
* @param url 请求网址
* @return DownInfo
*/
private CacheInfo createDownInfo(String url) {
CacheInfo cacheInfo = new CacheInfo(url);
long contentLength = getContentLength(url);//获得文件大小
cacheInfo.setTotal(contentLength);
String fileName = generator.generate(url) + TEMP_POSTFIX;
cacheInfo.setFileName(fileName);
return cacheInfo;
}
private CacheInfo getRealFileName(CacheInfo cacheInfo) {
String fileName = cacheInfo.getFileName();
long downloadLength = 0;
if (!cacheDirectory.exists()) {
cacheDirectory.mkdir();
}
File file = new File(cacheDirectory, fileName);
if (file.exists()) {
//找到了文件,代表已经下载过,则获取其长度
downloadLength = file.length();
}
//设置改变过的文件名/大小
cacheInfo.setProgress(downloadLength);
cacheInfo.setFileName(file.getName());
return cacheInfo;
}
private class DownloadSubscribe implements ObservableOnSubscribe<CacheInfo> {
private CacheInfo cacheInfo;
public DownloadSubscribe(CacheInfo cacheInfo) {
this.cacheInfo = cacheInfo;
}
@Override
public void subscribe(ObservableEmitter<CacheInfo> e) throws Exception {
String url = cacheInfo.getUrl();
final long[] downloadLength = {cacheInfo.getProgress()};//已经下载好的长度
long contentLength = cacheInfo.getTotal();//文件的总长度
// if (downloadLength[0] >= preLength) {
if (downloadLength[0] >= contentLength) {
e.onComplete();
return;
}
e.onNext(cacheInfo);
Request request = new Request.Builder()
// .addHeader("RANGE", "bytes=" + downloadLength[0] + "-" + (contentLength > preLength ? preLength : contentLength))
.addHeader("RANGE", "bytes=" + downloadLength[0] + "-" + contentLength)
.url(url)
.build();
Call call = mClient.newCall(request);
getDownCalls().put(url, call);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
File file = new File(cacheDirectory, cacheInfo.getFileName());
InputStream is = null;
FileOutputStream fileOutputStream = null;
try {
is = response.body().byteStream();
fileOutputStream = new FileOutputStream(file, true);
byte[] buffer = new byte[2048];
int len;
while (downCalls.get(url) != null && !call.isCanceled() && (len = is.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
downloadLength[0] += len;
cacheInfo.setProgress(downloadLength[0]);
e.onNext(cacheInfo);
}
fileOutputStream.flush();
getDownCalls().remove(url);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (is != null) {
is.close();
}
if (fileOutputStream != null) {
fileOutputStream.close();
}
}
if (file.length() == contentLength) {
file.renameTo(new File(file.getPath().substring(0, file.getPath().lastIndexOf(TEMP_POSTFIX))));
}
e.onComplete();
}
});
}
}
/**
* 获取下载长度
*
* @param downloadUrl
* @return
*/
public long getContentLength(String downloadUrl) {
long contentLength = CacheInfo.TOTAL_ERROR;
Request request = new Request.Builder()
.url(downloadUrl)
.build();
Response response = null;
try {
response = mClient.newCall(request).execute();
if (response.isSuccessful() && response.body() != null) {
long length = response.body().contentLength();
contentLength = length == 0 ? CacheInfo.TOTAL_ERROR : length;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (response != null) {
response.close();
}
}
return contentLength;
}
public void removeAllCall() {
AppExecutor.getIoExecutor().execute(() -> {
for (Map.Entry<String, Call> entry : getDownCalls().entrySet()) {
entry.getValue().cancel();
}
AppExecutor.getUiExecutor().execute(() -> getDownCalls().clear());
});
}
private List<File> getAllFile() {
try {
if (cacheDirectory.exists() && cacheDirectory.isDirectory()) {
File[] files = cacheDirectory.listFiles();
return Arrays.asList(files);
}
} catch (Exception e) {
e.printStackTrace();
return new ArrayList<>();
}
return new ArrayList<>();
}
}

View File

@ -1,35 +0,0 @@
package com.gh.download.cache;
import com.gh.gamecenter.BuildConfig;
import com.lightgame.utils.Utils;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
public abstract class CacheObserver implements Observer<CacheInfo> {
protected Disposable d;//可以用于取消注册的监听者
protected CacheInfo cacheInfo;
@Override
public void onSubscribe(Disposable d) {
this.d = d;
}
@Override
public void onNext(CacheInfo cacheInfo) {
this.cacheInfo = cacheInfo;
if (BuildConfig.DEBUG) {
Utils.log(cacheInfo.getProgress() + "-" + cacheInfo.getTotal());
}
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
}
}

View File

@ -91,7 +91,7 @@ object ExoCacheManager {
}
}
private fun getContentLength(downloadUrl: String): Long {
fun getContentLength(downloadUrl: String): Long {
var contentLength = -1L
val request = Request.Builder()
.url(downloadUrl)

View File

@ -211,10 +211,10 @@ class DownloadDialogItemViewHolder(val binding: DownloadDialogItemBinding) : Bas
} else {
if (PackageUtils.isCanPluggable(apkEntity)) {
DialogUtils.showPluginDialog(it.context) {
it.context.startActivity(PackageUtils.getUninstallIntent(it.context, downloadEntity.path))
PackageInstaller.uninstall(it.context, downloadEntity.path)
}
} else {
PackageUtils.launchSetup(it.context, downloadEntity)
PackageInstaller.install(it.context, downloadEntity)
}
}

View File

@ -11,9 +11,10 @@ import com.gh.gamecenter.game.GameFragment
class BlockActivity : NormalActivity() {
companion object {
fun getIntent(context: Context, blockData: SubjectRecommendEntity): Intent {
fun getIntent(context: Context, blockData: SubjectRecommendEntity, entrance: String): Intent {
val args = Bundle()
args.putParcelable(EntranceUtils.KEY_BLOCK_DATA, blockData)
args.putString(EntranceUtils.KEY_ENTRANCE, entrance)
return getTargetIntent(context, BlockActivity::class.java, GameFragment::class.java, args)
}
}

View File

@ -72,12 +72,14 @@ public class CommentDetailActivity extends NormalActivity {
}
public static Intent getVideoCommentIntent(Context context,
String commentId,
String videoId,
LinkEntity linkEntity) {
String commentId,
String videoId,
boolean isVideoAuthor,
LinkEntity linkEntity) {
Bundle args = new Bundle();
args.putString(EntranceUtils.KEY_COMMENTID, commentId);
args.putString(CommentActivity.VIDEO_ID, videoId);
args.putBoolean(CommentActivity.IS_VIDEO_AUTHOR, isVideoAuthor);
args.putParcelable(EntranceUtils.KEY_LINK, linkEntity);
return getTargetIntent(context, CommentDetailActivity.class, NewCommentConversationFragment.class, args);
}

View File

@ -3,20 +3,22 @@ package com.gh.gamecenter;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.gh.base.ToolBarActivity;
import com.gh.common.util.BitmapUtils;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.EntranceUtils;
import com.gh.common.view.CropImageCustom;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.lang.ref.SoftReference;
import butterknife.BindView;
@ -25,17 +27,22 @@ public class CropImageActivity extends ToolBarActivity {
@BindView(R.id.cropimage_custom)
public CropImageCustom mCropImageCustom;
@BindView(R.id.status_bar)
public View mStatusBar;
public static final String RESULT_CLIP_PATH = "result_clip_path";
private SoftReference<Bitmap> reference;
protected boolean mBlackTheme = false;
@NonNull
public static Intent getIntent(Context context, String picturePath, float cropRatio, String entrance) {
Intent intent = new Intent(context, CropImageActivity.class);
intent.putExtra(EntranceUtils.KEY_PATH, picturePath);
intent.putExtra(EntranceUtils.KEY_ENTRANCE, entrance);
intent.putExtra(EntranceUtils.KEY_IMAGE_CROP_RATIO, cropRatio);
intent.putExtra(EntranceUtils.KEY_BLACK_THEME, true);
return intent;
}
@ -46,28 +53,35 @@ public class CropImageActivity extends ToolBarActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
mBlackTheme = getIntent().getBooleanExtra(EntranceUtils.KEY_BLACK_THEME, false);
super.onCreate(savedInstanceState);
mTitleTv.setTextColor(mBlackTheme ? Color.WHITE : Color.BLACK);
mToolbar.setBackgroundColor(getResources().getColor(mBlackTheme ? R.color.text_28282E : R.color.white));
mStatusBar.setBackgroundColor(getResources().getColor(mBlackTheme ? R.color.text_28282E : R.color.white));
setNavigationTitle(getString(R.string.title_crop_image));
setToolbarMenu(R.menu.menu_positive);
MenuItem menuItem = getMenuItem(R.id.layout_menu_positive);
TextView menuButton = menuItem.getActionView().findViewById(R.id.menu_answer_post);
menuButton.setTextColor(getResources().getColor(mBlackTheme ? R.color.theme_font : R.color.title));
float ratio = getIntent().getFloatExtra(EntranceUtils.KEY_IMAGE_CROP_RATIO, 1F);
mCropImageCustom.setCropRatio(ratio);
DisplayUtils.transparentStatusBar(this);
}
@Override
public int provideNavigationIcon() {
return mBlackTheme ? R.drawable.ic_toolbar_back_white : R.drawable.ic_bar_back;
}
@Override
public boolean onMenuItemClick(MenuItem item) {
if (item.getItemId() == R.id.layout_menu_positive) {
Intent data = new Intent();
Bitmap clip = mCropImageCustom.clip();
String clipPath = getCacheDir().getAbsolutePath() + File.separator + System.currentTimeMillis() + ".jpg";
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(clipPath)));
clip.compress(Bitmap.CompressFormat.WEBP, 100, bos);
bos.flush();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
mCropImageCustom.savePicture(clipPath);
data.putExtra(RESULT_CLIP_PATH, clipPath);
setResult(RESULT_OK, data);

View File

@ -0,0 +1,638 @@
package com.gh.gamecenter
import android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.app.Dialog
import android.content.Context
import android.content.Intent
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.ViewTreeObserver.OnGlobalLayoutListener
import android.view.Window
import android.view.animation.DecelerateInterpolator
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.viewpager.widget.PagerAdapter
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
import butterknife.BindView
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView.DefaultOnImageEventListener
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.Base64ImageHolder.image
import com.gh.common.runOnIoThread
import com.gh.common.util.*
import com.gh.common.util.ImageUtils.getTransformLimitUrl
import com.gh.common.view.DraggableBigImageView
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 io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.io.*
import java.util.*
/**
* 查看游戏截图页面
*
* @author 黄壮华
*
* todo BigImageView静态webp/动态webp(ImageInfoExtractor.getImageType(image))判断有问题,导致部分静态webp无法使用缩放功能
*/
class ImageViewerActivity : BaseActivity(), OnPageChangeListener {
@BindView(R.id.image_detail_page)
lateinit var mViewPager: Gh_ViewPager
@BindView(R.id.image_detail_progress)
lateinit var mProgressHint: TextView
@BindView(R.id.image_mask)
lateinit var mIndicatorMask: View
@BindView(R.id.image_indicator_tv)
lateinit var mIndicatorTv: TextView
@BindView(R.id.background_view)
lateinit var mBackgroundView: View
private var adapter: ViewImageAdapter? = null
private var mImagePipeline: ImagePipeline? = null
private var mShowBase64Image = false
private var mUrlList: ArrayList<String>? = null
private var mViewedSet: HashSet<Int>? = null // 让调用者知道该图片是否被看过了
private var mImageInfoMap: MutableMap<String, ImageInfoEntity>? = null
private var mBigImageView: BigImageView? = null
private var mInitialPosition = 0
private var mUseEnterAndExitAnimation = false
private var mLimitWidth = 0
private var mOriginLeft = 0
private var mOriginTop = 0
private var mOriginHeight = 0
private var mOriginWidth = 0
private var mOriginCenterX = 0
private var mOriginCenterY = 0
private var mOriginHeightWidthRatio = 0f
private var mTargetHeight = 0f
private var mTargetWidth = 0f
private var mScaleX = 0f
private var mScaleY = 0f
private var mTranslationX = 0f
private var mTranslationY = 0f
override fun getLayoutId() = R.layout.activity_viewimage
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mViewedSet = HashSet()
mImageInfoMap = HashMap()
mImagePipeline = Fresco.getImagePipeline()
mInitialPosition = savedInstanceState?.getInt(EntranceUtils.KEY_CURRENTITEM, 0) ?: 0
intent.extras?.let {
if (it.getBoolean(KEY_BASE64)) {
mShowBase64Image = true
mUrlList = ArrayList()
mUrlList?.add(image)
} else {
mUrlList = it.getStringArrayList(KEY_URL_LIST)
mInitialPosition = it.getInt(KEY_CURRENT, 0)
}
mUseEnterAndExitAnimation = it.getBoolean(KEY_USE_ENTER_AND_EXIT_ANIMATION)
}
mIndicatorMask.goneIf(mUrlList?.size == 1)
mIndicatorTv.text = String.format("%d/%d", mInitialPosition + 1, mUrlList!!.size)
// init slide
val outMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(outMetrics)
val widthPixels = outMetrics.widthPixels
mLimitWidth = if (NetworkUtils.isWifiOr4GConnected(this)) {
widthPixels * 2
} else {
widthPixels
}
// init viewPage
adapter = ViewImageAdapter()
mViewPager.adapter = adapter
mViewPager.currentItem = mInitialPosition
mViewPager.addOnPageChangeListener(this)
mProgressHint.setOnClickListener {
val position = mViewPager.currentItem
val `object`: Any? = mViewPager.findViewWithTag(position)
if (`object` != null) {
mProgressHint.width = mProgressHint.width
val view = `object` as RelativeLayout
val imageView: BigImageView = view.findViewById(R.id.viewimage_iv_show)
val url = mUrlList!![position]
imageView.showImage(Uri.parse(url))
imageView.setImageLoaderCallback(object : SimpleImageLoader() {
@SuppressLint("SetTextI18n")
override fun onProgress(progress: Int) {
if (position == mViewPager.currentItem) { // 防止下载过程中切换图片
if (progress < 100) {
mProgressHint.text = "$progress%"
} else {
mProgressHint.text = "已完成"
mBaseHandler.postDelayed({
if (position == mViewPager.currentItem) { // 防止等待过程中切换图片
mProgressHint.visibility = View.GONE
}
}, 500)
}
}
}
})
}
}
initEnterAnimation()
DisplayUtils.transparentStatusAndNavigation(this)
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putInt(EntranceUtils.KEY_CURRENTITEM, mViewPager.currentItem)
}
override fun onDestroy() {
super.onDestroy()
if (mShowBase64Image) {
mUrlList!!.clear()
image = ""
}
mViewPager.onDestroy() // 注销EventBus
}
@SuppressLint("SetTextI18n")
override fun onPageScrolled(position: Int, positionOffset: Float,
positionOffsetPixels: Int) {
if (positionOffset != 0f) {
mProgressHint.visibility = View.GONE
} else {
val url = mUrlList!![position]
val imageInfoEntity = mImageInfoMap!![url]
if (imageInfoEntity?.fileSize != null &&
!mImagePipeline!!.isInBitmapMemoryCache(ImageRequest.fromUri(url)) &&
!mImagePipeline!!.isInDiskCacheSync(ImageRequest.fromUri(url))) {
val size = String.format(Locale.CHINA, "%.1fM",
Integer.valueOf(imageInfoEntity.fileSize.value) / 1024f / 1024f)
mProgressHint.visibility = View.VISIBLE
mProgressHint.text = "查看原图($size)"
val layoutParams = mProgressHint.layoutParams
layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT
mProgressHint.layoutParams = layoutParams
}
}
mViewedSet?.add(position)
setResult(Activity.RESULT_OK, Intent().putExtra(VIEWED_IMAGE, mViewedSet))
}
override fun onPageSelected(position: Int) {
var ghRelativeLayout: Gh_RelativeLayout?
for (i in 0 until mViewPager.childCount) {
if (mViewPager.getChildAt(i).tag != null) {
ghRelativeLayout = mViewPager.getChildAt(i) as? Gh_RelativeLayout
if (ghRelativeLayout == null) {
return
}
val imageView: BigImageView = ghRelativeLayout.findViewById(R.id.viewimage_iv_show)
val ssiv = imageView.ssiv
ssiv?.resetScaleAndCenter()
}
}
mIndicatorTv.text = String.format("%d/%d", position + 1, mUrlList!!.size)
}
override fun onPageScrollStateChanged(newState: Int) {}
private fun initEnterAnimation() {
mViewPager.viewTreeObserver
.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
override fun onGlobalLayout() {
mViewPager.viewTreeObserver.removeOnGlobalLayoutListener(this)
mOriginLeft = intent.getIntExtra(KEY_LEFT, 0)
mOriginTop = intent.getIntExtra(KEY_TOP, 0)
mOriginHeight = intent.getIntExtra(KEY_HEIGHT, 0)
mOriginWidth = intent.getIntExtra(KEY_WIDTH, 0)
mOriginCenterX = mOriginLeft + mOriginWidth / 2
mOriginCenterY = mOriginTop + mOriginHeight / 2
mOriginHeightWidthRatio = mOriginHeight.toFloat() / mOriginWidth
val location = IntArray(2)
mBigImageView?.getLocationOnScreen(location)
mTargetHeight = mBigImageView?.height?.toFloat() ?: 0F
mTargetWidth = mBigImageView?.width?.toFloat() ?: 0F
mScaleX = mOriginWidth.toFloat() / mTargetWidth
// TODO 非等比例缩放的图片动画不正常
mScaleY = mScaleX
val targetCenterX = location[0] + mTargetWidth / 2
val targetCenterY = location[1] + mTargetHeight / 2
mTranslationX = mOriginCenterX - targetCenterX
mTranslationY = mOriginCenterY - targetCenterY
if (mUseEnterAndExitAnimation) {
mBigImageView?.translationX = mTranslationX
mBigImageView?.translationY = mTranslationY
mBigImageView?.scaleX = mScaleX
mBigImageView?.scaleY = mScaleY
}
performEnterAnimation()
}
})
}
private fun loadImageInfo(position: Int, width: Int) {
val url = mUrlList!![position]
RetrofitManager.getInstance(this)
.api.getImageInfo("$url?x-oss-process=image/info")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ImageInfoEntity?>() {
override fun onResponse(response: ImageInfoEntity?) {
if (response?.imageWidth != null && Integer.valueOf(response.imageWidth.value) > width) {
mImageInfoMap!![url] = response
if (position == mViewPager.currentItem) {
onPageScrolled(position, 0f, 0) // 刷新下载原图提示按钮
}
}
}
})
}
private fun loadImage(originalUrl: String, decoratedUrl: String?, imageView: BigImageView) {
if (TextUtils.isEmpty(originalUrl)) return
if (originalUrl.startsWith("data:image/png;base64")) {
runOnIoThread {
val base64String = originalUrl.replace("data:image/png;base64", "")
try {
val imageFile = File(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()
runOnUiThread {
imageView.setImageViewFactory(FrescoImageViewFactory())
imageView.showImage(Uri.fromFile(imageFile))
}
} catch (e: Exception) {
e.printStackTrace()
}
}
} else {
// 添加GIF支持
imageView.setImageViewFactory(FrescoImageViewFactory())
imageView.setThumbnailScaleType(ImageView.ScaleType.FIT_CENTER)
imageView.showImage(Uri.parse(originalUrl), Uri.parse(decoratedUrl))
}
}
private fun finishWithAnimation(fadeOnly: Boolean) {
val animatorSet = AnimatorSet()
val translateXAnimator = ValueAnimator.ofFloat(0F, mTranslationX).apply {
addUpdateListener { va -> mBigImageView?.x = (va.animatedValue as Float) }
}
val translateYAnimator = ValueAnimator.ofFloat(0F, mTranslationY).apply {
addUpdateListener { va -> mBigImageView?.y = (va.animatedValue as Float) }
}
val scaleYAnimator = ValueAnimator.ofFloat(1F, mScaleY).apply {
addUpdateListener { va -> mBigImageView?.scaleY = (va.animatedValue as Float) }
}
val scaleXAnimator = ValueAnimator.ofFloat(1F, mScaleX).apply {
addUpdateListener { va -> mBigImageView?.scaleX = (va.animatedValue as Float) }
}
val backgroundAlphaAnimation = ValueAnimator.ofFloat(1F, 0F).apply {
addUpdateListener { va -> mBackgroundView.alpha = (va.animatedValue as Float) }
}
val alphaAnimator = ValueAnimator.ofFloat(1F, 0F).apply {
addUpdateListener { va -> mViewPager.alpha = (va.animatedValue as Float) }
}
animatorSet.apply {
if (mUseEnterAndExitAnimation && !fadeOnly) {
playTogether(translateXAnimator, translateYAnimator, scaleXAnimator, scaleYAnimator, backgroundAlphaAnimation)
} else {
playTogether(alphaAnimator, backgroundAlphaAnimation)
}
duration = ANIMATION_DURATION
doOnStart { mIndicatorMask.visibility = View.GONE }
doOnEnd {
it.removeAllListeners()
finish()
overridePendingTransition(0, 0)
}
}.start()
}
private fun performEnterAnimation() {
val animatorSet = AnimatorSet()
val translateXAnimator = ValueAnimator.ofFloat(mBigImageView!!.x, 0F).apply {
addUpdateListener { va -> mBigImageView?.x = (va.animatedValue as Float) }
}
val translateYAnimator = ValueAnimator.ofFloat(mBigImageView!!.y, 0F).apply {
addUpdateListener { va -> mBigImageView?.y = (va.animatedValue as Float) }
}
val scaleYAnimator = ValueAnimator.ofFloat(mScaleY, 1F).apply {
addUpdateListener { va -> mBigImageView?.scaleY = (va.animatedValue as Float) }
}
val scaleXAnimator = ValueAnimator.ofFloat(mScaleX, 1F).apply {
addUpdateListener { va -> mBigImageView?.scaleX = (va.animatedValue as Float) }
}
val backgroundAlphaAnimator = ValueAnimator.ofFloat(0F, 1F).apply {
addUpdateListener { va -> mBackgroundView.alpha = (va.animatedValue as Float) }
}
animatorSet.apply {
if (mUseEnterAndExitAnimation) {
playTogether(translateXAnimator, translateYAnimator, scaleXAnimator, scaleYAnimator, backgroundAlphaAnimator)
} else {
playTogether(backgroundAlphaAnimator)
}
duration = ANIMATION_DURATION
doOnStart { mIndicatorMask.visibility = View.GONE }
doOnEnd {
if (mUrlList?.size != 1) {
mIndicatorMask.visibility = View.VISIBLE
}
}
}.start()
}
private fun performExitAnimation(view: DraggableBigImageView, scale: Float, fadeOnly: Boolean) {
val finalScale = mOriginWidth / mTargetWidth
val finalTranslationX = mOriginLeft - (1 - finalScale) * mTargetWidth / 2
val finalTranslationY = mOriginTop - ((1 - finalScale) * mTargetHeight + (mTargetHeight * finalScale - mOriginHeight)) / 2
val animatorSet = AnimatorSet()
val scaleAnimator = ValueAnimator.ofFloat(scale, finalScale).apply {
addUpdateListener { va ->
view.scaleX = (va.animatedValue as Float)
view.scaleY = (va.animatedValue as Float)
}
}
val translateXAnimator = ValueAnimator.ofFloat(view.x, finalTranslationX).apply {
addUpdateListener { va -> view.translationX = (va.animatedValue as Float) }
}
val translateYAnimator = ValueAnimator.ofFloat(view.y, finalTranslationY).apply {
addUpdateListener { va -> view.translationY = (va.animatedValue as Float) }
}
val backgroundAlphaAnimator = ValueAnimator.ofFloat(mBackgroundView.alpha, 0F).apply {
addUpdateListener { va -> mBackgroundView.alpha = (va.animatedValue as Float) }
}
val alphaAnimator = ValueAnimator.ofFloat(1F, 0F).apply {
addUpdateListener { va -> view.alpha = (va.animatedValue as Float) }
}
animatorSet.apply {
if (mUseEnterAndExitAnimation && !fadeOnly) {
playTogether(scaleAnimator, translateXAnimator, translateYAnimator, backgroundAlphaAnimator)
} else {
playTogether(backgroundAlphaAnimator, alphaAnimator)
}
doOnEnd {
it.removeAllListeners()
finish()
overridePendingTransition(0, 0)
}
interpolator = DecelerateInterpolator()
duration = ANIMATION_DURATION
}.start()
}
private inner class ViewImageAdapter : PagerAdapter() {
override fun getCount() = mUrlList?.size ?: 0
@SuppressLint("MissingPermission")
override fun instantiateItem(container: ViewGroup, position: Int): Any {
val originalUrl = mUrlList!![position]
var decoratedUrl: String? = originalUrl
val imageRequest = ImageRequest.fromUri(originalUrl)
val isInMemoryCache = mImagePipeline!!.isInBitmapMemoryCache(imageRequest)
val isInDiskCache = imageRequest != null && mImagePipeline!!.isInDiskCacheSync(imageRequest)
val view = View.inflate(container.context, R.layout.viewimage_normal_item, null) as Gh_RelativeLayout
val imageView: DraggableBigImageView = view.findViewById(R.id.viewimage_iv_show)
if (mBigImageView == null) {
mBigImageView = imageView
}
imageView.setDragListener(object : DraggableBigImageView.DragListener {
override fun onRelease(draggableBigImageView: DraggableBigImageView, scale: Float) {
performExitAnimation(draggableBigImageView, scale, mViewPager.currentItem != mInitialPosition)
}
override fun onDrag(draggableBigImageView: DraggableBigImageView, fraction: Float) {
mBackgroundView.alpha = 1 - fraction
mIndicatorMask.visibility = View.GONE
}
override fun onRestore(draggableBigImageView: DraggableBigImageView, fraction: Float) {
mBackgroundView.alpha = 1F
mIndicatorMask.goneIf(mUrlList?.size == 1)
}
})
if (!isInMemoryCache
&& !isInDiskCache
&& !NetworkUtils.isWifiOr4GConnected(this@ImageViewerActivity)
&& !originalUrl.contains(".gif")) {
decoratedUrl = getTransformLimitUrl(originalUrl, mLimitWidth, applicationContext)
}
val finalUrl = decoratedUrl
imageView.setImageLoaderCallback(object : SimpleImageLoader() {
override fun onSuccess(image: File) {
if (finalUrl != mUrlList!![position]) {
val options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(File(image.path).absolutePath, options)
loadImageInfo(position, options.outWidth) // 加载图片参数,目的是用户显示原文按钮
}
val ssiv = imageView.ssiv
if (ssiv != null) {
ssiv.maxScale = 10f // 这个缩放倍数最好很具宽高自动调节
ssiv.setOnImageEventListener(object : DefaultOnImageEventListener() {
override fun onReady() {
ssiv.resetScaleAndCenter()
}
})
ssiv.setOnClickListener { finishWithAnimation(mInitialPosition != mViewPager.currentItem) }
}
}
})
loadImage(originalUrl, decoratedUrl, imageView)
//长按
imageView.setOnLongClickListener {
// 下滑的时候不弹
if (imageView.isDragging()) {
return@setOnLongClickListener false
}
val dialog = Dialog(this@ImageViewerActivity)
val container1 = LinearLayout(this@ImageViewerActivity)
container1.orientation = LinearLayout.VERTICAL
container1.setBackgroundColor(Color.WHITE)
val reportTv = TextView(this@ImageViewerActivity)
reportTv.setPadding(
DisplayUtils.dip2px(this@ImageViewerActivity, 20f),
DisplayUtils.dip2px(this@ImageViewerActivity, 12f),
0,
DisplayUtils.dip2px(this@ImageViewerActivity, 12f))
reportTv.setText(R.string.save_pic)
reportTv.textSize = 17f
reportTv.setTextColor(ContextCompat.getColor(applicationContext, R.color.title))
reportTv.setBackgroundResource(R.drawable.textview_white_style)
val widthPixels = resources.displayMetrics.widthPixels
reportTv.layoutParams = 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 {
checkStoragePermissionBeforeAction {
saveImageToFile(imageView.currentImageFile, finalUrl)
dialog.cancel()
}
dialog.cancel()
}
false
}
view.tag = position
container.addView(view)
return view
}
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) {
}
}
}
override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
container.removeView(`object` as View)
}
override fun isViewFromObject(view: View, `object`: Any): Boolean {
return view === `object`
}
}
override fun onBackPressed() {
finishWithAnimation(mInitialPosition != mViewPager.currentItem)
}
companion object {
const val REQUEST_FOR_VIEWED_IMAGE = 921
const val VIEWED_IMAGE = "viewed_image"
private const val KEY_BASE64 = "base64"
private const val KEY_URL_LIST = "urls"
private const val KEY_CURRENT = "current"
private const val KEY_USE_ENTER_AND_EXIT_ANIMATION = "use_enter_and_exit_animation"
private const val KEY_LEFT = "left"
private const val KEY_TOP = "top"
private const val KEY_HEIGHT = "height"
private const val KEY_WIDTH = "width"
private const val ANIMATION_DURATION = 350L
@JvmStatic
fun getBase64Intent(context: Context?, showSingleBase64Image: Boolean): Intent {
val checkIntent = Intent(context, ImageViewerActivity::class.java)
checkIntent.putExtra(KEY_BASE64, showSingleBase64Image)
return checkIntent
}
@JvmStatic
fun getIntent(context: Context, list: ArrayList<String>, position: Int = 0, entrance: String?): Intent {
return getIntent(context, list, position, null, entrance)
}
/**
* 传入 view 代表使用渐入渐出动画
*/
@JvmStatic
fun getIntent(context: Context, list: ArrayList<String>, position: Int = 0, originalView: View? = null, entrance: String?): Intent {
val intent = Intent(context, ImageViewerActivity::class.java)
intent.putExtra(KEY_URL_LIST, list)
intent.putExtra(KEY_CURRENT, position)
intent.putExtra(EntranceUtils.KEY_ENTRANCE, entrance)
val location = IntArray(2)
if (originalView != null) {
originalView.getLocationOnScreen(location)
intent.putExtra(KEY_LEFT, location[0])
intent.putExtra(KEY_TOP, location[1])
intent.putExtra(KEY_HEIGHT, originalView.height)
intent.putExtra(KEY_WIDTH, originalView.width)
intent.putExtra(KEY_USE_ENTER_AND_EXIT_ANIMATION, true)
}
if (context is Activity) {
context.overridePendingTransition(0, 0)
}
return intent
}
}
}

View File

@ -24,6 +24,7 @@ import com.gh.common.util.ApkActiveUtils;
import com.gh.common.util.DetailDownloadUtils;
import com.gh.common.util.DeviceTokenUtils;
import com.gh.common.util.EntranceUtils;
import com.gh.common.util.ExtensionsKt;
import com.gh.common.util.LibaoUtils;
import com.gh.common.view.DownloadProgressBar;
import com.gh.common.view.VerticalItemDecoration;
@ -375,6 +376,12 @@ public class LibaoDetailActivity extends ToolBarActivity implements LibaoDetailA
mGameEntity = response;
mAdapter.setGameEntity(mGameEntity);
// 添加启动弹窗的相关信息
if (mEntrance.contains(EntranceUtils.ENTRANCE_WELCOME)
&& ExtensionsKt.countOccurrences(mEntrance,("+")) <= 1) {
mGameEntity.setWelcomeDialogInfoIfAvailable();
}
DetailDownloadUtils.detailInitDownload(getDetailViewHolder(), true);
mAdapter.addLibaoDetail(LibaoDetailActivity.this, true);

View File

@ -53,6 +53,7 @@ import com.gh.common.util.LogUtils;
import com.gh.common.util.LunchType;
import com.gh.common.util.MtaHelper;
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.PushHelper;
@ -217,8 +218,7 @@ public class MainActivity extends BaseActivity {
// checkTinkerPath(); // 看情况是否需要显示补丁弹窗
// 必须放在这里,否则会导致获取 baseActivity 不是本应用包名
// postDelayed 是为了让 ProcessLifecycleOwner 知道准确状态,避免调用 startForegroundService
handler.postDelayed(() -> DownloadManager.getInstance(this).initDownloadService(), 0);
DownloadManager.getInstance(this).initDownloadService();
checkNotificationPermission();
@ -444,7 +444,7 @@ public class MainActivity extends BaseActivity {
"提示", msg,
"直接退出",
"立即安装",
() -> handler.postDelayed(() -> PackageUtils.launchSetup(MainActivity.this, finalDownloadEntity), 200),
() -> handler.postDelayed(() -> PackageInstaller.install(MainActivity.this, finalDownloadEntity), 200),
this::finish);
return true;
}
@ -772,7 +772,7 @@ public class MainActivity extends BaseActivity {
// "操作", "卸载完成",
// "网络状态", DeviceUtils.getNetwork(HaloApp.getInstance().getApplication()));
startActivity(PackageUtils.getInstallIntent(this, mDownloadEntity.getPath()));
PackageInstaller.install(this, mDownloadEntity);
}
// 更新已安装游戏

View File

@ -17,11 +17,11 @@ import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.ScrollView
import android.widget.TextView
import com.gh.base.BaseActivity
import com.gh.base.ToolBarActivity
import com.gh.common.util.CommentUtils
import com.gh.common.util.DeviceUtils
import com.gh.common.util.DialogUtils
import com.gh.common.util.tryCatchInRelease
import com.gh.gamecenter.retrofit.Response
import io.reactivex.Observable
import io.reactivex.ObservableOnSubscribe
@ -67,85 +67,85 @@ class NetworkDiagnosisActivity : ToolBarActivity() {
Observable.create(ObservableOnSubscribe<ProgressAndDetail> {
var progress = 0
var progress = 0
// 1.获取当前网络情况network/ip/mac/sim
// 1.获取当前网络情况network/ip/mac/sim
// Network 2G/3G/4G/WIFI
builder.append("Network:")
builder.append(DeviceUtils.getNetwork(applicationContext))
// Network 2G/3G/4G/WIFI
builder.append("Network:")
builder.append(DeviceUtils.getNetwork(applicationContext))
builder.append("\n")
progress += 3
it.onNext(ProgressAndDetail(progress, builder.toString()))
// IP
builder.append("IP:")
builder.append(DeviceUtils.getIPAddress(applicationContext))
builder.append("\n")
progress += 3
it.onNext(ProgressAndDetail(progress, builder.toString()))
// MAC
builder.append("MAC:")
builder.append(DeviceUtils.getMac(applicationContext))
builder.append("\n")
progress += 3
it.onNext(ProgressAndDetail(progress, builder.toString()))
// SIM
builder.append("SIM:")
builder.append(DeviceUtils.getSim(applicationContext))
builder.append("\n")
progress += 3
it.onNext(ProgressAndDetail(progress, builder.toString()))
builder.append("-----------------------------------------------------------------------\n")
// 2.ping域名
val domains = arrayOf("api.ghzs.com", "download.ghzs.com", "apk.ghzs666.com", "image.ghzs666.com", "image.ghzhushou.com")
for (domain in domains) {
builder.append(DeviceUtils.ping(domain))
builder.append("-----------------------------------------------------------------------\n")
progress += 7
it.onNext(ProgressAndDetail(progress, builder.toString()))
}
// 3.okhttp访问链接
val urls = arrayOf("https://api.ghzs.com/v3d3/index/columns", "https://download.ghzs.com/game?id=55097638fc1a6fa45f8b4568&platform=9u", "https://apk.ghzs666.com/packed/5af00abc02b30f7c038b456c.apk", "http://image.ghzs666.com/pic/5b29b3c92924bcaf5d438d38.jpg", "http://image.ghzhushou.com/pic/586cad378ab49e0f1b91b3e8.png")
for (url in urls) {
builder.append("Url:")
builder.append(url)
builder.append("\n")
val client = OkHttpClient()
val request = Request.Builder()
.head()
.url(url)
.build()
try {
val response = client.newCall(request).execute()
builder.append("Success:\n")
builder.append("Response:\n")
builder.append(response.toString())
builder.append("\n")
progress += 3
it.onNext(ProgressAndDetail(progress, builder.toString()))
// IP
builder.append("IP:")
builder.append(DeviceUtils.getIPAddress(applicationContext))
builder.append("Response Header:\n")
builder.append(response.headers().toString())
builder.append("\n")
} catch (e: IOException) {
builder.append("Error:\n")
builder.append(Log.getStackTraceString(e))
}
builder.append("-----------------------------------------------------------------------\n")
progress += 7
it.onNext(ProgressAndDetail(progress, builder.toString()))
}
progress += 3
it.onNext(ProgressAndDetail(progress, builder.toString()))
// MAC
builder.append("MAC:")
builder.append(DeviceUtils.getMac(applicationContext))
builder.append("\n")
progress += 3
it.onNext(ProgressAndDetail(progress, builder.toString()))
// SIM
builder.append("SIM:")
builder.append(DeviceUtils.getSim(applicationContext))
builder.append("\n")
progress += 3
it.onNext(ProgressAndDetail(progress, builder.toString()))
builder.append("-----------------------------------------------------------------------\n")
// 2.ping域名
val domains = arrayOf("api.ghzs.com", "download.ghzs.com", "apk.ghzs666.com", "image.ghzs666.com", "image.ghzhushou.com")
for (domain in domains) {
builder.append(DeviceUtils.ping(domain))
builder.append("-----------------------------------------------------------------------\n")
progress += 7
it.onNext(ProgressAndDetail(progress, builder.toString()))
}
// 3.okhttp访问链接
val urls = arrayOf("https://api.ghzs.com/v3d3/index/columns", "https://download.ghzs.com/game?id=55097638fc1a6fa45f8b4568&platform=9u", "https://apk.ghzs666.com/packed/5af00abc02b30f7c038b456c.apk", "http://image.ghzs666.com/pic/5b29b3c92924bcaf5d438d38.jpg", "http://image.ghzhushou.com/pic/586cad378ab49e0f1b91b3e8.png")
for (url in urls) {
builder.append("Url:")
builder.append(url)
builder.append("\n")
val client = OkHttpClient()
val request = Request.Builder()
.head()
.url(url)
.build()
try {
val response = client.newCall(request).execute()
builder.append("Success:\n")
builder.append("Response:\n")
builder.append(response.toString())
builder.append("\n")
builder.append("Response Header:\n")
builder.append(response.headers().toString())
builder.append("\n")
} catch (e: IOException) {
builder.append("Error:\n")
builder.append(Log.getStackTraceString(e))
}
builder.append("-----------------------------------------------------------------------\n")
progress += 7
it.onNext(ProgressAndDetail(progress, builder.toString()))
}
it.onComplete()
}).subscribeOn(Schedulers.io())
it.onComplete()
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ProgressAndDetail>() {
override fun onNext(response: ProgressAndDetail) {
@ -185,44 +185,46 @@ class NetworkDiagnosisActivity : ToolBarActivity() {
// 等待10秒等待js检查完成
view.postDelayed({
// WebView生成长图也就是超过一屏的图片代码中的bitmap就是最后生成的长图
mWebView.measure(View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED))
mWebView.layout(0, 0, mWebView.measuredWidth, mWebView.measuredHeight)
mWebView.isDrawingCacheEnabled = true
mWebView.buildDrawingCache()
val bitmap = Bitmap.createBitmap(mWebView.measuredWidth,
mWebView.measuredHeight, Bitmap.Config.ARGB_8888)
tryCatchInRelease {
mWebView.measure(View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED))
mWebView.layout(0, 0, mWebView.measuredWidth, mWebView.measuredHeight)
mWebView.isDrawingCacheEnabled = true
mWebView.buildDrawingCache()
val bitmap = Bitmap.createBitmap(mWebView.measuredWidth,
mWebView.measuredHeight, Bitmap.Config.ARGB_8888)
// 画布的宽高和WebView的网页保持一致
val canvas = Canvas(bitmap)
val paint = Paint()
canvas.drawBitmap(bitmap, 0F, mWebView.measuredHeight.toFloat(), paint)
mWebView.draw(canvas)
// 画布的宽高和WebView的网页保持一致
val canvas = Canvas(bitmap)
val paint = Paint()
canvas.drawBitmap(bitmap, 0F, mWebView.measuredHeight.toFloat(), paint)
mWebView.draw(canvas)
// 保存成文件
val filePath = bitmap2File(bitmap)
// 保存成文件
val filePath = bitmap2File(bitmap)
builder.append("WebView Long Image Path:\n")
builder.append(filePath)
builder.append("\n")
builder.append("\n")
builder.append(mSuccessHint)
builder.append("WebView Long Image Path:\n")
builder.append(filePath)
builder.append("\n")
builder.append("\n")
builder.append(mSuccessHint)
builder.setSpan(object : ClickableSpan() {
override fun updateDrawState(ds: TextPaint) {
super.updateDrawState(ds)
ds.color = resources.getColor(R.color.theme_font)
ds.isUnderlineText = false
}
builder.setSpan(object : ClickableSpan() {
override fun updateDrawState(ds: TextPaint) {
super.updateDrawState(ds)
ds.color = resources.getColor(R.color.theme_font)
ds.isUnderlineText = false
}
override fun onClick(widget: View) {
CommentUtils.copyText(builder.toString().replace(mSuccessHint, ""), applicationContext)
}
}, builder.length - mSuccessHint.length, builder.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
override fun onClick(widget: View) {
CommentUtils.copyText(builder.toString().replace(mSuccessHint, ""), applicationContext)
}
}, builder.length - mSuccessHint.length, builder.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
mResult.text = builder
mResult.setMovementMethod(LinkMovementMethod.getInstance())
setResultProgress(100)
mResult.text = builder
mResult.setMovementMethod(LinkMovementMethod.getInstance())
setResultProgress(100)
}
}, 1000)
}
}
@ -252,7 +254,7 @@ class NetworkDiagnosisActivity : ToolBarActivity() {
override fun handleBackPressed(): Boolean {
if (mProgressData < 100) {
DialogUtils.showAlertDialog(this, "确认退出", "网络诊断还未完成,退出会终止所有诊断进程,确定退出吗?"
,"确定", "取消", DialogUtils.ConfirmListener {
, "确定", "取消", DialogUtils.ConfirmListener {
finish()
}, null)
return true

View File

@ -18,6 +18,10 @@ import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.view.MotionEventCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.ethanhua.skeleton.Skeleton;
import com.ethanhua.skeleton.ViewSkeletonScreen;
import com.gh.base.OnRequestCallBackListener;
@ -65,9 +69,6 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import androidx.annotation.NonNull;
import androidx.core.view.MotionEventCompat;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
@ -228,8 +229,8 @@ public class NewsDetailActivity extends ToolBarActivity implements OnClickListen
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == ViewImageActivity.REQUEST_FOR_VIEWED_IMAGE && resultCode == Activity.RESULT_OK) {
HashSet<Integer> imageSet = (HashSet<Integer>) data.getExtras().get(ViewImageActivity.VIEWED_IMAGE);
if (requestCode == ImageViewerActivity.REQUEST_FOR_VIEWED_IMAGE && resultCode == Activity.RESULT_OK) {
HashSet<Integer> imageSet = (HashSet<Integer>) data.getExtras().get(ImageViewerActivity.VIEWED_IMAGE);
adapter.replaceDefaultImage(imageSet);
}
}

View File

@ -12,6 +12,7 @@ import com.gh.common.util.EntranceUtils;
import com.gh.common.util.PlatformUtils;
import com.gh.common.util.RunningUtils;
import com.gh.gamecenter.entity.CommunityEntity;
import com.gh.gamecenter.entity.SimpleGameEntity;
import com.gh.gamecenter.entity.SubjectRecommendEntity;
import com.gh.gamecenter.entity.VideoLinkEntity;
import com.gh.gamecenter.manager.UserManager;
@ -34,6 +35,7 @@ import static com.gh.common.util.EntranceUtils.HOST_DOWNLOAD;
import static com.gh.common.util.EntranceUtils.HOST_GAME;
import static com.gh.common.util.EntranceUtils.HOST_LIBAO;
import static com.gh.common.util.EntranceUtils.HOST_QQ;
import static com.gh.common.util.EntranceUtils.HOST_QQ_QUN;
import static com.gh.common.util.EntranceUtils.HOST_QQ_GROUP;
import static com.gh.common.util.EntranceUtils.HOST_QUESTION;
import static com.gh.common.util.EntranceUtils.HOST_SUGGESTION;
@ -97,7 +99,7 @@ public class SkipActivity extends BaseActivity {
DirectUtils.directToArticle(this, path, ENTRANCE_BROWSER);
break;
case HOST_GAME:
DirectUtils.directToGameDetail(this, path, ENTRANCE_BROWSER, false, "libao".equals(to),null);
DirectUtils.directToGameDetail(this, path, ENTRANCE_BROWSER, false, "libao".equals(to), null);
break;
case HOST_COLUMN:
DirectUtils.directToSubject(this, path, uri.getQueryParameter(KEY_NAME), ENTRANCE_BROWSER);
@ -182,21 +184,34 @@ public class SkipActivity extends BaseActivity {
String categoryId = uri.getQueryParameter("category_id");
String link = uri.getQueryParameter("link");
VideoLinkEntity linkEntity = new VideoLinkEntity(title, categoryId, link);
// if (!CheckLoginUtils.isLogin()) {
// HaloApp.put(HOST_UPLOAD_VIDEO, linkEntity);
// }
Bundle nextToBundle = VideoManagerActivity.getVideoManagerBundle(linkEntity, EntranceUtils.ENTRANCE_BROWSER, "");
CheckLoginUtils.checkLogin(this, nextToBundle, true, EntranceUtils.ENTRANCE_BROWSER, null);
gameId = uri.getQueryParameter("gameId");
String gameName = uri.getQueryParameter("gameName");
SimpleGameEntity simpleGameEntity = new SimpleGameEntity(gameId, gameName, "");
Bundle nextToBundle = VideoManagerActivity.getVideoManagerBundle(linkEntity, simpleGameEntity, EntranceUtils.ENTRANCE_BROWSER, "");
CheckLoginUtils.checkLogin(this, nextToBundle, true, EntranceUtils.ENTRANCE_BROWSER, () ->
DirectUtils.directToVideoManager(SkipActivity.this, linkEntity, simpleGameEntity, EntranceUtils.ENTRANCE_BROWSER, "浏览器"));
break;
case HOST_VIDEO_SINGLE:
DirectUtils.directToVideoDetail(this, path, VideoDetailContainerViewModel.Location.SINGLE_VIDEO.getValue(),
false, "", ENTRANCE_BROWSER, "浏览器", TextUtils.isEmpty(referer) ? "" : referer);
break;
case HOST_VIDEO_MORE:
gameId = uri.getQueryParameter("gameId");
String act = uri.getQueryParameter("act");
String loaction = TextUtils.isEmpty(act) ? path : VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.getValue();
DirectUtils.directToVideoDetail(this, path, loaction,
false, "", ENTRANCE_BROWSER, "浏览器", TextUtils.isEmpty(referer) ? "" : referer, type, act);
String fieldId = uri.getQueryParameter("fieldId");
String sectionName = uri.getQueryParameter("sectionName");
String location;
if (!TextUtils.isEmpty(act)) {
location = VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.getValue();
} else if (!TextUtils.isEmpty(fieldId)) {
location = VideoDetailContainerViewModel.Location.GAME_ZONE.getValue();
} else {
location = path;
}
DirectUtils.directToVideoDetail(this, path, location,
false, TextUtils.isEmpty(gameId) ? "" : gameId, ENTRANCE_BROWSER, "浏览器", TextUtils.isEmpty(referer) ? "" : referer,
TextUtils.isEmpty(type) ? "" : type, TextUtils.isEmpty(act) ? "" : act, TextUtils.isEmpty(fieldId) ? "" : fieldId,
TextUtils.isEmpty(sectionName) ? "" : sectionName);
break;
case HOST_VIDEO_STREAMING_HOME:
// 把切换放到 MainActivity 处理
@ -230,6 +245,13 @@ public class SkipActivity extends BaseActivity {
bundle.putString(KEY_DATA, path);
EntranceUtils.jumpActivity(this, bundle);
break;
case HOST_QQ_QUN:
String key = uri.getQueryParameter("key");
bundle = new Bundle();
bundle.putString(KEY_TO, HOST_QQ_GROUP);
bundle.putString(KEY_DATA, key);
EntranceUtils.jumpActivity(this, bundle);
break;
case HOST_WEB:
bundle = new Bundle();
bundle.putString(KEY_TO, HOST_WEB);
@ -279,11 +301,11 @@ public class SkipActivity extends BaseActivity {
entity.setLink(path);
entity.setName(name);
entity.setText(name);
DirectUtils.directToBlock(this, entity);
DirectUtils.directToBlock(this, entity, mEntrance);
break;
case EntranceUtils.HOST_SERVER_BLOCK:
DirectUtils.directToGameServers(this,ENTRANCE_BROWSER, "浏览器");
DirectUtils.directToGameServers(this, ENTRANCE_BROWSER, "浏览器");
break;
case EntranceUtils.HOST_AMWAY_BLOCK:
@ -302,6 +324,10 @@ public class SkipActivity extends BaseActivity {
case EntranceUtils.HOST_GAME_UPLOAD:
DirectUtils.directGameUpload(this, ENTRANCE_BROWSER, "浏览器");
break;
case EntranceUtils.HOST_GAME_ZONE:
String zoneUrl = uri.getQueryParameter("url");
DirectUtils.directGameZone(this, path, zoneUrl, ENTRANCE_BROWSER);
break;
default:
EntranceUtils.jumpActivity(this, new Bundle()); // 跳转至首页

View File

@ -15,6 +15,12 @@ import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.g00fy2.versioncompare.Version;
import com.gh.base.BaseActivity;
import com.gh.common.AppExecutor;
@ -33,6 +39,7 @@ import com.gh.common.util.PackageUtils;
import com.gh.common.util.PlatformUtils;
import com.gh.common.util.SPUtils;
import com.gh.common.util.TagUtils;
import com.gh.common.util.TeaHelper;
import com.gh.common.util.UsageStatsHelper;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.entity.AuthDialogEntity;
@ -56,11 +63,6 @@ import java.util.Date;
import java.util.List;
import java.util.Locale;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import pub.devrel.easypermissions.AfterPermissionGranted;
@ -169,9 +171,7 @@ public class SplashScreenActivity extends BaseActivity {
}
private void requestPermission() {
if (EasyPermissions.hasPermissions(this, mPermissions)) {
GdtHelper.INSTANCE.logAction(ActionType.START_APP, GdtHelper.NETWORK_TYPE, DeviceUtils.getNetwork(this));
} else {
if (!EasyPermissions.hasPermissions(this, mPermissions)) {
checkAndRequestPermission();
}
}
@ -276,6 +276,10 @@ public class SplashScreenActivity extends BaseActivity {
private void launchMainActivity() {
getUniqueId();
// 在可能获取了相关权限后才初始化SDK/发送激活数据
TeaHelper.init(getApplication(), HaloApp.getInstance().getChannel());
GdtHelper.INSTANCE.logAction(ActionType.START_APP, GdtHelper.NETWORK_TYPE, DeviceUtils.getNetwork(this));
Bundle bundle = getIntent().getExtras();
Intent intent = new Intent(SplashScreenActivity.this, MainActivity.class);
if (bundle != null) intent.putExtras(bundle);

View File

@ -1,482 +1,482 @@
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;
}
}
}
//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

@ -227,11 +227,12 @@ public class WebActivity extends NormalActivity {
return getTargetIntent(context, WebActivity.class, WebFragment.class, bundle);
}
public static Intent getIntentForWebGame(Context context, String url, String gameName, boolean interveneBackpress) {
public static Intent getIntentForWebGame(Context context, String url, String gameName, boolean interveneBackpress, String closeButton) {
Bundle bundle = new Bundle();
bundle.putString(EntranceUtils.KEY_URL, url);
bundle.putString(KEY_GAME_NAME, gameName);
bundle.putBoolean(WebFragment.KEY_LEAVE_WEB_PAGE_TO_HANDLE_TITLE, true);
bundle.putString(WebFragment.KEY_CLOSE_BUTTON, closeButton);
if (interveneBackpress) {
bundle.putBoolean(KEY_REQUIRE_BACK_CONFIRMATION, true);
bundle.putString(WebFragment.KEY_BACK_CONFIRMATION_CONTENT, "退出后将不保存当前游戏进度,确定退出吗?");

View File

@ -7,10 +7,9 @@ import android.graphics.Bitmap;
import android.media.ThumbnailUtils;
import android.os.Bundle;
import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
import com.facebook.imagepipeline.image.CloseableImage;
import androidx.annotation.NonNull;
import com.gh.common.util.BiCallback;
import com.gh.common.util.ImageUtils;
import com.gh.common.util.LogUtils;
import com.gh.common.util.ShareUtils;
@ -26,8 +25,6 @@ import com.sina.weibo.sdk.utils.Utility;
import org.greenrobot.eventbus.EventBus;
import androidx.annotation.NonNull;
/**
* Created by khy on 2016/11/23.
* <p>
@ -80,7 +77,7 @@ public class WeiBoShareActivity extends Activity implements WbShareCallback {
mWeiboShareAPI = new WbShareHandler(this);
mWeiboShareAPI.registerApp();
weiboLoadBitMap(shareIcon);
loadIconAndShare(shareIcon);
}
@ -89,10 +86,10 @@ public class WeiBoShareActivity extends Activity implements WbShareCallback {
mWeiboShareAPI.doResultIntent(intent, this); //当前应用唤起微博分享后,返回当前应用
}
private void weiboLoadBitMap(String iconUrl) {
ImageUtils.display(this, iconUrl, new BaseBitmapDataSubscriber() {
private void loadIconAndShare(String iconUrl) {
ImageUtils.getBitmap(iconUrl, new BiCallback<Bitmap, Boolean>() {
@Override
protected void onNewResultImpl(Bitmap bitmap) {
public void onFirst(Bitmap bitmap) {
Utils.log("分享获取bitmap成功准备分享");
if (ShareUtils.ShareEntrance.video.name().equals(mShareType)) {
@ -144,8 +141,7 @@ public class WeiBoShareActivity extends Activity implements WbShareCallback {
}
@Override
protected void onFailureImpl(DataSource<CloseableReference<CloseableImage>> dataSource) {
Utils.log("分享获取bitmap失败");
public void onSecond(Boolean aBoolean) {
}
});
}

View File

@ -11,9 +11,12 @@ import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.TextView;
import androidx.collection.ArrayMap;
import androidx.core.content.ContextCompat;
import com.gh.common.util.BitmapUtils;
import com.gh.common.util.MtaHelper;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.PackageInstaller;
import com.gh.gamecenter.R;
import com.gh.gamecenter.adapter.viewholder.KcSelectGameViewHolder;
import com.gh.gamecenter.entity.InstallGameEntity;
@ -28,8 +31,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import androidx.collection.ArrayMap;
import androidx.core.content.ContextCompat;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
@ -74,17 +75,17 @@ public class CleanApkAdapter extends BaseRecyclerAdapter<KcSelectGameViewHolder>
private void init() {
Observable.create(new ObservableOnSubscribe<Object>() {
@Override
public void subscribe(ObservableEmitter<Object> emitter) {
// 扫描和获取apk数据 分步操作 尽量避免 StackoverflowError
FindAllAPKPath(Environment.getExternalStorageDirectory());
LoadApkData();
emitter.onComplete();
}
})
@Override
public void subscribe(ObservableEmitter<Object> emitter) {
// 扫描和获取apk数据 分步操作 尽量避免 StackoverflowError
FindAllAPKPath(Environment.getExternalStorageDirectory());
LoadApkData();
emitter.onComplete();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Response<Object>(){
.subscribe(new Response<Object>() {
@Override
public void onComplete() {
super.onComplete();
@ -264,7 +265,7 @@ public class CleanApkAdapter extends BaseRecyclerAdapter<KcSelectGameViewHolder>
@Override
public void onClick(View v) {
if (mIsScanOver) {
mContext.startActivity(PackageUtils.getInstallIntent(mContext, gameEntity.getGamePath()));
mContext.startActivity(PackageInstaller.getInstallIntent(mContext, gameEntity.getGamePath()));
}
}
});

View File

@ -9,6 +9,8 @@ import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.common.exposure.ExposureEvent;
import com.gh.common.exposure.ExposureSource;
@ -27,8 +29,6 @@ import com.gh.gamecenter.game.data.GameItemData;
import java.util.ArrayList;
import java.util.List;
import androidx.annotation.NonNull;
/**
* ImagePagerAdapter
*
@ -100,10 +100,10 @@ public class ImagePagerAdapter extends RecyclingPagerAdapter {
// 轮播图数据统计
if (mContext instanceof MainActivity) {
DataLogUtils.uploadLunbotuLog(mContext, slideEntity.getType(), slideEntity.getText(), String
DataLogUtils.uploadLunbotuLog(mContext, slideEntity.getType(), slideEntity.getText(), "", String
.valueOf(getPosition(position) % size + 1), "游戏库");
} else {
DataLogUtils.uploadLunbotuLog(mContext, slideEntity.getType(), slideEntity.getText(), String
DataLogUtils.uploadLunbotuLog(mContext, slideEntity.getType(), slideEntity.getText(), "", String
.valueOf(getPosition(position) % size + 1), mSource);
}

View File

@ -9,6 +9,10 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.gh.base.OnRequestCallBackListener;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.ImageUtils;
@ -38,9 +42,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import retrofit2.HttpException;
@ -177,7 +178,12 @@ public class LibaoDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
}
});
ImageUtils.display(holder.libaoGameIcon, mLibaoEntity.getIcon());
if (mLibaoEntity.getGame() != null) {
ImageUtils.display(holder.libaoGameIcon.getIconIv(), mLibaoEntity.getGame().getIcon());
ImageUtils.display(holder.libaoGameIcon.getIconDecoratorIv(), mLibaoEntity.getGame().getIconSubscript());
} else {
ImageUtils.display(holder.libaoGameIcon.getIconIv(), mLibaoEntity.getIcon());
}
holder.libaoName.setText(mLibaoEntity.getName());
if (TextUtils.isEmpty(mLibaoEntity.getPlatform())) {
holder.libaoGameName.setText(mLibaoEntity.getGame().getName());

View File

@ -9,7 +9,11 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.gh.common.constant.Config;
import com.gh.common.databind.BindingAdapters;
import com.gh.common.util.CheckLoginUtils;
import com.gh.common.util.CommentUtils;
import com.gh.common.util.ConcernContentUtils;
@ -55,8 +59,6 @@ import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import retrofit2.HttpException;
@ -276,7 +278,12 @@ public class MessageDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
mContext.getResources().getDisplayMetrics().widthPixels - DisplayUtils.dip2px(mContext, 34));
}
ImageUtils.display(viewHolder.thumb, mConcernEntity.getGameIcon());
if (mConcernEntity.getGame() != null) {
ImageUtils.display(viewHolder.thumb.getIconIv(), mConcernEntity.getGame().getIcon());
ImageUtils.display(viewHolder.thumb.getIconDecoratorIv(), mConcernEntity.getGame().getIconSubscript());
} else {
ImageUtils.display(viewHolder.thumb.getIconIv(), mConcernEntity.getGameIcon());
}
viewHolder.title.setText(mConcernEntity.getGameName());
NewsUtils.setNewsPublishOn(viewHolder.time, mConcernEntity.getTime());

View File

@ -1,11 +1,10 @@
package com.gh.gamecenter.adapter.viewholder;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.base.BaseRecyclerViewHolder;
import com.gh.common.view.GameIconView;
import com.gh.gamecenter.R;
import butterknife.BindView;
@ -16,7 +15,7 @@ import butterknife.BindView;
public class ConcernViewHolder extends BaseRecyclerViewHolder {
@BindView(R.id.concern_item_icon)
public SimpleDraweeView concern_item_icon;
public GameIconView concern_item_icon;
@BindView(R.id.concern_item_name)
public TextView concern_item_name;
@ -24,9 +23,6 @@ public class ConcernViewHolder extends BaseRecyclerViewHolder {
@BindView(R.id.concern_item_concern)
public TextView concern_item_concern;
@BindView(R.id.concern_libao_icon)
public ImageView concern_libao_icon;
public ConcernViewHolder(View itemView) {
super(itemView);
}

View File

@ -19,6 +19,7 @@ import com.gh.common.util.DialogUtils;
import com.gh.common.util.DownloadDialogHelper;
import com.gh.common.util.LogUtils;
import com.gh.common.util.MtaHelper;
import com.gh.common.util.PackageInstaller;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.PermissionHelper;
import com.gh.common.util.ReservationHelper;
@ -135,16 +136,8 @@ public class DetailViewHolder {
MtaHelper.onEvent("游戏详情_新", "更新", mGameEntity.getName());
}
String autoDownloadPlatform = v.getTag() instanceof String ? (String) v.getTag() : "";
v.setTag(null);
switch (mViewHolder.mDownloadPb.getDownloadType()) {
case DOWNLOADING_PLUGIN:
case DOWNLOADING_NORMAL:
Intent intent = DownloadManagerActivity.getDownloadMangerIntent(mViewHolder.context,
mGameEntity.getApk().get(0).getUrl(),
StringUtils.buildString(mEntrance, "+(", mName, "[", mTitle, "])"));
mViewHolder.context.startActivity(intent);
break;
case NONE_WITH_HINT:
case NONE:
String offStatus = mGameEntity.getDownloadOffStatus();
@ -172,7 +165,9 @@ public class DetailViewHolder {
() -> {
CertificationDialog.showCertificationDialog(mViewHolder.context, mGameEntity, () -> {
DialogUtils.showVersionNumberDialog(mViewHolder.context, mGameEntity, () -> {
DialogUtils.checkDownload(mViewHolder.context, apk.getSize(), this::download);
DialogUtils.showOverseaDownloadDialog(mViewHolder.context, mGameEntity, () -> {
DialogUtils.checkDownload(mViewHolder.context, apk.getSize(), this::download);
});
});
});
});
@ -212,7 +207,7 @@ public class DetailViewHolder {
mDownloadEntity = DownloadManager.getInstance(mViewHolder.context).getDownloadEntityByUrl(mGameEntity.getApk().get(0).getUrl());
}
if (mDownloadEntity != null) {
PackageUtils.launchSetup(mViewHolder.context, mDownloadEntity);
PackageInstaller.install(mViewHolder.context, mDownloadEntity);
}
});
break;
@ -248,14 +243,21 @@ public class DetailViewHolder {
MtaHelper.onEvent("H5页面", "入口", "详情页_" + mGameEntity.getName());
LinkEntity linkEntity = mGameEntity.getH5Link();
boolean isPlay = "play".equals(linkEntity.getType()); // 是否为开始玩
boolean isPlay = "play".equals(linkEntity.getType()); // 是否为开始玩
if (isPlay) {
HistoryHelper.insertGameEntity(mGameEntity);
}
Intent i = new Intent(WebActivity.getIntentForWebGame(mViewHolder.context, linkEntity.getLink(), mGameEntity.getName(),isPlay));
Intent i = new Intent(WebActivity.getIntentForWebGame(mViewHolder.context, linkEntity.getLink(), mGameEntity.getName(), isPlay, linkEntity.getCloseButton()));
mViewHolder.context.startActivity(i);
break;
default:
Intent intent = DownloadManagerActivity.getDownloadMangerIntent(mViewHolder.context,
mGameEntity.getApk().get(0).getUrl(),
StringUtils.buildString(mEntrance, "+(", mName, "[", mTitle, "])"));
mViewHolder.context.startActivity(intent);
break;
}
}

View File

@ -5,8 +5,8 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.base.BaseRecyclerViewHolder;
import com.gh.common.view.GameIconView;
import com.gh.gamecenter.R;
import butterknife.BindView;
@ -17,7 +17,7 @@ import butterknife.BindView;
public class GameDownloadViewHolder extends BaseRecyclerViewHolder {
@BindView(R.id.dm_item_iv_icon)
public SimpleDraweeView dmIcon;
public GameIconView dmIcon;
@BindView(R.id.dm_item_tv_title)
public TextView dmTitle;
@ -37,6 +37,9 @@ public class GameDownloadViewHolder extends BaseRecyclerViewHolder {
@BindView(R.id.dm_item_tv_startorpause)
public TextView dmStartorpause;
@BindView(R.id.unzip_failure_hint)
public View unzipFailureHint;
public GameDownloadViewHolder(View itemView) {
super(itemView);
}

View File

@ -3,8 +3,8 @@ package com.gh.gamecenter.adapter.viewholder;
import android.view.View;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.base.BaseRecyclerViewHolder;
import com.gh.common.view.GameIconView;
import com.gh.gamecenter.R;
import butterknife.BindView;
@ -15,7 +15,7 @@ import butterknife.BindView;
public class GameUpdateViewHolder extends BaseRecyclerViewHolder {
@BindView(R.id.gu_item_iv_icon)
public SimpleDraweeView guIcon;
public GameIconView guIcon;
@BindView(R.id.gu_item_tv_name)
public TextView guName;

View File

@ -7,9 +7,9 @@ import android.widget.TextView;
import androidx.annotation.Nullable;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.base.BaseRecyclerViewHolder;
import com.gh.common.view.DrawableView;
import com.gh.common.view.GameIconView;
import com.gh.gamecenter.databinding.GameItemBinding;
import com.gh.gamecenter.entity.ColorEntity;
import com.gh.gamecenter.entity.GameEntity;
@ -17,7 +17,7 @@ import com.gh.gamecenter.entity.GameEntity;
public class GameViewHolder extends BaseRecyclerViewHolder {
public SimpleDraweeView gameThumb;
public GameIconView gameThumb;
public TextView gameName;
public TextView gameDownloadBtn;
public TextView gameDes;

View File

@ -1,11 +1,12 @@
package com.gh.gamecenter.adapter.viewholder;
import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import androidx.recyclerview.widget.RecyclerView;
import com.gh.base.BaseRecyclerViewHolder;
import com.gh.common.view.GameIconView;
import com.gh.gamecenter.R;
import butterknife.BindView;
@ -24,7 +25,7 @@ public class LibaoDetailTopViewHolder extends BaseRecyclerViewHolder {
@BindView(R.id.libaodetail_copy_btn)
public TextView libaoCopyBtn;
@BindView(R.id.libaodetail_game_icon)
public SimpleDraweeView libaoGameIcon;
public GameIconView libaoGameIcon;
@BindView(R.id.libaodetail_libaocode_rv)
public RecyclerView libaoCodeRv;

View File

@ -3,9 +3,9 @@ package com.gh.gamecenter.adapter.viewholder;
import android.view.View;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.base.BaseRecyclerViewHolder;
import com.gh.base.OnListClickListener;
import com.gh.common.view.GameIconView;
import com.gh.gamecenter.R;
import com.gh.gamecenter.entity.LibaoEntity;
@ -25,7 +25,7 @@ public class LibaoNormalViewHolder extends BaseRecyclerViewHolder<LibaoEntity> {
@BindView(R.id.libao_btn_status)
public TextView libaoBtnStatus;
@BindView(R.id.libao_game_icon)
public SimpleDraweeView libaoGameIcon;
public GameIconView libaoGameIcon;
public LibaoNormalViewHolder(View itemView) {
super(itemView);

View File

@ -3,8 +3,8 @@ package com.gh.gamecenter.adapter.viewholder;
import android.view.View;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.base.BaseRecyclerViewHolder;
import com.gh.common.view.GameIconView;
import com.gh.gamecenter.R;
import butterknife.BindView;
@ -15,15 +15,13 @@ import butterknife.BindView;
public class NewsDetailGameViewHolder extends BaseRecyclerViewHolder {
@BindView(R.id.game_iv_thumb)
public SimpleDraweeView gameThumb;
public GameIconView gameThumb;
@BindView(R.id.game_tv_name)
public TextView gameName;
@BindView(R.id.game_tv_info)
public TextView gameInfo;
@BindView(R.id.game_tv_concern)
public TextView gameConcern;
@BindView(R.id.game_iv_libao)
public View libaoIcon;
@BindView(R.id.game_server_type)
public TextView gameServerType;

View File

@ -5,9 +5,9 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.base.BaseRecyclerViewHolder;
import com.gh.base.OnListClickListener;
import com.gh.common.view.GameIconView;
import com.gh.gamecenter.R;
import com.gh.gamecenter.entity.ConcernEntity;
@ -20,7 +20,7 @@ import butterknife.BindView;
public class NewsDigestViewHolder extends BaseRecyclerViewHolder<ConcernEntity> {
@BindView(R.id.news_digest_thumb)
public SimpleDraweeView thumb;
public GameIconView thumb;
@BindView(R.id.news_digest_title)
public TextView title;
@BindView(R.id.news_digest_time)
@ -55,6 +55,6 @@ public class NewsDigestViewHolder extends BaseRecyclerViewHolder<ConcernEntity>
itemView.setOnClickListener(this);
share.setOnClickListener(this);
comment.setOnClickListener(this);
}
}

View File

@ -32,7 +32,9 @@ import com.lightgame.view.CheckableImageView
import java.util.*
import java.util.regex.Pattern
class AmwayAdapter(context: Context, private var mViewModel: AmwayViewModel, private var mLayoutManager: RecyclerView.LayoutManager)
class AmwayAdapter(context: Context,
private var mViewModel: AmwayViewModel,
private var mLayoutManager: RecyclerView.LayoutManager)
: ListAdapter<AmwayListItemData>(context), IExposable {
private val mBasicExposureSource by lazy { ExposureSource("安利墙", "") }
@ -185,10 +187,13 @@ class AmwayAdapter(context: Context, private var mViewModel: AmwayViewModel, pri
itemData.exposureEvent = ExposureEvent.createEvent(amway.game.toGameEntity(), listOf(basicExposureSource))
binding.gameContainer.setOnClickListener {
GameDetailActivity.startGameDetailActivity(binding.root.context, amway.game.id, "(安利墙)", itemData.exposureEvent)
GameDetailActivity.startGameDetailActivity(binding.root.context, amway.game.id, "${viewModel.entrance}+(安利墙)", itemData.exposureEvent)
MtaHelper.onEvent("安利墙", "点击", "评论${blockPosition}_${amway.game.name}_游戏")
}
ImageUtils.display(binding.gameIconView.getIconIv(), amway.game.rawIcon ?: amway.game.icon)
ImageUtils.display(binding.gameIconView.getIconDecoratorIv(), amway.game.iconSubscript)
amway.game.tag?.let {
val tags = it.take(3)
val tagString = StringBuilder()
@ -200,13 +205,14 @@ class AmwayAdapter(context: Context, private var mViewModel: AmwayViewModel, pri
}
binding.commentContainer.setOnClickListener {
val intent = RatingReplyActivity.getIntent(context, amway.game.id, amway.comment, false, "(安利墙)", "")
val intent = RatingReplyActivity.getIntent(context, amway.game.id, amway.comment, false, viewModel.entrance
?: "", EntranceUtils.ENTRANCE_AMWAY)
SyncDataBetweenPageHelper.startActivityForResult(binding.root.context, intent, RatingFragment.RATING_REPLAY_REQUEST, adapterPosition)
MtaHelper.onEvent("安利墙", "点击", "评论${blockPosition}_${amway.game.name}_评论")
}
binding.userIconContainer.setOnClickListener {
DirectUtils.directToHomeActivity(context, amway.comment.user.id, "(安利墙)", "")
binding.userClickableView.setOnClickListener {
DirectUtils.directToHomeActivity(context, amway.comment.user.id, viewModel.entrance, EntranceUtils.ENTRANCE_AMWAY)
}
if (amway.comment.me.isVoted) {

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