Compare commits

...

440 Commits
v2.4 ... v3.0.2

Author SHA1 Message Date
44deb59624 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev
# Conflicts:
#	gradle.properties
2017-12-11 18:42:29 +08:00
27fc39cb70 revert tinkerid 2017-12-11 17:35:25 +08:00
5480caf44e update gradle to 3.0 & fix tinker filepath 2017-12-11 17:33:44 +08:00
a448a974f1 1.开服日历页面时关闭日历详情
2.删除对开服信息时间的判断 如果有开服信息就显示开服日历
2017-12-07 16:14:15 +08:00
1738fac5fd Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-12-06 18:33:10 +08:00
7b74318ca5 修复详情页面 启动需要检查网络的BUG 2017-12-06 18:32:19 +08:00
6dabbe19dd update libs 2017-12-06 16:15:20 +08:00
e286be7f1e 修复开服表过年无法显示问题,修改开服表显示规则 2017-12-05 16:39:21 +08:00
ef040f68c7 区分企业QQ和普通QQ的打开方式 2017-12-05 10:15:44 +08:00
b134d077cf 礼包领取判断修正 2017-12-04 12:01:43 +08:00
69fe3b3d76 fix appbar scroll behavior 2017-11-28 15:37:57 +08:00
c134c4bf9e 删除无用log 2017-11-28 09:22:09 +08:00
633acb9236 礼包领取限制 2017-11-27 16:03:32 +08:00
cf84205571 修复礼包列表和专题列表分页加载过慢问题 2017-11-27 15:04:12 +08:00
5aced2c03c 光环助手V3.0.2优化需求与bug汇总(11-27) 2017-11-27 11:57:11 +08:00
6cfa949493 解决跳转QQ后无法聊天问题 2017-11-27 09:27:29 +08:00
dcfe3dee0e Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-11-23 20:04:24 +08:00
8348568ea7 修复3.0.1意见反馈Bug
1.ImageUtils传入的url允许为空
2.修复专题ActionBarTitle有可能为空问题
2017-11-23 19:58:00 +08:00
1d5dece894 fix git submodule update script 2017-11-23 15:57:52 +08:00
64594c4457 调整下载重试时间间隔 2017-11-22 17:12:07 +08:00
c2274d6b6f 3.0升级日志 2017-11-22 15:44:04 +08:00
165059ca2d 快传 传送最低时间设为1秒 2017-11-22 15:07:29 +08:00
89e05d1a29 设置GH_TEST渠道为Bugly(tinker)开发设备 2017-11-20 17:24:02 +08:00
9f00cab409 优化首页缓存 2017-11-19 12:00:00 +08:00
e8a795f97b 1117测试问题:
1.如果后台隐藏了评论,前端在消息中心点击跳转对话详情,会一直转圈圈,也没任何提示(应该要出现飞碟提示才对)
2.消息中心-客服,排版和“评论”不一致
(1)分割线与头像之间的间距(比“评论”那边窄了)
(2)昵称和时间之间的间距(比“评论”那边宽了)
2017-11-17 11:37:00 +08:00
96c233ebd0 光环助手V3.0.2 评论对话增加弹窗(回复/点赞/复制 等) 2017-11-16 20:04:03 +08:00
36ef13b237 光环助手V3.0.2优化需求汇总(20171116) 2017-11-16 18:06:23 +08:00
f54fdd6016 可以多次重定向,但有数量限制.断网重连先延迟后判断 2017-11-16 12:02:32 +08:00
a43947077a 新增打渠道包脚本 2017-11-16 09:19:47 +08:00
a863b7be13 fix proguard library 2017-11-15 17:33:47 +08:00
ab1ce8e179 HttpDns版本和友盟HttpDns改成一致 2017-11-15 17:12:27 +08:00
d391ab20b7 更换3.0.1tinker_id以及tinker_base_apk_dir 2017-11-15 15:13:47 +08:00
8403bc3a64 求版本敏感词优化 2017-11-15 15:07:34 +08:00
038e736eb1 下载暂停和继续延迟一秒,防止一个包同时有多个线程在下载(后续想一个更好的方案) 2017-11-15 10:41:02 +08:00
a215192175 下载增加httpdns 2017-11-14 20:42:27 +08:00
22a1895050 消息中心优化, 求版本功能完善(敏感词toast还要确认), 修复搜索游戏失败时无法重试问题 2017-11-13 17:44:10 +08:00
2df42c772b 下载重试优化 2017-11-13 15:34:24 +08:00
82fea04460 首页启动性能优化-获取到了新数据后 要更新界面 2017-11-10 15:53:53 +08:00
6ccd70dad2 首页启动性能优化,修复游戏平台面板插件图标异常问题 2017-11-09 19:28:24 +08:00
ec2d14bffe 修复refreshToken过期重试问题 2017-11-09 11:47:13 +08:00
1d544ff558 修复登录相关问题(删除用户数据后发注销广播) 2017-11-08 20:06:40 +08:00
867cd29e0f 修复强制弹窗 2017-11-08 19:39:59 +08:00
d1cf660af6 修复获取验证码返回处理 2017-11-08 18:36:58 +08:00
adabe3a227 20171108issues: http://gitlab.ghzhushou.com/pm/halo-app-issues/issues/91
登录无网络处理
2017-11-08 18:31:38 +08:00
b57ef6c3b4 refreshToken抽离 refreshToken同一时间只能访问一次 2017-11-08 10:53:23 +08:00
1b50823843 修复第一次登录头像提示 2017-11-07 21:04:31 +08:00
18ddad522a 补丁弹窗问题 2017-11-07 14:40:50 +08:00
85b034ecfb make sure that update dialog show 2017-11-07 12:05:11 +08:00
5a04749ef2 make sure that update dialog show 2017-11-07 11:59:22 +08:00
bcd32dde85 fix tinker patch 2017-11-07 11:31:41 +08:00
7832cb810b fix tinker script bug 2017-11-07 10:59:00 +08:00
cb652721a7 fix tinker script bug 2017-11-07 10:57:19 +08:00
4ad005fb53 update about version 2017-11-07 10:46:19 +08:00
ce312aea72 fix tinkerId show update log 2017-11-07 10:39:40 +08:00
6c5dc556e8 fix tinker support 2017-11-07 10:21:03 +08:00
95dc18f1d0 remove tinkerid before tinker release base 2017-11-07 10:17:10 +08:00
f284cc8676 test 2017-11-07 09:59:47 +08:00
0cc456c4a1 3.0.1优化
http://gitlab.ghzhushou.com/pm/halo-app-issues/issues/87
2017-11-06 18:29:34 +08:00
0666146374 11-6 打母包 2017-11-06 15:56:32 +08:00
5860ca720e 优化掉线问题 2017-11-03 19:43:49 +08:00
91ee8362ba 修复查看评论 2017-11-02 19:43:04 +08:00
dd81460826 修复补丁弹窗问题 2017-11-01 17:29:50 +08:00
5180916d9f Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-11-01 17:16:18 +08:00
4d92ddcc75 ... 2017-11-01 17:15:09 +08:00
f1f0900e13 get tinkerid 2017-11-01 17:12:46 +08:00
6edf68d03c 补丁安装完成弹窗提示优化 2017-11-01 15:23:11 +08:00
c643ae507b 添加额外渠道 2017-10-31 18:13:05 +08:00
fdc843a93a V3.0.1优化完成 补丁安装完成要出现弹窗提示->还没测试 2017-10-31 18:11:09 +08:00
383c06c72a V3.0.1优化 1~9 2017-10-31 11:10:29 +08:00
a255ce3e36 添加渠道 2017-10-27 18:16:00 +08:00
fa58e820c1 修改老用户同步逻辑 2017-10-27 18:07:51 +08:00
fad011e2fc 修复登录掉线问题(应该OK了),关于版本显示补丁包版本号 2017-10-26 14:51:51 +08:00
9fad040e14 修复礼包(从文章超链接进入礼包详情),登录问题 2017-10-25 18:39:24 +08:00
8d0ab82f15 整理hardcode问题,整理imageUtils工具类 2017-10-19 09:11:22 +08:00
3d7e994b93 增加NormalActivity/fragment 2017-10-17 20:35:03 +08:00
4d40c0f431 修复首页页面切换时 插件化模块自动滑动问题 2017-10-16 17:53:39 +08:00
d9bb60e66b 整理Entrance type 2017-10-16 16:49:22 +08:00
52433ba347 抽离部分java代码中的字符串,equals判断常量放在前面 2017-10-14 15:52:15 +08:00
4290846698 抽离代码中的fromHtml的content,修复个人中心同步问题 2017-10-14 14:44:25 +08:00
6bbd7ec00d ... 2017-10-13 17:01:51 +08:00
dc535ddb38 V3.0 10-12汇总, 优化我的关注和登录页面 2017-10-13 16:54:37 +08:00
dfe087e702 登录增加进度弹窗,统一toast 2017-10-12 17:57:40 +08:00
930e0b92eb 修改下载管理更新游戏逻辑 2017-10-12 16:37:18 +08:00
3199bc6118 10-12优化补充 2017-10-12 09:57:36 +08:00
b1384375a3 打publish包 2017-10-12 09:24:45 +08:00
81dd98cefd 关于版本号更改, gson工具类 2017-10-11 20:19:14 +08:00
02108b3273 ViewPager 指针四舍五入取整 2017-10-11 15:34:12 +08:00
c5deb87c23 整理代码中的文本 2017-10-11 10:24:07 +08:00
572274a2e3 V3.0 10月10日测试包 2017-10-10 16:36:10 +08:00
7f303b362d bug修复 2017-10-10 15:19:17 +08:00
41ce6cce05 打tinker包 2017-10-10 10:17:38 +08:00
f6e1c55749 ... 2017-10-10 09:53:14 +08:00
d24bcf8e6e 修复游戏详情空白提示异常问题 2017-10-09 18:42:46 +08:00
2b497ff679 登录成功提示逻辑修改 2017-10-09 16:05:09 +08:00
627c20f6fd 修复验证码礼包无法领取问题 2017-10-09 15:15:18 +08:00
cfab918bea layout的颜色代码抽离 2017-10-09 14:29:53 +08:00
89746203fe 登录完成后自动刷新页面(我的关注,存号箱,礼包-关注), 修复资讯-关注礼包同步问题 2017-10-09 11:47:25 +08:00
8bc6947c94 dialog由默认layout改为在xml创建 2017-10-08 17:58:53 +08:00
5a264804da 整理礼包详情内容(layout抽离到xml) 2017-10-08 15:42:04 +08:00
1f5e025fea java代码抽离颜色代码,修复历史礼包已知bug 2017-10-08 14:56:40 +08:00
3201e220a0 礼包重复领取修复已知问题,收藏页面优化 2017-10-08 11:39:49 +08:00
d41129011b Tab指针居中 2017-09-28 17:50:20 +08:00
6eb1aefb1c V3.0 09-27 Tab指针居中问题未解决 2017-09-28 15:25:06 +08:00
433646b455 修改登录相关接口循环请求问题, 定时任务统一用postDelayedRunnable在baseFragment 统一remove 避免刷新过程中对出闪退 2017-09-28 11:57:34 +08:00
257e7d1d27 viewPager+fragment adapter notifyItemRangeInserted去除判断(好像是recyclerview修复了这个bug) 2017-09-27 18:22:51 +08:00
f668d6492a 整理部分可以快速弄好的todo 2017-09-27 17:44:15 +08:00
b8ea968a58 抽离列表的onclick事件 还未完成(太太太多啦), click startActivityForResult相关已经完成 2017-09-27 17:15:58 +08:00
5b361afc67 更换沉浸栏实现方法, actionBar menu 修改 2017-09-27 11:28:07 +08:00
0947cd1e24 fix tinker 2017-09-26 19:22:30 +08:00
f8ab1f108b Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-26 18:39:53 +08:00
84f53bbf81 fix gnu/bsd script 2017-09-26 18:39:50 +08:00
ad2656a24b ... 2017-09-26 18:39:42 +08:00
4734fbd651 fix tinker 2017-09-26 17:46:20 +08:00
a947755383 tinker test 2017-09-26 17:05:45 +08:00
f4a1283073 将列表onclick事件提取出来(只完成资讯模块) 2017-09-26 16:34:51 +08:00
f37a2c5b24 测试tinker回退, 没有测试成功.
获取VERSION_CODE失败 返回的是null.
跑patch脚本 gradle指定baseApk路径和脚本生成(多出两层目录)的路径不一致.
打出来的补丁包上传时提示"未匹配到可应用补丁app版本".
2017-09-25 20:56:10 +08:00
cca7d770f1 测试tinker 改了一些脚本 待会改回来哈 2017-09-25 19:28:30 +08:00
2c7f3f2321 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-25 18:34:16 +08:00
cc92e025f2 fix tinker build.gradle file 2017-09-25 18:32:57 +08:00
ce0e28dad6 activity入口整理(涉及到外部启动(插件跳转/推送)的未完成) 2017-09-25 17:22:07 +08:00
b483f7cdb6 整理xml的hardCode(部分固定文案尚未抽离) 2017-09-25 16:36:40 +08:00
8ce0694099 重写选择地区页面 2017-09-25 16:04:52 +08:00
910b356a05 push LGLibrary?? 2017-09-25 12:05:13 +08:00
d74e0310f8 测试tinker 2017-09-25 11:40:10 +08:00
ee19879577 整理startActivityForResult CODE 2017-09-22 18:02:20 +08:00
5e3d7d2f2b 删除无用的数据库相关类(concernDao还需整理), 修复不能重复领取/淘号问题 2017-09-22 15:27:54 +08:00
f292eb0fc6 9月19日测试包 完成 2017-09-22 09:21:49 +08:00
475de2281b Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-21 16:15:12 +08:00
949c8331d2 礼包详情重复淘号,从存号箱和普通列表进去礼包详情 数据同步 2017-09-21 16:09:06 +08:00
a5e0fa8402 fix conflict 2017-09-21 15:27:03 +08:00
f0066b3dac fix strings.xml formatable string 2017-09-21 15:26:04 +08:00
10480a533c add readme sample 2017-09-21 12:12:45 +08:00
d36dcac817 登录失败提示 2017-09-21 10:33:42 +08:00
d6d49ac7ad 整理Activity入口(还有部分未检查) 2017-09-20 20:01:07 +08:00
573e127fc7 整理消息页面(阅读后即时提交) 2017-09-20 15:39:20 +08:00
9cb6e599a4 整理无用代码 2017-09-20 14:37:40 +08:00
f42dd1013e Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-19 15:05:48 +08:00
70441658e5 收藏同步相关 2017-09-19 15:04:59 +08:00
0ca71f25f6 fix context related 2017-09-19 14:28:43 +08:00
5d3a4e24f9 fix debug keystore 2017-09-19 10:29:03 +08:00
3b8faf47c4 资讯-关注 优化 2017-09-18 19:33:01 +08:00
ac21776d3b 退出登录时增加过程弹窗, 防止过早或过慢清除token造成的页面错误 2017-09-18 18:36:13 +08:00
e5127c9040 退出登录时增加过程弹窗, 防止过早或过慢清除token造成的页面错误 2017-09-18 18:35:32 +08:00
8533d06943 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-18 17:11:27 +08:00
066dd6103f 用户数据同步-收藏增加多页面同步, 礼包部分整理(资讯-关注接口欠缺礼包用户数据, 评论相关接口欠缺用户数据) 2017-09-18 17:11:15 +08:00
670f9c280c add debug bugly 2017-09-18 15:37:43 +08:00
5ea3c58b77 update tree image 2017-09-18 14:16:50 +08:00
6e15571ef4 create patch test 2017-09-18 12:17:49 +08:00
ad46fced4f Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-18 12:06:54 +08:00
5acf42423e add rsync to 2.100 for backup 2017-09-18 12:06:44 +08:00
2a2bef8a47 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-16 18:31:20 +08:00
2ffb0f59a2 用户数据同步-礼包相关(优化/整理) 2017-09-16 18:30:51 +08:00
400bdaa6f5 fix script var naming 2017-09-16 18:10:54 +08:00
fa449966f3 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-16 17:29:41 +08:00
f4078493f6 fix patch/channel/ release scripts 2017-09-16 17:29:31 +08:00
5007f7e83d 用户数据同步-我的关注(页面更新逻辑修改) 2017-09-16 14:49:25 +08:00
ca1a8e15cb Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-16 09:24:35 +08:00
b9f299805f 用户数据同步-基本完成(比较多细节问题需优化) 2017-09-15 18:38:31 +08:00
acde49012f test tinker multiple channel release & patch 2017-09-15 18:17:35 +08:00
76c69f0d19 fix tinkerid not the same 2017-09-15 17:38:51 +08:00
c0b91b4a63 Merge remote-tracking branch 'origin/dev' into dev 2017-09-15 17:00:47 +08:00
f6a194ae92 fix patch script 2017-09-15 16:57:27 +08:00
9e7d178972 fix tinker patch path 2017-09-15 16:25:03 +08:00
17b2e2dca7 test patch 2017-09-15 16:03:22 +08:00
eb73bcd25f fix script 2017-09-15 16:00:13 +08:00
f96c090120 test script 2017-09-15 15:39:46 +08:00
aefdaee424 fix script 2017-09-15 15:34:50 +08:00
27ceee37c0 fix script 2017-09-15 15:33:24 +08:00
e1c2e3c984 fix script 2017-09-15 15:25:56 +08:00
48e3718fc2 add tinker scripts 2017-09-15 15:24:44 +08:00
ee20f65a9b 用户数据同步-礼包相关(基本完成, 尚未测试) 2017-09-15 14:22:40 +08:00
235a13855b 用户数据同步相关 2017-09-14 20:03:47 +08:00
acec42c6af 光环助手V3.0(9月12日测试包) 2017-09-14 17:16:33 +08:00
6d009eddf5 test tinker 2017-09-14 16:54:17 +08:00
b0dde281ea 相关数据关联UserDataEntity, 统一在Interceptor添加token 2017-09-14 15:23:08 +08:00
1e7d43bc72 优化6.0权限, 快传部分权限先搁置 2017-09-14 11:54:19 +08:00
8576a1edaf test patch 2017-09-13 19:24:26 +08:00
5dc97d2191 add test 2017-09-13 19:12:48 +08:00
a32b45e067 fix conflict 2017-09-13 18:31:41 +08:00
ff8210c72b proxy app logic 2017-09-13 18:27:11 +08:00
85031e89b6 Android 6.0及以上 权限适配 2017-09-13 18:23:55 +08:00
f2676532ce test fresco 2017-09-13 17:58:18 +08:00
7a996ee8f4 test fresco 2017-09-13 17:50:04 +08:00
d09e28d653 fix fresco crash 2017-09-13 17:08:24 +08:00
e446f4190f remove crash button 2017-09-13 16:57:34 +08:00
f53534b803 add crash button 2017-09-13 16:40:49 +08:00
fb6792f71b add crash button 2017-09-13 16:40:37 +08:00
5d66d55db1 remove crash me 2017-09-13 16:19:40 +08:00
d3bdd48425 test 2017-09-13 16:07:33 +08:00
c89f675ccf remove uncaught handler 2017-09-13 16:02:30 +08:00
f0b989d20e add crash button 2017-09-13 15:54:18 +08:00
3b950b6b28 fix extend 2017-09-13 15:47:37 +08:00
d10a0f956e fix crash 2017-09-13 15:11:41 +08:00
2317228642 test tinker patch 2017-09-13 14:47:08 +08:00
78a4571a8a Merge remote-tracking branch 'origin/dev' into merge 2017-09-12 15:03:56 +08:00
c7b16ce91b fix npe 2017-09-12 15:02:46 +08:00
2e84aeaccf 9月7日测试包 UI部分完成 2017-09-12 10:52:17 +08:00
49e9a532b8 Merge remote-tracking branch 'origin/dev' into merge 2017-09-11 15:37:45 +08:00
47f7d70cfb fix apiservice 2017-09-11 15:37:31 +08:00
a3b03efcbe Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-11 10:44:47 +08:00
814d4faf3e 9月7日测试包 UI部分未完成 2017-09-11 10:44:30 +08:00
c487dc2f03 fix webview focus issue 2017-09-08 15:22:40 +08:00
f552a06bd7 1、takingdata升级,精简无用的组件,480kb->220kb
2、数据统计部分整理,切勿更改数据统计和错误上报任何顺序
3、依赖整理
2017-09-08 11:58:56 +08:00
4de5593802 9月7日测试包 部分修复 2017-09-08 11:04:45 +08:00
4294e9f98d 最新礼包合并 2017-09-07 16:13:23 +08:00
05a911f1e3 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-07 16:06:08 +08:00
464f8d7851 最新礼包重做完成 2017-09-07 16:05:26 +08:00
370b07be31 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-07 14:32:30 +08:00
cb49c71279 check entrance 2017-09-07 14:32:20 +08:00
f637fb9596 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-06 17:01:27 +08:00
5c8072cc9e 完善礼包最新页面(未完成), 9.5测试需求 2017-09-06 17:00:59 +08:00
40cd057c28 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-06 16:18:52 +08:00
0bcd37c1f1 礼包最新页面修改实现逻辑(未完成), 9.5测试需求 2017-09-06 14:37:10 +08:00
6fce6fd80b Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-09-06 10:43:50 +08:00
bc13a6056d update gradle.properties to use https 2017-09-06 10:43:13 +08:00
00879964e4 3.0Bug修复/UI优化/礼包优化
暂时未解决:礼包页面切换时,礼包搜索条自动顶上去/LibaoEntity getId和getLibaoId混乱
2017-09-05 11:57:19 +08:00
64711d6175 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev
# Conflicts:
#	app/src/main/java/com/gh/base/AppController.java
2017-09-04 10:54:35 +08:00
0d3704f55d 3.0bug优化,3.0UI问题汇总,3.0礼包问题.部分未完成需要检查 2017-09-04 10:52:21 +08:00
0856eed7fc update lglibrary 2017-09-03 17:13:23 +08:00
8efaa9e724 1、更改build variant,debugCompile,确保正式打包不包含debug类库
2、支持按照debug/release简单区分更多代码逻辑
2017-09-03 17:11:04 +08:00
98983ff4eb 关注操作成功时才改变按钮状态 2017-08-29 11:52:04 +08:00
0ae36ed7a7 完善token重试逻辑和多设备登录 2017-08-28 16:58:39 +08:00
8e8b20c3dd 光环助手V3.0(8月24日)测试包问题汇总 2017-08-25 17:40:17 +08:00
82e2aa45c6 更新登录相关错误码 多设备登录提示优化(没效果) 2017-08-25 10:47:15 +08:00
e4be47e7e7 部分UI调整,优化登录模块 2017-08-23 17:48:42 +08:00
404c605a1e 优化开服表(主要是单个游戏开服点下载是非常卡,修复后还是有点卡),把首页控制台的消息提示红点移到我的光环 2017-08-22 10:51:23 +08:00
4966d85634 新开服表(默认定位未完成,等待根据游戏id获取开服信息接口) 2017-08-20 17:47:53 +08:00
043eebf013 修改获取token逻辑,以及处理token 401问题 2017-08-18 11:25:28 +08:00
3b521ddd08 首页-游戏插件化模块改成可伸缩,user部分接口移到api(关注) 2017-08-16 16:00:42 +08:00
bd74d3a1a3 底部点击回到顶部(首页-游戏),闪退重启出现提示框 2017-08-15 11:30:04 +08:00
0dc867ce64 重做登录页面(普通登录和老用户登录) 2017-08-14 17:19:41 +08:00
dde08e2d67 老用户登录,token过去处理,登录模块优化 2017-08-14 15:17:26 +08:00
3c5871526d 登录签名认证 专题列表开服时间格式更改  收藏优化 2017-08-10 10:20:35 +08:00
49f4ba2da8 同步最后修改2.6代码 2017-08-01 17:10:47 +08:00
77b660576a 登录验证(还不行) 2017-08-01 16:43:59 +08:00
a15ab62f03 收藏相关(文章检查是否修改未完成) 2017-07-28 18:32:29 +08:00
c65250973c fix okhttp retry logic 2017-07-24 16:36:16 +08:00
d5872aad55 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev
# Conflicts:
#	app/src/main/java/com/gh/base/AppController.java
2017-07-23 18:42:41 +08:00
9d27bba6fd 增加流量统计(尚未完善) 2017-07-23 18:36:45 +08:00
caeecce50b add okhttp request retry logic 2017-07-21 19:58:03 +08:00
f6c82dd767 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-07-21 18:21:16 +08:00
f88205f957 消息页面入口移到我的光环 2017-07-21 18:19:59 +08:00
3a0a4726b9 fix host and gradle vars 2017-07-21 10:11:30 +08:00
a113fc1c73 优化登录流程,修复2.6已知的BUG 2017-07-20 18:04:38 +08:00
92d122b0a3 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-07-19 11:24:15 +08:00
d96a2d22e0 增加收藏页面,优化登录流程 2017-07-19 11:21:42 +08:00
8d96b9fe7f add init submodule scripts 2017-07-19 10:25:17 +08:00
fdf7bdd64d 修改用户信息(更换头像未完成) 2017-07-18 15:43:02 +08:00
4f4c90f792 对接测试登录流程(第三方登录以及手机号码登录) 2017-07-18 10:43:38 +08:00
f42674ed6f add dev/public host product flavors 2017-07-14 17:15:05 +08:00
e66b697663 修改域名为ghzs666 2017-07-14 16:45:00 +08:00
a21e40a4f1 Merge branch '3.0' of gitlab.ghzhushou.com:halo/assistant-android into 3.0 2017-07-10 17:03:06 +08:00
03dd58ca41 .. 2017-07-10 17:00:41 +08:00
6f3827db15 Merge branch '3.0' of gitlab.ghzhushou.com:halo/assistant-android into 3.0
# Conflicts:
#	app/src/main/java/com/gh/common/util/GetLoginDataUtils.java
#	app/src/main/java/com/gh/gamecenter/LoginActivity.java
2017-07-10 17:00:22 +08:00
fd2b333bc7 fix weibo webview proguard 2017-07-10 16:37:18 +08:00
ba3e882f47 微信获取登录数据成功回调,登录模块优化 2017-07-10 16:22:13 +08:00
b9a80e12a8 patch viewpagerfragment for onactivityresult callback 2017-07-10 16:07:38 +08:00
979ee209dd Merge branch '3.0' of gitlab.ghzhushou.com:halo/assistant-android into 3.0 2017-07-10 15:01:39 +08:00
10b1380aad 1、下载模块部分重构
2、整理actionbar高度问题,测试toolbar,暂时未启用
3、sharedpreference使用default文件
2017-07-10 14:35:32 +08:00
b239407c25 模拟对接登录接口已经登录流程(未完成) 2017-07-07 19:31:58 +08:00
475221a011 debug和release生成对应域名 2017-07-07 14:36:15 +08:00
658ee78213 分离build/release key 2017-07-07 10:46:23 +08:00
5244fe73ea 1、处理release debug build区分 2017-07-07 10:43:29 +08:00
243e024d1a 修复友盟推送初始化异常,ImageUtils代码转换kotlin 2017-07-06 19:50:40 +08:00
5930996e1c 修复插件平台图片(旧版本)无法更新问题, 优化登录 2017-07-06 09:10:44 +08:00
a05e6917b8 更改弹出软键盘时移动布局方法(防止软键盘挡住布局),以适配个别手机计算错误 2017-07-05 15:09:51 +08:00
d05e9e57c3 实体类全部转换成kotlin 2017-07-04 17:07:12 +08:00
1b88e9c238 ... 2017-07-03 19:51:51 +08:00
ce32b8d63a Merge remote-tracking branch 'origin/3.0' into 3.0 2017-07-03 10:04:15 +08:00
2bc26c9046 接入微信登录, 增加个人信息编辑页面,我的光环增加自动检测更新,登录页面增加新老用户切换 2017-07-03 09:58:54 +08:00
714a64975f 1、对下载器任务的状态变化修改
2、对下载器内部的handler的消息屏蔽,pause的修改,剩余download roll未改动。
2017-07-02 09:49:21 +08:00
7a6354e781 Merge remote-tracking branch 'origin/3.0' into 3.0 2017-06-30 17:30:03 +08:00
5f11c2d0b6 接入微信登录, 增加个人信息编辑页面,我的光环增加自动检测更新,登录页面增加新老用户切换 2017-06-30 17:29:43 +08:00
9c6814482d 1、downloadmangeractivity逻辑改动到viewpager fragment 2017-06-30 11:44:23 +08:00
94a5736939 fix data error in gameentity 2017-06-29 16:36:27 +08:00
bc496d8277 update lib 2017-06-29 16:00:32 +08:00
2b23665cb5 fix entity 2017-06-29 15:59:36 +08:00
1d1c9666dc Merge remote-tracking branch 'origin/3.0' into 3.0 2017-06-29 11:27:21 +08:00
1b14f3f608 微博登录改动 2017-06-29 11:27:07 +08:00
d370e817c0 update lib 2017-06-29 11:25:06 +08:00
ea4c6c95a1 Merge branch '3.0' of gitlab.ghzhushou.com:halo/assistant-android into 3.0
# Conflicts:
#	app/src/main/java/com/gh/gamecenter/adapter/CommentDetailAdapter.java
#	app/src/main/java/com/gh/gamecenter/adapter/MessageDetailAdapter.java
#	app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailAdapter.java
2017-06-29 11:23:47 +08:00
f97ccd5c5d 1、更改部分下载类,未完成
2、@#&@*#
2017-06-29 11:17:01 +08:00
0b2a672f2d Merge remote-tracking branch 'origin/3.0' into 3.0
# Conflicts:
#	app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailAdapter.java
2017-06-28 18:09:08 +08:00
f960b07ee1 涉及到用户操作的加上登录验证, 把评论点赞抽离成工具方法 2017-06-28 18:06:36 +08:00
5cbf06f6a6 1、回退umeng push版本到3.1.1
2、catch umeng初始化。
2017-06-28 11:06:50 +08:00
70ea517a3a update LGLibrary, fix libaodetail entrance 2017-06-28 10:12:48 +08:00
957305d6f5 抽离详情页面下载模块(已完成) 2017-06-28 09:53:03 +08:00
bbd1d429a6 Merge remote-tracking branch 'origin/3.0' into 3.0 2017-06-27 16:31:48 +08:00
56ba47a844 抽离详情页面下载模块(新闻详情已完成) 2017-06-27 16:31:27 +08:00
8101d394a8 fix conflict 2017-06-27 16:28:33 +08:00
496144f8ad 1、统一下载逻辑,下一步准备移动到通用类库
2、修复viewimageactivity npe
2017-06-27 16:27:51 +08:00
4cbee71636 fix submodule 2017-06-26 18:57:09 +08:00
7aed643eba fix conflict 2017-06-26 18:38:16 +08:00
829da52101 更换新版Dialog,微博获取用户信息 2017-06-26 18:27:36 +08:00
9dd449c2ef 1、移动公用文件、样式到libraries/LGLibrary
2、请使用git submodule foreach git pull更新代码
2017-06-26 16:35:36 +08:00
f78bdaa358 mv BaseRecyclerAdapter to lib 2017-06-26 15:16:00 +08:00
16236e1361 remove weibo lib 2017-06-26 15:12:18 +08:00
8e52d45037 fix build script 2017-06-26 14:57:33 +08:00
2246670487 1、移除libraries/LGLibrary,使用submodule替代,具体使用方式参考README.md
2、更新readme.md的说明
3、添加CHANGELOG.md文件,用于保存版本更新记录
2017-06-26 14:48:16 +08:00
cde325198e 登录模块界面 2017-06-26 09:20:08 +08:00
faa0710fd5 ... 2017-06-22 16:28:54 +08:00
d1bb27ad6b 雷达扫描动画GIF 用自定义View实现 2017-06-22 16:28:08 +08:00
8f1f679077 修改代码遗留问题 2017-06-22 15:05:11 +08:00
b632cbba0f 命名规则 2017-06-22 11:08:22 +08:00
de438e90c4 大于两个的if分支,使用switch 2017-06-21 20:54:12 +08:00
7a3b1d4dbe viewpager滑动时,tab无法同步问题 2017-06-21 17:17:35 +08:00
44e9b4da4d Activity 统一入口 2017-06-21 12:03:27 +08:00
559710b4b4 部分Intent.putExtras参数传递实体类 用Parcelable来处理, 修复游戏插件下载闪退 2017-06-20 18:29:49 +08:00
a05c5b4976 登录页面 2017-06-20 09:30:49 +08:00
51cfe767ee 合并冲突 2017-06-19 11:30:25 +08:00
0891e78b8d Merge remote-tracking branch 'origin/3.0' into 3.0
# Conflicts:
#	app/src/main/java/com/gh/gamecenter/personal/PersonalFragment.java
2017-06-19 11:06:16 +08:00
5c834ec09c gradle引入weiboSDK,接入第三点登录(QQ、微信、微博(未完善)), 我的光环改版 2017-06-19 11:05:16 +08:00
fc5b6ae4e4 1、处理viewpager fragment
2、将MainActivity管理的逻辑移到MainFragment,MessageFragment,NewsFragment同理,
3、处理首页ViewPager等手动计算size的问题
4、添加一堆ScaleXXXView,可以用来设定View本身的比例大小

TODO
1、将DownloadManagerActivity也处理成Fragment
2、其他界面也慢慢处理成Fragment
2017-06-17 15:52:42 +08:00
5e668d9e13 fix deprecated 2017-06-16 16:12:28 +08:00
b3d63c5698 1、处理viewholder
2、处理adapter
3、
2017-06-16 15:39:45 +08:00
079c160268 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-06-16 14:11:25 +08:00
f5425c2bea 1、统一所有fragment初始化流程,继承自BaseFragment
2、统一大部分viewholder
3、TODO fragment adapter viewholder的分包还没处理完成
2017-06-16 14:11:18 +08:00
8c4967abc4 Merge remote-tracking branch 'origin/dev' into dev 2017-06-15 17:28:03 +08:00
8dd069b38d 切换正式接口 2017-06-15 17:27:49 +08:00
1160fb999e 修改mta接入,然而貌似并没有什么卵用
禁用tcagent的uncaught exception 上报
2017-06-15 17:24:16 +08:00
948329f79e 0614小问题优化 2017-06-14 20:02:12 +08:00
a61ef37a22 Merge remote-tracking branch 'origin/dev' into dev 2017-06-14 18:23:10 +08:00
66e8de06f0 gif移到assets 2017-06-14 18:22:26 +08:00
b5c3daea0c fix conflict 2017-06-14 18:20:08 +08:00
ba70ecc063 1、修复验证码出现的各种奇葩问题和崩溃,传入activity作为显示,但不持有引用
2、增加重试机制,3次获取不成功才失败。
3、修复okhttp Response返回非HTTPException导致的NPE
2017-06-14 18:19:25 +08:00
3b1145133a 1、修复验证码textview 初始化崩溃的bug
2、修复验证码返回时activity 已被销毁仍然弹窗导致的崩溃
2017-06-14 16:46:38 +08:00
3e0e4caa0c 专题头图增加跳转 2017-06-14 11:22:41 +08:00
a55a969588 修复部分bug 2017-06-13 09:25:40 +08:00
8b46435808 增加eventbus混淆 2017-06-12 15:08:48 +08:00
1a6966e522 EventBus版本升级(旧版的lib未删) 2017-06-09 16:08:19 +08:00
2fcee4cea0 优化代码,删除无用文件 2017-06-08 14:55:29 +08:00
223b7c6069 修改平铺专题实现方法, 修复资讯关注推荐小板块BUG,版本升至2.6 2017-06-07 10:25:30 +08:00
771946918a 工具想搜索关注的游戏优先, 开服表修改显示逻辑 2017-06-05 11:20:06 +08:00
d7e113108b 修复部分BUG,工具箱增加搜索功能 2017-05-31 20:39:18 +08:00
e3d9fac1b0 修改游戏详情协调布局的实现方法 2017-05-28 18:36:58 +08:00
7c78109bf2 整理专题合并,首页游戏列表和专题列表增加游戏标签 2017-05-28 16:10:33 +08:00
b7ccf66418 合并专题Acticity 2017-05-28 15:08:50 +08:00
8c5265f9b5 对接工具箱接口,工具箱详情增加分享功能 2017-05-27 16:46:02 +08:00
e81bbf5195 增加平铺游戏专题(缺少数据借口) 2017-05-26 11:44:39 +08:00
f219b62fcc 工具箱(为对接数据接口),解决footeritem高度不一问题和开服表闪退问题 2017-05-25 09:54:21 +08:00
6347260522 修复模拟器闪退问题,增加DEBUG推送appid和appkey 2017-05-24 10:13:27 +08:00
9fe80cfb66 Merge remote-tracking branch 'origin/dev' into dev 2017-05-23 14:35:05 +08:00
62975e5e1b .... 2017-05-23 14:34:56 +08:00
9b4e62149f add umeng debug 2017-05-23 14:30:15 +08:00
8643a12744 new Thread用RxJava或者线程池代替 2017-05-22 09:07:18 +08:00
9369218821 reformat code & optimize import & rearrange code 2017-05-19 10:54:56 +08:00
0c1b48cee2 规范部分Thread 2017-05-19 10:47:43 +08:00
5865fdbd23 调整界面 2017-05-19 09:23:09 +08:00
6b5fe82d47 1、修复url匹配正则
2、部分onclick处理
3、代码整理
2017-05-18 12:05:25 +08:00
49624d5afd 修改游戏详情插件介绍实现方法(页面视图在XML创建,解决展开时位置移动) 2017-05-18 11:49:55 +08:00
a92de9ffa0 切换接口 2017-05-17 19:52:01 +08:00
d4133cc51d Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	app/src/main/java/com/gh/gamecenter/SuggestionActivity.java
2017-05-17 18:35:37 +08:00
611068a9b0 修复意见反馈提示顺序混乱问题 2017-05-17 18:32:51 +08:00
7930ecaea9 fix entrance 2017-05-17 18:28:26 +08:00
3c72080802 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-05-17 18:19:03 +08:00
4c762a1aa8 1、统一了entrance
2、统一了部分跳转intent,传参返回intent
3、修改出dialogfragment
4、clipboardmanager等处理
2017-05-17 18:18:46 +08:00
4f99477d83 修改游戏详情空白页优化, 开服表修改部分显示规则和增加滑动动画 2017-05-17 14:47:00 +08:00
6d4de6795e 优化汇总(20170513)补充 2017-05-16 17:43:59 +08:00
4f8b93cb6a 优化代码 2017-05-15 18:21:07 +08:00
48a4aefe96 Merge remote-tracking branch 'origin/dev' into dev 2017-05-14 17:54:02 +08:00
dc530be9be V2.5优化汇总(20170513) 2017-05-14 17:53:36 +08:00
bb5e9e739b fix td npe 2017-05-12 15:53:43 +08:00
7264249ef8 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-05-12 15:46:57 +08:00
5eb5f6807e 1、使用新的渠道打包方案,将渠道值写入到已经签名的apk上面,具体参考README.md
2、将各种第三方工具使用的appid、key和渠道值改成用gradle.properties的配置方式
2017-05-12 15:46:46 +08:00
8bb50a6e2c 游戏详情部分位置和显示方式修改 2017-05-12 11:39:31 +08:00
7b7987b574 部分代码整理 2017-05-11 14:55:54 +08:00
a209edc181 Merge remote-tracking branch 'origin/dev' into dev 2017-05-10 18:24:19 +08:00
e744a475c3 插件包增加隐藏状态(只能识别到插件化) 2017-05-10 18:24:02 +08:00
b280e1bf89 add shrinkresources & resconfig in order to reduce apk size 2017-05-10 16:26:55 +08:00
decb763a5f filter unused abi 2017-05-10 16:07:40 +08:00
28f685ae10 fix wrong arg in compress_resources.sh 2017-05-10 15:45:16 +08:00
6270d59f3f Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-05-10 15:23:02 +08:00
11c6f33392 1、升级support v4 系列到24.1.0
2、增加混淆配置,release默认启用
2017-05-10 15:22:50 +08:00
a75b613744 Merge remote-tracking branch 'origin/dev' into dev 2017-05-09 18:25:06 +08:00
ccca070dd2 修改礼包倒数领取规则 2017-05-09 18:24:45 +08:00
bd2ce2e82d 1、升级umeng push到3.1.1a,解决通知栏持续运行中的问题
2、fix game adapter
2017-05-09 18:17:28 +08:00
a88c920ef0 Merge remote-tracking branch 'origin/dev' into dev 2017-05-08 18:37:07 +08:00
6aee2ae6b9 ProgressBar固定样式(防止5.0以上样式改变),修改SwitchButton样式 2017-05-08 18:36:45 +08:00
0ec14da2ac 1、修复AppController push被禁用的问题
2、修复deprecated api call, Resources.getColor()
2017-05-08 17:41:58 +08:00
375f538bc1 1、fix skipactivity/baseappcompatactivity potential npe
2、fix appcontroller logic
2017-05-08 15:22:57 +08:00
1c6907373b 直接导入的swipelayout无法适应需求,直接源码导入进行修改 2017-05-08 14:58:53 +08:00
64fe9048f0 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	app/src/main/java/com/gh/gamecenter/LibaoDetailActivity.java
2017-05-05 18:25:14 +08:00
c96193b3d0 代码整理 2017-05-05 18:24:37 +08:00
b1035c24bc resolve conflict 2017-05-05 18:21:21 +08:00
d69e75480e 1、将所有Activity统一到一个base(主题AppCompatTheme),layout和contentView统一处理
2、MainActivity tab切换方式的重构
3、下一步更改toolbar实现方式,然后再是尽量用fragment替换
2017-05-05 18:12:51 +08:00
4da12055e9 优化字符串叠加 2017-05-05 17:11:05 +08:00
7ca9272fe7 提取activity title到strings.xml 2017-05-05 10:23:13 +08:00
5f0bf827ae 游戏专题自动获取专题名称 2017-05-04 16:21:25 +08:00
87cd020e94 调整资讯位置 2017-05-04 15:15:44 +08:00
4fd7095fbf Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.java
2017-05-04 14:44:24 +08:00
c1ebcf33e5 引入design包,游戏专题改版 2017-05-04 14:42:36 +08:00
d0f72a8d96 1、修改autoscrollviewpager/swipelayout为gradle引用
2、添加google iosched部分ui类
2017-05-04 11:59:54 +08:00
6e5ac76b52 Merge branch 'dev' of gitlab.ghzhushou.com:halo/assistant-android into dev 2017-05-04 11:49:03 +08:00
8ae3b93307 文章详情webview链接跳转修改 2017-05-04 11:48:34 +08:00
7246e06973 Merge remote-tracking branch 'origin/dev' into dev 2017-05-03 16:44:04 +08:00
0ae824b9f5 合并 2017-05-03 16:43:15 +08:00
deee22b6db Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	app/src/main/java/com/gh/gamecenter/CleanApkActivity.java
#	app/src/main/java/com/gh/gamecenter/MessageDetailActivity.java
#	app/src/main/java/com/gh/gamecenter/VoteActivity.java
#	app/src/main/java/com/gh/gamecenter/adapter/LibaoDetailAdapter.java
#	app/src/main/java/com/gh/gamecenter/adapter/MessageDetailAdapter.java
#	app/src/main/java/com/gh/gamecenter/adapter/VoteAdapter.java
#	app/src/main/java/com/gh/gamecenter/libao/Libao1FragmentAdapter.java
#	app/src/main/java/com/gh/gamecenter/libao/Libao2FragmentAdapter.java
#	app/src/main/java/com/gh/gamecenter/libao/Libao3FragmentAdapter.java
#	app/src/main/java/com/gh/gamecenter/message/CommentFragmentAdapter.java
#	app/src/main/java/com/gh/gamecenter/news/News1FragmentAdapter.java
2017-05-03 16:32:10 +08:00
c8e01182f4 1、fix splashscreen test
2、fix multidex lib
3、add stetho
2017-05-03 16:14:49 +08:00
dec6bd6779 产品优化汇总 2017-05-03 16:09:14 +08:00
b00b791107 启动屏幕部分逻辑修改 2017-05-02 17:15:37 +08:00
4db2b1cc29 retrofit2.adapter.rxjava.HttpException修改为retrofit2.HttpException 2017-05-02 10:44:23 +08:00
471ebb3b6e icon-default问题修改 2017-05-02 10:39:10 +08:00
e057b6530b confirem修改为confirm 2017-05-02 10:28:04 +08:00
b5328324f8 产品测试汇总 2017-04-29 17:25:37 +08:00
18aa853948 fix color back 2017-04-28 16:37:08 +08:00
c8559bd36a Merge branch 'dev' of git.oschina.net:dreamhua/GH-ASSISTv1.45 into dev 2017-04-28 16:13:44 +08:00
b71e717da1 1、提取xml的字符串和部分java文件的字符串到strings.xml
2、提取color到colors.xml
2017-04-28 16:13:32 +08:00
60677b36f6 沉浸栏颜色修改 2017-04-28 14:32:42 +08:00
339d32b1dd 推荐位数据收集修改 2017-04-28 11:53:11 +08:00
9432e34571 reformat code & optimize import & reorder entries 2017-04-27 14:27:17 +08:00
153f33f44b 1、所有引用整理到从dependencies.gradle文件,部分库做了升级
2、添加httplogginginteceptor记录网络请求情况
2017-04-27 14:15:05 +08:00
fc2f20ee6e Merge branch 'dev' of https://git.oschina.net/dreamhua/GH-ASSISTv1.45 into dev
Conflicts:
	app/src/main/java/com/gh/gamecenter/MainActivity.java
2017-04-26 15:59:44 +08:00
cfa70feac5 应用上报 2017-04-26 15:56:19 +08:00
6a851f7247 应用数据上报 2017-04-26 15:55:53 +08:00
khy
a0cae1b7b8 游戏详情接入游戏推荐(大家都在玩),优化代码 2017-04-26 15:34:58 +08:00
khy
5798ba9e89 Merge remote-tracking branch 'origin/dev' into dev 2017-04-25 19:17:20 +08:00
khy
919e3c386d 增加MessageService(更改域名), 取消显示公告 2017-04-25 19:16:58 +08:00
e85bb40904 Merge remote-tracking branch 'origin/dev' into dev 2017-04-25 18:45:28 +08:00
d23a2df809 1、systembar tint/switchbutton/mipush做了更新整理
2、部分跳转常量整理
3、工程结构整理,基本国内几个sdk解决完了
2017-04-25 18:45:10 +08:00
khy
b9185cce15 合并整理 2017-04-25 14:53:57 +08:00
khy
c14605b598 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	app/src/main/java/com/gh/gamecenter/adapter/PlatformAdapter.java
#	app/src/main/java/com/gh/gamecenter/adapter/VoteAdapter.java
2017-04-25 14:35:03 +08:00
khy
68c2f3c754 对接求版本投票数据接口, 礼包重复领取优化 2017-04-25 14:29:58 +08:00
2725a59826 基本处理完成recyclerview adapter 2017-04-25 13:16:16 +08:00
khy
9ef8f5d1f2 Merge remote-tracking branch 'origin/dev' into dev 2017-04-25 10:17:18 +08:00
khy
39cd66032d 修改Handler 在 ondestroy时 remove全部消息,Context获取的Manager,改为Application Context持有,游戏列表增加开服信息 2017-04-24 19:14:47 +08:00
ee53e7218f add so files. 2017-04-24 18:49:17 +08:00
72a1cded27 fix so libs 2017-04-21 18:08:40 +08:00
3295b6a7b7 fix gradle version 2017-04-21 17:45:13 +08:00
4f64325f2b fix conflict 2017-04-21 17:08:36 +08:00
de9c9cbdcb 整理工程结构,library配置未完全抽离出来 2017-04-21 16:59:29 +08:00
khy
1fe9e259b4 消息页面对接数据接口, 礼包重复领取,删除无用文件 2017-04-21 16:32:44 +08:00
6b4f68128b fix gitignore 2017-04-21 14:34:12 +08:00
khy
8b637fbeec ... 2017-04-14 16:36:22 +08:00
khy
7267535ccf Merge remote-tracking branch 'origin/2.5' into 2.5
# Conflicts:
#	app/src/main/java/com/gh/gamecenter/game/GameFragmentAdapter.java
2017-04-14 16:15:19 +08:00
khy
8f99987696 游戏求版本功能(未对接接口), 文章详情优化(文章内容跳转未做),消息中心(未接接口),首页资讯改版,跳转意见反馈规则汇总 2017-04-14 16:07:36 +08:00
khy
515473e5dc 新版游戏详情,我的光环, 反馈页面。 专题增加标签 2017-04-02 17:18:00 +08:00
8b83e939ce 搜索数据收集 2017-03-28 16:44:00 +08:00
c71bd36de2 推荐位数据收集 2017-03-28 14:57:51 +08:00
9fdc8f0da3 修改版本为2.5,版本号20 2017-03-28 14:57:08 +08:00
khy
8ba099024e 去卡片化, 开服表, 首页游戏-资讯改版,游戏专题增加标签(还差一个接口) 2017-03-22 15:25:05 +08:00
1153 changed files with 69670 additions and 70973 deletions

14
.gitignore vendored
View File

@ -1,9 +1,9 @@
/.idea
.idea/misc.xml
.idea/
*.iml
.gradle
/local.properties
.gradle/
local.properties
# sign.properties
.DS_Store
/build
/captures
/PushSDK/build
captures/
build/
release-app/

4
.gitmodules vendored Normal file
View File

@ -0,0 +1,4 @@
[submodule "libraries/LGLibrary"]
path = libraries/LGLibrary
url = git@gitlab.ghzhushou.com:client/client-common.git
branch = master

15
CHANGELOG.md Normal file
View File

@ -0,0 +1,15 @@
### Ver 3.0
* 升级账号系统(登录流程/用户信息相关/用户账号相关操作(评论,礼包...))
* 新增收藏功能(文章/工具箱)
* 删除用户相关的所有本地数据库
* 重做总开服表
* 重做首页插件化模块
* 礼包重复领取机制改变(可重复领取的礼包,领取后立刻显示再领一个/再淘一个)
* 游戏下载平台面板修改(加快弹出速度,不再读取本地平台图片)
* 接入bugly(tinker)
### Ver 2.6
* xx
### Ver 2.5
* 此处写本次更新所做的业务和代码修改

View File

@ -1,147 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.umeng.message.lib"
android:versionCode="1"
android:versionName="1.0">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.BROADCAST_PACKAGE_ADDED" />
<uses-permission android:name="android.permission.BROADCAST_PACKAGE_CHANGED" />
<uses-permission android:name="android.permission.BROADCAST_PACKAGE_INSTALL" />
<uses-permission android:name="android.permission.BROADCAST_PACKAGE_REPLACED" />
<uses-permission android:name="android.permission.RESTART_PACKAGES" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<application>
<service
android:name="com.taobao.accs.ChannelService"
android:exported="true"
android:process=":channel">
<intent-filter>
<action android:name="com.taobao.accs.intent.action.SERVICE" />
</intent-filter>
<intent-filter>
<action android:name="com.taobao.accs.intent.action.ELECTION" />
</intent-filter>
</service>
<service
android:name="com.taobao.accs.data.MsgDistributeService"
android:exported="true">
<intent-filter>
<action android:name="com.taobao.accs.intent.action.RECEIVE" />
</intent-filter>
</service>
<receiver
android:name="com.taobao.accs.EventReceiver"
android:process=":channel">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
<receiver
android:name="com.taobao.accs.ServiceReceiver"
android:exported="false"
android:process=":channel">
<intent-filter>
<action android:name="com.taobao.accs.intent.action.COMMAND" />
</intent-filter>
<intent-filter>
<action android:name="com.taobao.accs.intent.action.START_FROM_AGOO" />
</intent-filter>
</receiver>
<service
android:name="com.taobao.accs.ChannelService$KernelService"
android:process=":channel">
</service>
<service
android:name="org.android.agoo.accs.AgooService"
android:exported="true">
<intent-filter>
<action android:name="com.taobao.accs.intent.action.RECEIVE" />
</intent-filter>
</service>
<service android:name="com.umeng.message.UmengIntentService"
android:exported="true"
android:process=":channel">
<intent-filter>
<action android:name="org.agoo.android.intent.action.RECEIVE" />
</intent-filter>
</service>
<receiver
android:name="com.taobao.agoo.AgooCommondReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.gh.gamecenter.intent.action.COMMAND" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<receiver
android:name="com.umeng.message.NotificationProxyBroadcastReceiver"
android:exported="false" />
<service
android:name="com.umeng.message.UmengMessageCallbackHandlerService"
android:exported="false">
<intent-filter>
<action android:name="com.umeng.messge.registercallback.action" />
</intent-filter>
<intent-filter>
<action android:name="com.umeng.message.enablecallback.action" />
</intent-filter>
<intent-filter>
<action android:name="com.umeng.message.disablecallback.action" />
</intent-filter>
<intent-filter>
<action android:name="com.umeng.message.message.handler.action" />
</intent-filter>
</service>
<service
android:name="com.umeng.message.UmengDownloadResourceService"
android:exported="false" />
<service
android:name="com.umeng.message.UmengMessageIntentReceiverService"
android:exported="true"
android:process=":channel" >
<intent-filter>
<action android:name="org.android.agoo.client.MessageReceiverService" />
</intent-filter>
</service>
<provider
android:name="com.umeng.message.provider.MessageProvider"
android:authorities="com.gh.gamecenter.umeng.message"
android:exported="false">
<grant-uri-permission android:pathPattern=".*" />
</provider>
</application>
</manifest>

View File

@ -1,51 +0,0 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 22
buildToolsVersion "23.0.2"
defaultConfig {
minSdkVersion 11
targetSdkVersion 22
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
debug {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
// Move the tests to tests/java, tests/res, etc...
instrumentTest.setRoot('tests')
// Move the build types to build-types/<type>
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
// This moves them out of them default location under src/<type>/... which would
// conflict with src/ being used by the main source set.
// Adding new build types or product flavors should be accompanied
// by a similar customization.
debug.setRoot('build-types/debug')
release.setRoot('build-types/release')
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}

View File

@ -1,156 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.umeng.message.lib.test" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="22" />
<instrumentation
android:name="android.test.InstrumentationTestRunner"
android:functionalTest="false"
android:handleProfiling="false"
android:label="Tests for com.umeng.message.lib.test"
android:targetPackage="com.umeng.message.lib.test" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.BROADCAST_PACKAGE_ADDED" />
<uses-permission android:name="android.permission.BROADCAST_PACKAGE_CHANGED" />
<uses-permission android:name="android.permission.BROADCAST_PACKAGE_INSTALL" />
<uses-permission android:name="android.permission.BROADCAST_PACKAGE_REPLACED" />
<uses-permission android:name="android.permission.RESTART_PACKAGES" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<application>
<uses-library android:name="android.test.runner" />
<service
android:name="com.taobao.accs.ChannelService"
android:exported="true"
android:process=":channel" >
<intent-filter>
<action android:name="com.taobao.accs.intent.action.SERVICE" />
</intent-filter>
<intent-filter>
<action android:name="com.taobao.accs.intent.action.ELECTION" />
</intent-filter>
</service>
<service
android:name="com.taobao.accs.data.MsgDistributeService"
android:exported="true" >
<intent-filter>
<action android:name="com.taobao.accs.intent.action.RECEIVE" />
</intent-filter>
</service>
<receiver
android:name="com.taobao.accs.EventReceiver"
android:process=":channel" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT" />
</intent-filter>
</receiver>
<receiver
android:name="com.taobao.accs.ServiceReceiver"
android:exported="false"
android:process=":channel" >
<intent-filter>
<action android:name="com.taobao.accs.intent.action.COMMAND" />
</intent-filter>
<intent-filter>
<action android:name="com.taobao.accs.intent.action.START_FROM_AGOO" />
</intent-filter>
</receiver>
<service
android:name="com.taobao.accs.ChannelService$KernelService"
android:process=":channel" >
</service>
<service
android:name="org.android.agoo.accs.AgooService"
android:exported="true" >
<intent-filter>
<action android:name="com.taobao.accs.intent.action.RECEIVE" />
</intent-filter>
</service>
<service
android:name="com.umeng.message.UmengIntentService"
android:exported="true"
android:process=":channel" >
<intent-filter>
<action android:name="org.agoo.android.intent.action.RECEIVE" />
</intent-filter>
</service>
<receiver
android:name="com.taobao.agoo.AgooCommondReceiver"
android:exported="true" >
<intent-filter>
<action android:name="com.gh.gamecenter.intent.action.COMMAND" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<receiver
android:name="com.umeng.message.NotificationProxyBroadcastReceiver"
android:exported="false" />
<service
android:name="com.umeng.message.UmengMessageCallbackHandlerService"
android:exported="false" >
<intent-filter>
<action android:name="com.umeng.messge.registercallback.action" />
</intent-filter>
<intent-filter>
<action android:name="com.umeng.message.enablecallback.action" />
</intent-filter>
<intent-filter>
<action android:name="com.umeng.message.disablecallback.action" />
</intent-filter>
<intent-filter>
<action android:name="com.umeng.message.message.handler.action" />
</intent-filter>
</service>
<service
android:name="com.umeng.message.UmengDownloadResourceService"
android:exported="false" />
<service
android:name="com.umeng.message.UmengMessageIntentReceiverService"
android:exported="true"
android:process=":channel" >
<intent-filter>
<action android:name="org.android.agoo.client.MessageReceiverService" />
</intent-filter>
</service>
<provider
android:name="com.umeng.message.provider.MessageProvider"
android:authorities="com.gh.gamecenter.umeng.message"
android:exported="false" >
<grant-uri-permission android:pathPattern=".*" />
</provider>
</application>
</manifest>

View File

@ -1,12 +0,0 @@
# This file is automatically generated by Android Tools.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
#
# This file must be checked in Version Control Systems.
#
# To customize properties used by the Ant build system use,
# "ant.properties", and override values to adapt the script to your
# project structure.
# Project target.
target=android-19
android.library=true

40
README.md Normal file
View File

@ -0,0 +1,40 @@
# 光环助手Android客户端
### Sourceset/debug/release
* https://developer.android.com/studio/build/build-variants.html#sourcesets
### 多渠道打包配置
* 使用[ApkChannelPackage](https://github.com/ltlovezh/ApkChannelPackage)的方案
* 正式打包命令:请使用./gradlew channelPubRelease打包渠道包
### 混淆配置
* 配置文件Android默认配置+proguard-rules.txt等
* 参考libraries下每个项目独立的配置文件``proguard-project.txt``
### apk大小优化
* 限制resConfig资源集
* 开启ShrinkResources
* 开启混淆使用minifyEnabled(仅在release开启
* pngquant对png压缩、png/jpg->webp(未尝试)
### 第三方appkey等配置
* 修改``gradle.properties``文件将各种key填入其中实现统一管理
* 通过gradle文件内的resValue/buildConfigField/manifestPlaceHolder方式实现编译期间修改具体情况请参考``./build.gradle``和``./app/build.gradle``配置
### 拉取代码步骤
1. 拉取主项目代码: `git clone -b dev git@gitlab.ghzhushou.com:halo/assistant-android.git`
2. 初始化公用类库: `bash ./scripts/init_submodules.sh`
### submodule管理方式(只拉取master)
* 提交代码需要cd到submodule文件夹去做修改
### TODO
* GSON 序列化用统一的一个, GsonUtil fromJson
* CleanApkAdapter 转化字符串size工具函数 比如SpeedUtils
* getString 解决 字符串hardcode问题
* Adapter 里面clicklistener 用接口传参将点击操作委托给controller
* Adapter ViewHolder的功能部分重写到ViewHolder类本身
* activity 统一入口未完成(外部入口相关)

1
app/.gitignore vendored
View File

@ -1 +0,0 @@
/build

View File

@ -1,153 +1,267 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' // kotlin
// apkChannelPackage
apply plugin: 'channel'
apply from: 'tinker-support.gradle'
android {
compileSdkVersion 21
buildToolsVersion "23.0.3"
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
dexOptions {
jumboMode = true
}
/**
* 定位编译出错的图片
*/
aaptOptions.cruncherEnabled = false
aaptOptions.useNewCruncher = false
defaultConfig {
applicationId "com.gh.gamecenter"
minSdkVersion 14
targetSdkVersion 21
versionCode 19
versionName "2.4"
// multiDexEnabled true
multiDexEnabled true
javaCompileOptions {
annotationProcessorOptions {
arguments = [eventBusIndex: 'com.gh.EventBusIndex']
}
}
/**
* 只支持两种架构减少apk大小有疑问请参考
* https://developer.android.com/ndk/guides/abis.html
* http://allenfeng.com/2016/11/06/what-you-should-know-about-android-abi-and-so/
* 为了性能考虑armeabi可以考虑替换成armeabi-v7a[需要先收集用户设备情况]
*/
ndk {
abiFilters "armeabi", "x86"
}
// 由于app只针对中文用户所以仅保留zh资源其他删掉
resConfigs "zh"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
applicationId rootProject.ext.applicationId
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-fresco.txt'
/**
* All third-party appid/appkey
*/
buildConfigField "String", "WECHAT_APPID", "\"${WECHAT_APPID}\""
buildConfigField "String", "WECHAT_SECRET", "\"${WECHAT_SECRET}\""
buildConfigField "String", "TENCENT_APPID", "\"${TENCENT_APPID}\""
buildConfigField "String", "WEIBO_APPKEY", "\"${WEIBO_APPKEY}\""
buildConfigField "String", "MTA_APPKEY", "\"${MTA_APPKEY}\""
buildConfigField "String", "TD_APPID", "\"${TD_APPID}\""
buildConfigField "String", "PATCH_VERSION_NAME", "\"${PATCH_VERSION_NAME}\""
}
/**
* 签名设置
*/
// gradle 2.2以上默认同时启用v1和v2优先用于Android N
signingConfigs {
debug {
v1SigningEnabled true
v2SigningEnabled true
}
release {
storeFile file("gh.keystore")
keyAlias "gh.keystore"
keyPassword "20150318"
storePassword "20150318"
v1SigningEnabled true
v2SigningEnabled true
}
}
buildTypes {
debug {
debuggable true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
zipAlignEnabled false
versionNameSuffix "-debug"
signingConfig signingConfigs.debug
buildConfigField "String", "UMENG_APPKEY", "\"${DEBUG_UMENG_APPKEY}\""
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${DEBUG_UMENG_MESSAGE_SECRET}\""
buildConfigField "String", "MIPUSH_APPID", "\"${DEBUG_MIPUSH_APPID}\""
buildConfigField "String", "MIPUSH_APPKEY", "\"${DEBUG_MIPUSH_APPKEY}\""
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
debuggable false
minifyEnabled true
zipAlignEnabled true
shrinkResources true
signingConfig signingConfigs.release
buildConfigField "String", "UMENG_APPKEY", "\"${UMENG_APPKEY}\""
buildConfigField "String", "UMENG_MESSAGE_SECRET", "\"${UMENG_MESSAGE_SECRET}\""
buildConfigField "String", "MIPUSH_APPID", "\"${MIPUSH_APPID}\""
buildConfigField "String", "MIPUSH_APPKEY", "\"${MIPUSH_APPKEY}\""
}
}
// sourceSets {
// main {
// jniLibs.srcDirs = ['libs']
// }
// }
flavorDimensions "nonsense"
/**
* 多渠道打包
* 多渠道打包,渠道请参考"channel.txt"文件所有渠道值均通过java code设置
*/
productFlavors {
GH_100 {}
GH_101 {}
GH_102 {}
GH_103 {}
GH_104 {}
GH_106 {}
GH_107 {}
GH_108 {}
GH_109 {}
GH_110 {}
GH_111 {}
GH_113 {}
GH_114 {}
GH_115 {}
GH_116 {}
GH_117 {}
GH_118 {}
GH_119 {}
GH_120 {}
GH_121 {}
GH_123 {}
GH_127 {}
GH_200 {}
GH_201 {}
GH_202 {}
GH_203 {}
GH_204 {}
GH_205 {}
GH_222 {}
GH_307 {}
GH_TEST {}
}
productFlavors.all { flavor ->
flavor.manifestPlaceholders = [CHANNEL_VALUE: name]//命令 gradlew assembleRelease
// publish release host
publish {
dimension "nonsense"
buildConfigField "String", "API_HOST", "\"${API_HOST}\""
buildConfigField "String", "USER_HOST", "\"${USER_HOST}\""
buildConfigField "String", "COMMENT_HOST", "\"${COMMENT_HOST}\""
buildConfigField "String", "LIBAO_HOST", "\"${LIBAO_HOST}\""
buildConfigField "String", "MESSAGE_HOST", "\"${MESSAGE_HOST}\""
buildConfigField "String", "DATA_HOST", "\"${DATA_HOST}\""
buildConfigField "String", "USERSEA_HOST", "\"${USERSEA_HOST}\""
buildConfigField "String", "BUGLY_APPID", "\"${BUGLY_APPID}\""
buildConfigField "String", "USERSEA_APP_ID", "\"${USERSEA_APP_ID}\""
buildConfigField "String", "USERSEA_APP_SECRET", "\"${USERSEA_APP_SECRET}\""
}
// internal test dev host
internal {
dimension "nonsense"
buildConfigField "String", "API_HOST", "\"${DEV_API_HOST}\""
buildConfigField "String", "USER_HOST", "\"${DEV_USER_HOST}\""
buildConfigField "String", "COMMENT_HOST", "\"${DEV_COMMENT_HOST}\""
buildConfigField "String", "LIBAO_HOST", "\"${DEV_LIBAO_HOST}\""
buildConfigField "String", "MESSAGE_HOST", "\"${DEV_MESSAGE_HOST}\""
buildConfigField "String", "DATA_HOST", "\"${DEV_DATA_HOST}\""
buildConfigField "String", "USERSEA_HOST", "\"${DEV_USERSEA_HOST}\""
buildConfigField "String", "BUGLY_APPID", "\"${DEBUG_BUGLY_APPID}\""
buildConfigField "String", "USERSEA_APP_ID", "\"${DEV_USERSEA_APP_ID}\""
buildConfigField "String", "USERSEA_APP_SECRET", "\"${DEV_USERSEA_APP_SECRET}\""
}
}
// productFlavors.all { flavor ->
// flavor.manifestPlaceholders = [CHANNEL_VALUE: name]//命令 gradlew assembleRelease
// }
}
//butterknife
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
// apkChannelPackage
channel {
//多渠道包的输出目录默认为new File(project.buildDir,"channel")
baseOutputDir = new File(project.buildDir, "channel")
//多渠道包的命名规则,默认为:${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}
apkNameFormat = '${appName}-${versionName}-${versionCode}-${flavorName}-${buildType}'
}
rebuildChannel {
// baseDebugApk = 已有Debug APK
// baseReleaseApk = 已有Release APK
// //默认为new File(project.buildDir, "rebuildChannel/debug")
// debugOutputDir = Debug渠道包输出目录
// //默认为new File(project.buildDir, "rebuildChannel/release")
// releaseOutputDir = Release渠道包输出目录
}
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:21.0.0'
// compile 'com.android.support:cardview-v7:21.0.0'
// fresco图片框架
compile 'com.facebook.fresco:fresco:0.12.0'
compile 'com.facebook.fresco:animated-gif:0.12.0'
// Retrofit2所需要的包
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
// okhttp
compile 'com.squareup.okhttp3:okhttp:3.2.0'
// ConverterFactory的Gson依赖包
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
// ConverterFactory的String依赖包
// compile 'com.squareup.retrofit2:converter-scalars:2.0.0-beta4'
// ConverterFactory的RxJava依赖包
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
// gson
compile 'com.google.code.gson:gson:2.8.0'
// OrmLite数据库
compile 'com.j256.ormlite:ormlite-android:5.0'
compile 'com.j256.ormlite:ormlite-core:5.0'
// butterknife
compile 'com.jakewharton:butterknife:8.4.0'
apt 'com.jakewharton:butterknife-compiler:8.4.0'
// RxJava && RxAndroid
compile 'io.reactivex:rxandroid:1.1.0'
compile 'io.reactivex:rxjava:1.1.0'
// RxBinding
compile 'com.jakewharton.rxbinding:rxbinding:0.3.0'
// compile 'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.3.0'
// compile 'com.jakewharton.rxbinding:rxbinding-design:0.3.0'
//添加友盟依赖工程
compile project(':PushSDK')
// zxing 二维码扫描以及生成
compile 'com.google.zxing:core:3.2.1'
compile 'com.google.zxing:android-core:3.2.1'
//tinker
// compile("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}") { changing = true }
// compile "com.android.support:multidex:1.0.1"
}
// 依赖插件脚本
//apply from: 'tinker-support.gradle'
implementation fileTree(include: '*.jar', dir: 'libs')
debugImplementation "com.squareup.leakcanary:leakcanary-android:${leakcanary}"
debugImplementation "com.facebook.stetho:stetho:${stetho}"
debugImplementation "com.facebook.stetho:stetho-okhttp3:${stetho}"
debugImplementation "com.squareup.okhttp3:logging-interceptor:${okHttp}"
implementation "com.android.support:multidex:${multidex}"
implementation "com.android.support:design:${androidSupport}"
implementation "com.android.support:support-v4:${androidSupport}"
implementation "com.android.support:appcompat-v7:${androidSupport}"
implementation "com.android.support:support-annotations:${androidSupport}"
implementation "com.android.support:percent:${androidSupport}"
implementation "com.kyleduo.switchbutton:library:${switchButton}"
implementation "com.readystatesoftware.systembartint:systembartint:${systemBarTint}"
implementation "com.facebook.fresco:fresco:${fresco}"
implementation "com.facebook.fresco:animated-gif:${fresco}"
implementation "com.squareup.okhttp3:okhttp:${okHttp}"
implementation "com.leon.channel:helper:${apkChannelPackage}"
implementation "com.squareup.retrofit2:retrofit:${retrofit}"
implementation "com.squareup.retrofit2:converter-gson:${retrofit}" // include gson 2.7
implementation "com.squareup.retrofit2:adapter-rxjava:${retrofit}"
// implementation "com.google.code.gson:gson:${gson}"
implementation "com.j256.ormlite:ormlite-android:${ormlite}"
implementation "com.j256.ormlite:ormlite-core:${ormlite}"
implementation "com.jakewharton:butterknife:${butterKnife}"
annotationProcessor "com.jakewharton:butterknife-compiler:${butterKnife}"
implementation "org.greenrobot:eventbus:${eventbus}"
annotationProcessor "org.greenrobot:eventbus-annotation-processor:${eventbusApt}"
implementation "io.reactivex:rxjava:${rxJava}"
implementation "io.reactivex:rxandroid:${rxAndroid}"
implementation "com.jakewharton.rxbinding:rxbinding:${rxBinding}"
//TODO update to rx 2.x
// implementation "io.reactivex.rxjava2:rxjava:${rxJava2}"
// implementation "io.reactivex.rxjava2:rxandroid:${rxAndroid2}"
// implementation "com.jakewharton.rxbinding2:rxbinding:${rxBinding2}"
implementation "com.google.zxing:core:${zxing}"
implementation "com.google.zxing:android-core:${zxing}"
implementation "com.daimajia.swipelayout:library:${swipeLayout}"
implementation("cn.trinea.android.view.autoscrollviewpager:android-auto-scroll-view-pager:${autoScrollViewPager}") {
exclude module: 'support-v4'
}
implementation "com.sina.weibo.sdk:core:${weiboSDK}"
// bugly with tinker support
implementation "com.tencent.bugly:crashreport_upgrade:${buglyTinkerSupport}"
implementation "pub.devrel:easypermissions:${easypermissions}"
implementation project(':libraries:MiPush')
implementation project(':libraries:MTA')
implementation project(':libraries:QQShare')
implementation project(':libraries:TalkingData')
implementation project(':libraries:UmengPush')
implementation project(':libraries:WechatShare')
implementation project(':libraries:iosched')
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
File propFile = file('sign.properties')
if (propFile.exists()) {
def Properties props = new Properties()
props.load(new FileInputStream(propFile))
if (props.containsKey('keyAlias') && props.containsKey('keyPassword') &&
props.containsKey('storeFile') && props.containsKey('storePassword')) {
android.signingConfigs {
// debug 不要使用正式签名这样tinker才不会打补丁。
debug {
keyAlias props.get('keyAlias')
keyPassword props.get('keyPassword')
storeFile file(props.get('storeFile'))
storePassword props.get('storePassword')
}
release {
keyAlias props.get('keyAlias')
keyPassword props.get('keyPassword')
storeFile file(props.get('storeFile'))
storePassword props.get('storePassword')
}
}
} else {
android.buildTypes.release.signingConfig = null
}
} else {
android.buildTypes.release.signingConfig = null
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

21
app/proguard-fresco.txt Normal file
View File

@ -0,0 +1,21 @@
# Keep our interfaces so they can be used by other ProGuard rules.
# See http://sourceforge.net/p/proguard/bugs/466/
-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
# Do not strip any method/class that is annotated with @DoNotStrip
-keep @com.facebook.common.internal.DoNotStrip class *
-keepclassmembers class * {
@com.facebook.common.internal.DoNotStrip *;
}
# Keep native methods
-keepclassmembers class * {
native <methods>;
}
-dontwarn okio.**
-dontwarn com.squareup.okhttp.**
-dontwarn okhttp3.**
-dontwarn javax.annotation.**
-dontwarn com.android.volley.toolbox.**
-dontwarn com.facebook.infer.**

192
app/proguard-rules.txt Normal file
View File

@ -0,0 +1,192 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\Android\sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
#--------- remove logs start ----------------
-assumenosideeffects class com.lightgame.config.CommonDebug {
private static String getLogTag(...);
private static String getMethodName();
public static void logMethodName(...);
public static void logParams(...);
public static void logFields(...);
public static void logMethodWithParams(...);
}
#-assumenosideeffects class com.lightgame.config.CommonDebug {*;}
#-dontoptimize
#--------- remove logs end ----------------
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
-dontwarn InnerClasses
# OrmLite uses reflection
-keep class com.j256.**
-keepclassmembers class com.j256.** { *; }
-keep enum com.j256.**
-keepclassmembers enum com.j256.** { *; }
-keep interface com.j256.**
-keepclassmembers interface com.j256.** { *; }
-dontwarn com.j256.**
#okhttp3
-dontwarn com.squareup.okhttp3.**
-dontwarn okio.**
-keep class com.squareup.okhttp3.** { *;}
# stetho
-keep class com.facebook.stetho.** { *; }
-dontwarn com.facebook.stetho.**
# Retrofit 2.2
# Platform calls Class.forName on types which do not exist on Android to determine platform.
-dontnote retrofit2.Platform
# Platform used when running on Java 8 VMs. Will not be used at runtime.
-dontwarn retrofit2.Platform$Java8
# Retain generic type information for use by reflection by converters and adapters.
-keepattributes Signature
# Retain declared checked exceptions for use by a Proxy instance.
-keepattributes Exceptions
# Retrofit 2.X
## https://square.github.io/retrofit/ ##
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
-keepclasseswithmembers class * {
@retrofit2.http.* <methods>;
}
# rxjava
-keep class rx.schedulers.Schedulers {
public static <methods>;
}
-keep class rx.schedulers.ImmediateScheduler {
public <methods>;
}
-keep class rx.schedulers.TestScheduler {
public <methods>;
}
-keep class rx.schedulers.Schedulers {
public static ** test();
}
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
long producerNode;
long consumerNode;
}
-dontwarn rx.internal.util.**
## AutoScrollViewPager
-keep class cn.trinea.android.** { *; }
-keepclassmembers class cn.trinea.android.** { *; }
-dontwarn cn.trinea.android.**
## butterknife
# Retain generated class which implement Unbinder.
#-keep public class * implements butterknife.Unbinder { public <init>(**, android.view.View); }
#
## Prevent obfuscation of types which use ButterKnife annotations since the simple name
## is used to reflectively look up the generated ViewBinding.
#-keep class butterknife.*
#-keepclasseswithmembernames class * { @butterknife.* <methods>; }
#-keepclasseswithmembernames class * { @butterknife.* <fields>; }
-dontwarn butterknife.internal.**
-keep class **$$ViewInjector { *; }
-keepnames class * { @butterknife.InjectView *;}
-dontwarn butterknife.Views$InjectViewProcessor
-dontwarn com.gc.materialdesign.views.**
# eventbus
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
# weiboSdk
-keep class com.sina.weibo.sdk.** { *; }
-dontwarn android.webkit.WebView
-dontwarn android.webkit.WebViewClient
# app models
-keep class com.gh.common.view.** {*;}
-keep class com.gh.gamecenter.db.info.** {*;}
-keep class com.gh.gamecenter.entity.** {*;}
-keep class com.gh.gamecenter.retrofit.** {*;}
-keep class com.gh.gamecenter.eventbus.** {*;}
-keep class * extends rx.Subscriber
#---------------------------------webview------------------------------------
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String);
}
#----------------------------------------------------------------------------
##---------------Begin: proguard configuration for Gson ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
#-keep class com.google.gson.stream.** { *; }
# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
-keepclassmembers enum * { *; }
##---------------End: proguard configuration for Gson ----------
# ------ bugly ---------
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# easypermission
-keepclassmembers class * {
@pub.devrel.easypermissions.AfterPermissionGranted <methods>;
}
# 重命名文件为SourceFile再配合mapping符号表可以拿到真实的类名
-renamesourcefileattribute SourceFile
# 保留源文件行号
-keepattributes SourceFile,LineNumberTable

4
app/sign.properties Normal file
View File

@ -0,0 +1,4 @@
storeFile=gh.keystore
storePassword=20150318
keyAlias=gh.keystore
keyPassword=20150318

View File

@ -1,13 +0,0 @@
package com.gh.gamecenter;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@ -0,0 +1,69 @@
package com.gh.gamecenter;
import android.app.Application;
import com.facebook.stetho.Stetho;
import com.facebook.stetho.okhttp3.StethoInterceptor;
import com.lightgame.utils.Utils;
import com.squareup.leakcanary.LeakCanary;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
/**
* @author CsHeng
* @Date 03/09/2017
* @Time 4:34 PM
*/
public class Injection {
public static boolean appInit(Application application) {
// init leakcanary
if (LeakCanary.isInAnalyzerProcess(application)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return false;
}
LeakCanary.install(application);
// init stetho
Stetho.initializeWithDefaults(application);
return true;
}
public static OkHttpClient.Builder provideRetrofitBuilder() {
// HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
//
// @Override
// public void log(String message) {
// //分段打印retrofit日志
// if (message.startsWith("{") || message.startsWith("["))
// if (message.length() > 4000) {
// for (int i = 0; i < message.length(); i += 4000) {
// if (i + 4000 < message.length())
// Utils.log("OkHttp_Body::" + i, message.substring(i, i + 4000));
// else
// Utils.log("OkHttp_Body::" + i, message.substring(i, message.length()));
// }
// } else
// Utils.log("OkHttp_Body::", message);
// }
// });
// loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder builder = new OkHttpClient.Builder();
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
builder.addNetworkInterceptor(interceptor);
builder.addNetworkInterceptor(new StethoInterceptor());
// if (BuildConfig.DEBUG) {
// builder.addNetworkInterceptor(loggingInterceptor);
// }
return builder;
}
}

View File

@ -1,360 +1,306 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.gh.gamecenter" >
<manifest xmlns:android = "http://schemas.android.com/apk/res/android"
package = "com.gh.gamecenter" >
<!-- 允许应用程序访问网络连接 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name = "android.permission.INTERNET" />
<!-- 允许应用程序写入外部存储如SD卡上写文件 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 允许应用程序读取扩展存储器 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name = "android.permission.READ_EXTERNAL_STORAGE" />
<!-- 允许挂载和反挂载文件系统可移动存储 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name = "android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<!-- 允许应用程序访问Wi-Fi网络状态信息 -->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name = "android.permission.ACCESS_WIFI_STATE" />
<!-- 允许应用程序获取网络信息状态 -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name = "android.permission.ACCESS_NETWORK_STATE" />
<!-- 允许应用程序读取电话状态 -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name = "android.permission.READ_PHONE_STATE" />
<!-- 允许应用程序获取当前或最近运行的应用 -->
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name = "android.permission.GET_TASKS" />
<!-- 允许访问振动设备 -->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name = "android.permission.VIBRATE" />
<!-- 允许应用程序通过WiFi或移动基站获取粗略的位置信息 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name = "android.permission.ACCESS_COARSE_LOCATION" />
<!-- 允许应用程序通过GPS获取精确的位置信息 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name = "android.permission.ACCESS_FINE_LOCATION" />
<!-- 允许应用程序改变Wi-Fi连接状态 -->
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name = "android.permission.CHANGE_WIFI_STATE" />
<!-- 允许应用程序管理AccountManager中的账户列表 -->
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name = "android.permission.MANAGE_ACCOUNTS" />
<!-- 允许应用程序访问GMail账户列表 -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name = "android.permission.GET_ACCOUNTS" />
<!-- 允许应用程序连接配对过的蓝牙设备 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name = "android.permission.BLUETOOTH" />
<!-- 允许应用程序管理蓝牙,搜索和配对新的蓝牙设备 -->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name = "android.permission.BLUETOOTH_ADMIN" />
<!-- 允许应用程序打开系统窗口,显示其他应用程序 -->
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<!-- 小米推送需要的权限 -->
<uses-permission android:name="com.gh.gamecenter.permission.MIPUSH_RECEIVE" />
<uses-permission android:name = "android.permission.SYSTEM_ALERT_WINDOW" />
<!-- 修改系统设置的权限 -->
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name = "android.permission.WRITE_SETTINGS" />
<permission
android:name="com.gh.gamecenter.permission.MIPUSH_RECEIVE"
android:protectionLevel="signature" />
<!-- bugly with tinker -->
<uses-permission android:name = "android.permission.READ_PHONE_STATE" />
<uses-permission android:name = "android.permission.INTERNET" />
<uses-permission android:name = "android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name = "android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name = "android.permission.READ_LOGS" />
<uses-permission android:name = "android.permission.WRITE_EXTERNAL_STORAGE" />
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
android:normalScreens="true"
android:resizeable="true"
android:smallScreens="true" />
android:anyDensity = "true"
android:largeScreens = "true"
android:normalScreens = "true"
android:resizeable = "true"
android:smallScreens = "true" />
<!--android:largeHeap = "true"-->
<application
android:name="com.gh.base.AppController"
android:icon="@drawable/logo"
android:label="@string/app_name"
android:theme="@style/AppNormalTheme" >
<!-- TalkingData -->
<meta-data
android:name="TD_APP_ID"
android:value="81DB144D555386A38A70B833537EC256" />
<meta-data
android:name="TD_CHANNEL_ID"
android:value="GH_TEST"/>
<!--android:value="${CHANNEL_VALUE}"-->
android:name = "com.halo.assistant.TinkerApp"
android:allowBackup = "true"
android:icon = "@drawable/logo"
android:label = "@string/app_name"
android:theme = "@style/AppCompatTheme.APP" >
<!-- MTA -->
<meta-data
android:name="TA_APPKEY"
android:value="APV567FTBS7J"/>
<meta-data
android:name="InstallChannel"
android:value="GH_TEST"/>
<!--android:value="${CHANNEL_VALUE}"-->
<!--android:launchMode = "singleTask"-->
<activity
android:name = "com.gh.gamecenter.SplashScreenActivity"
android:configChanges = "keyboardHidden|orientation|screenSize"
android:noHistory = "true"
android:screenOrientation = "portrait"
android:theme = "@style/AppGuideTheme" >
<intent-filter >
<action android:name = "android.intent.action.MAIN" />
<!-- 友盟推送 -->
<meta-data
android:name="UMENG_APPKEY"
android:value="585a29fa8f4a9d327600023e">
</meta-data>
<meta-data
android:name="UMENG_MESSAGE_SECRET"
android:value="8bcce6bed547ee624f5c2cc64d39a9e9">
</meta-data>
<category android:name = "android.intent.category.LAUNCHER" />
</intent-filter >
<!-- MTA可视化启动连接接口 -->
<intent-filter >
<data android:scheme = "${tencentAppScheme}" />
<action android:name = "android.intent.action.VIEW" />
<category android:name = "android.intent.category.DEFAULT" />
<category android:name = "android.intent.category.BROWSABLE" />
</intent-filter >
</activity >
<activity
android:name="com.gh.gamecenter.SplashScreenActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="portrait"
android:theme="@style/AppGuideTheme"
android:uiOptions="splitActionBarWhenNarrow" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
android:name = "com.gh.gamecenter.MainActivity"
android:launchMode = "singleTask"
android:screenOrientation = "portrait"
android:windowSoftInputMode = "stateAlwaysHidden|adjustResize" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.gh.gamecenter.MainActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateAlwaysHidden|adjustResize"/>
<activity
android:name="com.gh.gamecenter.DownloadManagerActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.ViewImageActivity"
android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" />
<activity
android:name="com.gh.gamecenter.SearchActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateVisible" />
<activity
android:name="com.gh.gamecenter.GameDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.NewsDetailActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.SettingActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.SuggestionActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateVisible" />
<activity
android:name="com.gh.gamecenter.ConcernActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.SubjectActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.PluginActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.NewsSearchActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.GameNewsActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.CropImageActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.WebActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.ShareCardPicActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.ShareCardActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.MessageDetailActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.StrategyActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.LibaoActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.LibaoDetailActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.ShareGhWfifActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.ShareGhActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.CleanApkActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.KcSelectGameActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.ChooseReceiverActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.ReceiverWaitingActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.FileSenderActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.FileReceiverActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.SelectUserIconActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.AboutActivity"
android:screenOrientation="portrait"/>
<activity
android:name="com.gh.gamecenter.SkipActivity"
android:theme="@android:style/Theme.Translucent">
<intent-filter>
<data android:scheme="ghzhushou"/>
android:name = "com.gh.gamecenter.DownloadManagerActivity"
android:launchMode = "singleTask"
android:screenOrientation = "portrait" />
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
</activity>
<activity
android:name="com.gh.gamecenter.wxapi.WXEntryActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true"
android:launchMode="singleTop"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="sdksample"/>
</intent-filter>
</activity>
<!-- QQ 分享 -->
android:name = "com.gh.gamecenter.ViewImageActivity"
android:theme = "@android:style/Theme.Black.NoTitleBar.Fullscreen" />
<activity
android:name="com.tencent.tauth.AuthActivity"
android:launchMode="singleTask"
android:noHistory="true" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="tencent1104659243" />
</intent-filter>
</activity>
android:name = "com.gh.gamecenter.SearchActivity"
android:configChanges = "keyboardHidden"
android:screenOrientation = "portrait" />
<activity
android:name="com.tencent.connect.common.AssistActivity"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="behind"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<!--微博分享-->
android:name = "com.gh.gamecenter.NewsDetailActivity"
android:screenOrientation = "portrait" />
<activity
android:name="com.sina.weibo.sdk.component.WeiboSdkBrowser"
android:configChanges="keyboardHidden|orientation"
android:windowSoftInputMode="adjustResize"
android:exported="false" >
</activity>
android:name = "com.gh.gamecenter.SettingActivity"
android:screenOrientation = "portrait" />
<activity
android:name="com.gh.gamecenter.WeiBoShareActivity"
android:configChanges="keyboardHidden|orientation"
android:screenOrientation="portrait"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="true"
android:launchMode="singleTop">
<intent-filter>
<action android:name="com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
android:name = "com.gh.gamecenter.ConcernActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.SubjectActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.PluginActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.NewsSearchActivity"
android:screenOrientation = "portrait"
android:windowSoftInputMode = "stateHidden" />
<activity
android:name = "com.gh.gamecenter.GameNewsActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.CropImageActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.WebActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.ShareCardPicActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.ShareCardActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.MessageDetailActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.LibaoActivity"
android:screenOrientation = "portrait"
android:windowSoftInputMode = "stateHidden" />
<activity
android:name = "com.gh.gamecenter.LibaoDetailActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.ShareGhWfifActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.ShareGhActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.CleanApkActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.KcSelectGameActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.ChooseReceiverActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.ReceiverWaitingActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.FileSenderActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.FileReceiverActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.SelectUserIconActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.AboutActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.CommentDetailActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.GameDetailActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.SuggestSelectActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.SuggestionActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.VoteActivity"
android:screenOrientation = "portrait"
android:windowSoftInputMode = "stateAlwaysHidden|adjustResize" />
<activity
android:name = "com.gh.gamecenter.ToolBoxActivity"
android:screenOrientation = "portrait"
android:windowSoftInputMode = "stateHidden" />
<receiver android:name="com.gh.gamecenter.receiver.InstallAndUninstallReceiver" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<activity
android:name = "com.gh.gamecenter.WeiBoShareActivity"
android:screenOrientation = "portrait"
android:windowSoftInputMode = "stateHidden" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<activity
android:name = "com.gh.gamecenter.InstallActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.LoginActivity"
android:screenOrientation = "portrait"
android:windowSoftInputMode = "stateHidden" />
<activity
android:name = "com.gh.gamecenter.UserInfoActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.UserRegionActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.CollectionActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.MessageActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.UserInfoEditActivity"
android:screenOrientation = "portrait"
android:windowSoftInputMode = "stateHidden" />
<activity
android:name = "com.gh.gamecenter.KaiFuActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.NormalActivity"
android:screenOrientation = "portrait" />
<activity
android:name = "com.gh.gamecenter.SkipActivity"
android:theme = "@style/Theme.AppCompat.Light.Fullscreen.Transparent" >
<intent-filter >
<data android:scheme = "ghzhushou" />
<category android:name = "android.intent.category.DEFAULT" />
<action android:name = "android.intent.action.VIEW" />
<category android:name = "android.intent.category.BROWSABLE" />
</intent-filter >
</activity >
<activity
android:name = ".CommonActivity"
android:screenOrientation = "portrait" />
<receiver android:name = "com.gh.gamecenter.receiver.InstallAndUninstallReceiver" >
<intent-filter >
<action android:name = "android.intent.action.PACKAGE_ADDED" />
<action android:name = "android.intent.action.PACKAGE_REMOVED" />
<action android:name = "android.intent.action.PACKAGE_REPLACED" />
<data android:scheme = "package" />
</intent-filter >
</receiver >
<receiver
android:name="com.gh.gamecenter.receiver.NotificationReceiver"
android:exported="false" >
<intent-filter>
<action android:name="com.gh.gamecenter.NOTIFICATION" />
</intent-filter>
</receiver>
android:name = "com.gh.gamecenter.receiver.NotificationReceiver"
android:exported = "false" >
<intent-filter >
<action android:name = "com.gh.gamecenter.NOTIFICATION" />
</intent-filter >
</receiver >
<receiver
android:name="com.gh.gamecenter.receiver.DownloadReceiver"
android:exported="false" >
<intent-filter>
<action android:name="com.gh.gamecenter.DOWNLOAD" />
</intent-filter>
</receiver>
android:name = "com.gh.gamecenter.receiver.DownloadReceiver"
android:exported = "false" >
<intent-filter >
<action android:name = "com.gh.gamecenter.DOWNLOAD" />
</intent-filter >
</receiver >
<receiver
android:name="com.gh.gamecenter.receiver.InstallReceiver"
android:exported="false" >
<intent-filter>
<action android:name="com.gh.gamecenter.INSTALL" />
</intent-filter>
</receiver>
<receiver android:name="com.gh.gamecenter.receiver.NetworkStateReceiver" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
android:name = "com.gh.gamecenter.receiver.InstallReceiver"
android:exported = "false" >
<intent-filter >
<action android:name = "com.gh.gamecenter.INSTALL" />
</intent-filter >
</receiver >
<receiver android:name = "com.gh.gamecenter.receiver.NetworkStateReceiver" >
<intent-filter >
<action android:name = "android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter >
</receiver >
<receiver
android:name="com.xiaomi.push.service.receivers.PingReceiver"
android:exported="false"
android:process=":pushservice" >
<intent-filter>
<action android:name="com.xiaomi.push.PING_TIMER" />
</intent-filter>
</receiver>
<receiver
android:name="com.gh.base.GHPushMessageReceiver"
android:exported="true" >
<intent-filter>
<action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" />
</intent-filter>
<intent-filter>
<action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" />
</intent-filter>
<intent-filter>
<action android:name="com.xiaomi.mipush.ERROR" />
</intent-filter>
</receiver>
<receiver
android:name="com.xiaomi.push.service.receivers.NetworkStatusReceiver"
android:exported="true" >
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
android:name = "com.gh.gamecenter.receiver.ActivitySkipReceiver"
android:exported = "true" >
<intent-filter >
<action android:name = "com.gh.gamecenter.ACTIVITYSKIP" />
</intent-filter >
</receiver >
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
<receiver
android:name="com.gh.gamecenter.receiver.ActivitySkipReceiver"
android:exported="true" >
<intent-filter>
<action android:name="com.gh.gamecenter.ACTIVITYSKIP" />
</intent-filter>
</receiver>
<!--<service android:name = "com.gh.gamecenter.statistics.AppStaticService" />-->
<service
android:name="com.gh.download.DownloadService" />
</application >
<!--<service-->
<!--android:name="com.gh.base.AppTinkerResultService"-->
<!--android:exported="false" />-->
<service
android:name="com.gh.gamecenter.statistics.AppStaticService" />
<service
android:name="com.xiaomi.push.service.XMJobService"
android:enabled="true"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE"
android:process=":pushservice" />
<service
android:name="com.xiaomi.push.service.XMPushService"
android:enabled="true"
android:process=":pushservice" />
<service
android:name="com.xiaomi.mipush.sdk.PushMessageHandler"
android:enabled="true"
android:exported="true" />
<service
android:name="com.xiaomi.mipush.sdk.MessageHandleService"
android:enabled="true" />
</application>
</manifest>
</manifest >

View File

@ -1,13 +1,13 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head>
<head>
<meta charset="utf-8">
<title>光环助手</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<style>
@ -46,22 +46,24 @@ article {
margin-bottom:20%;
}
}
</style>
</head>
</style>
</head>
<body>
<header>
</header>
<header>
</header>
<article>
<img src="http://192.168.43.1:3100/image/gh_icon.png" width="28%">
<p class="title">光环助手</p>
<br class="info">乐于分享的人是最帅的^_^ </p>
<div class="download">
<a href="http://192.168.43.1:3100/download/ghzs.apk">免流量下载</a>
<article>
<img src="http://192.168.43.1:3100/image/gh_icon.png" width="28%">
<p class="title">光环助手</p>
<br class="info">乐于分享的人是最帅的^_^ </p>
<div class="download">
<a href="http://192.168.43.1:3100/download/ghzs.apk">免流量下载</a>
</div>
<p class="title"><font color="#9A9A9A" size="3em">仅限安卓系统 </font></p>
</article>
<p class="title"><font color="#9A9A9A" size="3em">仅限安卓系统 </font></p>
</article>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@ -1,699 +0,0 @@
/*
* Copyright (C) 2014 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 android.support.v7.widget;
import android.support.v4.util.Pools;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static android.support.v7.widget.RecyclerView.ViewHolder;
/**
* Helper class that can enqueue and process adapter update operations.
* <p>
* To support animations, RecyclerView presents an older version the Adapter to best represent
* previous state of the layout. Sometimes, this is not trivial when items are removed that were
* not laid out, in which case, RecyclerView has no way of providing that item's view for
* animations.
* <p>
* AdapterHelper creates an UpdateOp for each adapter data change then pre-processes them. During
* pre processing, AdapterHelper finds out which UpdateOps can be deferred to second layout pass
* and which cannot. For the UpdateOps that cannot be deferred, AdapterHelper will change them
* according to previously deferred operation and dispatch them before the first layout pass. It
* also takes care of updating deferred UpdateOps since order of operations is changed by this
* process.
* <p>
* Although operations may be forwarded to LayoutManager in different orders, resulting data set
* is guaranteed to be the consistent.
*/
class AdapterHelper implements OpReorderer.Callback {
final static int POSITION_TYPE_INVISIBLE = 0;
final static int POSITION_TYPE_NEW_OR_LAID_OUT = 1;
private static final boolean DEBUG = false;
private static final String TAG = "AHT";
private Pools.Pool<UpdateOp> mUpdateOpPool = new Pools.SimplePool<UpdateOp>(UpdateOp.POOL_SIZE);
final ArrayList<UpdateOp> mPendingUpdates = new ArrayList<UpdateOp>();
final ArrayList<UpdateOp> mPostponedList = new ArrayList<UpdateOp>();
final Callback mCallback;
Runnable mOnItemProcessedCallback;
final boolean mDisableRecycler;
final OpReorderer mOpReorderer;
AdapterHelper(Callback callback) {
this(callback, false);
}
AdapterHelper(Callback callback, boolean disableRecycler) {
mCallback = callback;
mDisableRecycler = disableRecycler;
mOpReorderer = new OpReorderer(this);
}
AdapterHelper addUpdateOp(UpdateOp... ops) {
Collections.addAll(mPendingUpdates, ops);
return this;
}
void reset() {
recycleUpdateOpsAndClearList(mPendingUpdates);
recycleUpdateOpsAndClearList(mPostponedList);
}
void preProcess() {
mOpReorderer.reorderOps(mPendingUpdates);
final int count = mPendingUpdates.size();
for (int i = 0; i < count; i++) {
UpdateOp op = mPendingUpdates.get(i);
switch (op.cmd) {
case UpdateOp.ADD:
applyAdd(op);
break;
case UpdateOp.REMOVE:
applyRemove(op);
break;
case UpdateOp.UPDATE:
applyUpdate(op);
break;
case UpdateOp.MOVE:
applyMove(op);
break;
}
if (mOnItemProcessedCallback != null) {
mOnItemProcessedCallback.run();
}
}
mPendingUpdates.clear();
}
void consumePostponedUpdates() {
final int count = mPostponedList.size();
for (int i = 0; i < count; i++) {
mCallback.onDispatchSecondPass(mPostponedList.get(i));
}
recycleUpdateOpsAndClearList(mPostponedList);
}
private void applyMove(UpdateOp op) {
// MOVE ops are pre-processed so at this point, we know that item is still in the adapter.
// otherwise, it would be converted into a REMOVE operation
postponeAndUpdateViewHolders(op);
}
private void applyRemove(UpdateOp op) {
int tmpStart = op.positionStart;
int tmpCount = 0;
int tmpEnd = op.positionStart + op.itemCount;
int type = -1;
for (int position = op.positionStart; position < tmpEnd; position++) {
boolean typeChanged = false;
ViewHolder vh = mCallback.findViewHolder(position);
if (vh != null || canFindInPreLayout(position)) {
// If a ViewHolder exists or this is a newly added item, we can defer this update
// to post layout stage.
// * For existing ViewHolders, we'll fake its existence in the pre-layout phase.
// * For items that are added and removed in the same process cycle, they won't
// have any effect in pre-layout since their add ops are already deferred to
// post-layout pass.
if (type == POSITION_TYPE_INVISIBLE) {
// Looks like we have other updates that we cannot merge with this one.
// Create an UpdateOp and dispatch it to LayoutManager.
UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount);
dispatchAndUpdateViewHolders(newOp);
typeChanged = true;
}
type = POSITION_TYPE_NEW_OR_LAID_OUT;
} else {
// This update cannot be recovered because we don't have a ViewHolder representing
// this position. Instead, post it to LayoutManager immediately
if (type == POSITION_TYPE_NEW_OR_LAID_OUT) {
// Looks like we have other updates that we cannot merge with this one.
// Create UpdateOp op and dispatch it to LayoutManager.
UpdateOp newOp = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount);
postponeAndUpdateViewHolders(newOp);
typeChanged = true;
}
type = POSITION_TYPE_INVISIBLE;
}
if (typeChanged) {
position -= tmpCount; // also equal to tmpStart
tmpEnd -= tmpCount;
tmpCount = 1;
} else {
tmpCount++;
}
}
if (tmpCount != op.itemCount) { // all 1 effect
recycleUpdateOp(op);
op = obtainUpdateOp(UpdateOp.REMOVE, tmpStart, tmpCount);
}
if (type == POSITION_TYPE_INVISIBLE) {
dispatchAndUpdateViewHolders(op);
} else {
postponeAndUpdateViewHolders(op);
}
}
private void applyUpdate(UpdateOp op) {
int tmpStart = op.positionStart;
int tmpCount = 0;
int tmpEnd = op.positionStart + op.itemCount;
int type = -1;
for (int position = op.positionStart; position < tmpEnd; position++) {
ViewHolder vh = mCallback.findViewHolder(position);
if (vh != null || canFindInPreLayout(position)) { // deferred
if (type == POSITION_TYPE_INVISIBLE) {
UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount);
dispatchAndUpdateViewHolders(newOp);
tmpCount = 0;
tmpStart = position;
}
type = POSITION_TYPE_NEW_OR_LAID_OUT;
} else { // applied
if (type == POSITION_TYPE_NEW_OR_LAID_OUT) {
UpdateOp newOp = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount);
postponeAndUpdateViewHolders(newOp);
tmpCount = 0;
tmpStart = position;
}
type = POSITION_TYPE_INVISIBLE;
}
tmpCount++;
}
if (tmpCount != op.itemCount) { // all 1 effect
recycleUpdateOp(op);
op = obtainUpdateOp(UpdateOp.UPDATE, tmpStart, tmpCount);
}
if (type == POSITION_TYPE_INVISIBLE) {
dispatchAndUpdateViewHolders(op);
} else {
postponeAndUpdateViewHolders(op);
}
}
private void dispatchAndUpdateViewHolders(UpdateOp op) {
// tricky part.
// traverse all postpones and revert their changes on this op if necessary, apply updated
// dispatch to them since now they are after this op.
if (op.cmd == UpdateOp.ADD || op.cmd == UpdateOp.MOVE) {
throw new IllegalArgumentException("should not dispatch add or move for pre layout");
}
if (DEBUG) {
Log.d(TAG, "dispatch (pre)" + op);
Log.d(TAG, "postponed state before:");
for (UpdateOp updateOp : mPostponedList) {
Log.d(TAG, updateOp.toString());
}
Log.d(TAG, "----");
}
// handle each pos 1 by 1 to ensure continuity. If it breaks, dispatch partial
// TODO Since move ops are pushed to end, we should not need this anymore
int tmpStart = updatePositionWithPostponed(op.positionStart, op.cmd);
if (DEBUG) {
Log.d(TAG, "pos:" + op.positionStart + ",updatedPos:" + tmpStart);
}
int tmpCnt = 1;
int offsetPositionForPartial = op.positionStart;
final int positionMultiplier;
switch (op.cmd) {
case UpdateOp.UPDATE:
positionMultiplier = 1;
break;
case UpdateOp.REMOVE:
positionMultiplier = 0;
break;
default:
throw new IllegalArgumentException("op should be remove or update." + op);
}
for (int p = 1; p < op.itemCount; p++) {
final int pos = op.positionStart + (positionMultiplier * p);
int updatedPos = updatePositionWithPostponed(pos, op.cmd);
if (DEBUG) {
Log.d(TAG, "pos:" + pos + ",updatedPos:" + updatedPos);
}
boolean continuous = false;
switch (op.cmd) {
case UpdateOp.UPDATE:
continuous = updatedPos == tmpStart + 1;
break;
case UpdateOp.REMOVE:
continuous = updatedPos == tmpStart;
break;
}
if (continuous) {
tmpCnt++;
} else {
// need to dispatch this separately
UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt);
if (DEBUG) {
Log.d(TAG, "need to dispatch separately " + tmp);
}
dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial);
recycleUpdateOp(tmp);
if (op.cmd == UpdateOp.UPDATE) {
offsetPositionForPartial += tmpCnt;
}
tmpStart = updatedPos;// need to remove previously dispatched
tmpCnt = 1;
}
}
recycleUpdateOp(op);
if (tmpCnt > 0) {
UpdateOp tmp = obtainUpdateOp(op.cmd, tmpStart, tmpCnt);
if (DEBUG) {
Log.d(TAG, "dispatching:" + tmp);
}
dispatchFirstPassAndUpdateViewHolders(tmp, offsetPositionForPartial);
recycleUpdateOp(tmp);
}
if (DEBUG) {
Log.d(TAG, "post dispatch");
Log.d(TAG, "postponed state after:");
for (UpdateOp updateOp : mPostponedList) {
Log.d(TAG, updateOp.toString());
}
Log.d(TAG, "----");
}
}
void dispatchFirstPassAndUpdateViewHolders(UpdateOp op, int offsetStart) {
mCallback.onDispatchFirstPass(op);
switch (op.cmd) {
case UpdateOp.REMOVE:
mCallback.offsetPositionsForRemovingInvisible(offsetStart, op.itemCount);
break;
case UpdateOp.UPDATE:
mCallback.markViewHoldersUpdated(offsetStart, op.itemCount);
break;
default:
throw new IllegalArgumentException("only remove and update ops can be dispatched"
+ " in first pass");
}
}
private int updatePositionWithPostponed(int pos, int cmd) {
final int count = mPostponedList.size();
for (int i = count - 1; i >= 0; i--) {
UpdateOp postponed = mPostponedList.get(i);
if (postponed.cmd == UpdateOp.MOVE) {
int start, end;
if (postponed.positionStart < postponed.itemCount) {
start = postponed.positionStart;
end = postponed.itemCount;
} else {
start = postponed.itemCount;
end = postponed.positionStart;
}
if (pos >= start && pos <= end) {
//i'm affected
if (start == postponed.positionStart) {
if (cmd == UpdateOp.ADD) {
postponed.itemCount++;
} else if (cmd == UpdateOp.REMOVE) {
postponed.itemCount--;
}
// op moved to left, move it right to revert
pos++;
} else {
if (cmd == UpdateOp.ADD) {
postponed.positionStart++;
} else if (cmd == UpdateOp.REMOVE) {
postponed.positionStart--;
}
// op was moved right, move left to revert
pos--;
}
} else if (pos < postponed.positionStart) {
// postponed MV is outside the dispatched OP. if it is before, offset
if (cmd == UpdateOp.ADD) {
postponed.positionStart++;
postponed.itemCount++;
} else if (cmd == UpdateOp.REMOVE) {
postponed.positionStart--;
postponed.itemCount--;
}
}
} else {
if (postponed.positionStart <= pos) {
if (postponed.cmd == UpdateOp.ADD) {
pos -= postponed.itemCount;
} else if (postponed.cmd == UpdateOp.REMOVE) {
pos += postponed.itemCount;
}
} else {
if (cmd == UpdateOp.ADD) {
postponed.positionStart++;
} else if (cmd == UpdateOp.REMOVE) {
postponed.positionStart--;
}
}
}
if (DEBUG) {
Log.d(TAG, "dispath (step" + i + ")");
Log.d(TAG, "postponed state:" + i + ", pos:" + pos);
for (UpdateOp updateOp : mPostponedList) {
Log.d(TAG, updateOp.toString());
}
Log.d(TAG, "----");
}
}
for (int i = mPostponedList.size() - 1; i >= 0; i--) {
UpdateOp op = mPostponedList.get(i);
if (op.cmd == UpdateOp.MOVE) {
if (op.itemCount == op.positionStart || op.itemCount < 0) {
mPostponedList.remove(i);
recycleUpdateOp(op);
}
} else if (op.itemCount <= 0) {
mPostponedList.remove(i);
recycleUpdateOp(op);
}
}
return pos;
}
private boolean canFindInPreLayout(int position) {
final int count = mPostponedList.size();
for (int i = 0; i < count; i++) {
UpdateOp op = mPostponedList.get(i);
if (op.cmd == UpdateOp.MOVE) {
if (findPositionOffset(op.itemCount, i + 1) == position) {
return true;
}
} else if (op.cmd == UpdateOp.ADD) {
// TODO optimize.
final int end = op.positionStart + op.itemCount;
for (int pos = op.positionStart; pos < end; pos++) {
if (findPositionOffset(pos, i + 1) == position) {
return true;
}
}
}
}
return false;
}
private void applyAdd(UpdateOp op) {
postponeAndUpdateViewHolders(op);
}
private void postponeAndUpdateViewHolders(UpdateOp op) {
if (DEBUG) {
Log.d(TAG, "postponing " + op);
}
// Utils.log("add UpdateOp to PostponedList");
mPostponedList.add(op);
// Utils.log("op" + op.positionStart + "=" + op.itemCount);
switch (op.cmd) {
case UpdateOp.ADD:
mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
break;
case UpdateOp.MOVE:
mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
break;
case UpdateOp.REMOVE:
mCallback.offsetPositionsForRemovingLaidOutOrNewView(op.positionStart,
op.itemCount);
break;
case UpdateOp.UPDATE:
mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount);
break;
default:
throw new IllegalArgumentException("Unknown update op type for " + op);
}
}
boolean hasPendingUpdates() {
return mPendingUpdates.size() > 0;
}
int findPositionOffset(int position) {
return findPositionOffset(position, 0);
}
int findPositionOffset(int position, int firstPostponedItem) {
int count = mPostponedList.size();
for (int i = firstPostponedItem; i < count; ++i) {
UpdateOp op = mPostponedList.get(i);
if (op.cmd == UpdateOp.MOVE) {
if (op.positionStart == position) {
position = op.itemCount;
} else {
if (op.positionStart < position) {
position--; // like a remove
}
if (op.itemCount <= position) {
position++; // like an add
}
}
} else if (op.positionStart <= position) {
if (op.cmd == UpdateOp.REMOVE) {
if (position < op.positionStart + op.itemCount) {
return -1;
}
position -= op.itemCount;
} else if (op.cmd == UpdateOp.ADD) {
position += op.itemCount;
}
}
}
return position;
}
/**
* @return True if updates should be processed.
*/
boolean onItemRangeChanged(int positionStart, int itemCount) {
mPendingUpdates.add(obtainUpdateOp(UpdateOp.UPDATE, positionStart, itemCount));
return mPendingUpdates.size() == 1;
}
/**
* @return True if updates should be processed.
*/
boolean onItemRangeInserted(int positionStart, int itemCount) {
mPendingUpdates.add(obtainUpdateOp(UpdateOp.ADD, positionStart, itemCount));
return mPendingUpdates.size() == 1;
}
/**
* @return True if updates should be processed.
*/
boolean onItemRangeRemoved(int positionStart, int itemCount) {
mPendingUpdates.add(obtainUpdateOp(UpdateOp.REMOVE, positionStart, itemCount));
return mPendingUpdates.size() == 1;
}
/**
* @return True if updates should be processed.
*/
boolean onItemRangeMoved(int from, int to, int itemCount) {
if (from == to) {
return false;//no-op
}
if (itemCount != 1) {
throw new IllegalArgumentException("Moving more than 1 item is not supported yet");
}
mPendingUpdates.add(obtainUpdateOp(UpdateOp.MOVE, from, to));
return mPendingUpdates.size() == 1;
}
/**
* Skips pre-processing and applies all updates in one pass.
*/
void consumeUpdatesInOnePass() {
// we still consume postponed updates (if there is) in case there was a pre-process call
// w/o a matching consumePostponedUpdates.
consumePostponedUpdates();
final int count = mPendingUpdates.size();
for (int i = 0; i < count; i++) {
UpdateOp op = mPendingUpdates.get(i);
switch (op.cmd) {
case UpdateOp.ADD:
mCallback.onDispatchSecondPass(op);
mCallback.offsetPositionsForAdd(op.positionStart, op.itemCount);
break;
case UpdateOp.REMOVE:
mCallback.onDispatchSecondPass(op);
mCallback.offsetPositionsForRemovingInvisible(op.positionStart, op.itemCount);
break;
case UpdateOp.UPDATE:
mCallback.onDispatchSecondPass(op);
mCallback.markViewHoldersUpdated(op.positionStart, op.itemCount);
break;
case UpdateOp.MOVE:
mCallback.onDispatchSecondPass(op);
mCallback.offsetPositionsForMove(op.positionStart, op.itemCount);
break;
}
if (mOnItemProcessedCallback != null) {
mOnItemProcessedCallback.run();
}
}
recycleUpdateOpsAndClearList(mPendingUpdates);
}
/**
* Queued operation to happen when child views are updated.
*/
static class UpdateOp {
static final int ADD = 0;
static final int REMOVE = 1;
static final int UPDATE = 2;
static final int MOVE = 3;
static final int POOL_SIZE = 30;
int cmd;
int positionStart;
// holds the target position if this is a MOVE
int itemCount;
UpdateOp(int cmd, int positionStart, int itemCount) {
this.cmd = cmd;
this.positionStart = positionStart;
this.itemCount = itemCount;
}
String cmdToString() {
switch (cmd) {
case ADD:
return "add";
case REMOVE:
return "rm";
case UPDATE:
return "up";
case MOVE:
return "mv";
}
return "??";
}
@Override
public String toString() {
return "[" + cmdToString() + ",s:" + positionStart + "c:" + itemCount + "]";
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
UpdateOp op = (UpdateOp) o;
if (cmd != op.cmd) {
return false;
}
if (cmd == MOVE && Math.abs(itemCount - positionStart) == 1) {
// reverse of this is also true
if (itemCount == op.positionStart && positionStart == op.itemCount) {
return true;
}
}
if (itemCount != op.itemCount) {
return false;
}
if (positionStart != op.positionStart) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = cmd;
result = 31 * result + positionStart;
result = 31 * result + itemCount;
return result;
}
}
@Override
public UpdateOp obtainUpdateOp(int cmd, int positionStart, int itemCount) {
UpdateOp op = mUpdateOpPool.acquire();
if (op == null) {
op = new UpdateOp(cmd, positionStart, itemCount);
} else {
op.cmd = cmd;
op.positionStart = positionStart;
op.itemCount = itemCount;
}
return op;
}
@Override
public void recycleUpdateOp(UpdateOp op) {
if (!mDisableRecycler) {
mUpdateOpPool.release(op);
}
}
void recycleUpdateOpsAndClearList(List<UpdateOp> ops) {
final int count = ops.size();
for (int i = 0; i < count; i++) {
recycleUpdateOp(ops.get(i));
}
ops.clear();
}
/**
* Contract between AdapterHelper and RecyclerView.
*/
static interface Callback {
ViewHolder findViewHolder(int position);
void offsetPositionsForRemovingInvisible(int positionStart, int itemCount);
void offsetPositionsForRemovingLaidOutOrNewView(int positionStart, int itemCount);
void markViewHoldersUpdated(int positionStart, int itemCount);
void onDispatchFirstPass(UpdateOp updateOp);
void onDispatchSecondPass(UpdateOp updateOp);
void offsetPositionsForAdd(int positionStart, int itemCount);
void offsetPositionsForMove(int from, int to);
}
}

View File

@ -1,484 +0,0 @@
/*
* Copyright (C) 2014 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 android.support.v7.widget;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import java.util.ArrayList;
import java.util.List;
/**
* Helper class to manage children.
* <p>
* It wraps a RecyclerView and adds ability to hide some children. There are two sets of methods
* provided by this class. <b>Regular</b> methods are the ones that replicate ViewGroup methods
* like getChildAt, getChildCount etc. These methods ignore hidden children.
* <p>
* When RecyclerView needs direct access to the view group children, it can call unfiltered
* methods like get getUnfilteredChildCount or getUnfilteredChildAt.
*/
class ChildHelper {
private static final boolean DEBUG = false;
private static final String TAG = "ChildrenHelper";
final Callback mCallback;
final Bucket mBucket;
final List<View> mHiddenViews;
ChildHelper(Callback callback) {
mCallback = callback;
mBucket = new Bucket();
mHiddenViews = new ArrayList<View>();
}
/**
* Adds a view to the ViewGroup
*
* @param child View to add.
* @param hidden If set to true, this item will be invisible from regular methods.
*/
void addView(View child, boolean hidden) {
addView(child, -1, hidden);
}
/**
* Add a view to the ViewGroup at an index
*
* @param child View to add.
* @param index Index of the child from the regular perspective (excluding hidden views).
* ChildHelper offsets this index to actual ViewGroup index.
* @param hidden If set to true, this item will be invisible from regular methods.
*/
void addView(View child, int index, boolean hidden) {
final int offset;
if (index < 0) {
offset = mCallback.getChildCount();
} else {
offset = getOffset(index);
}
mCallback.addView(child, offset);
mBucket.insert(offset, hidden);
if (hidden) {
mHiddenViews.add(child);
}
if (DEBUG) {
Log.d(TAG, "addViewAt " + index + ",h:" + hidden + ", " + this);
}
}
private int getOffset(int index) {
if (index < 0) {
return -1; //anything below 0 won't work as diff will be undefined.
}
final int limit = mCallback.getChildCount();
int offset = index;
while (offset < limit) {
final int removedBefore = mBucket.countOnesBefore(offset);
final int diff = index - (offset - removedBefore);
if (diff == 0) {
while (mBucket.get(offset)) { // ensure this offset is not hidden
offset ++;
}
return offset;
} else {
offset += diff;
}
}
return -1;
}
/**
* Removes the provided View from underlying RecyclerView.
*
* @param view The view to remove.
*/
void removeView(View view) {
int index = mCallback.indexOfChild(view);
if (index < 0) {
return;
}
mCallback.removeViewAt(index);
if (mBucket.remove(index)) {
mHiddenViews.remove(view);
}
if (DEBUG) {
Log.d(TAG, "remove View off:" + index + "," + this);
}
}
/**
* Removes the view at the provided index from RecyclerView.
*
* @param index Index of the child from the regular perspective (excluding hidden views).
* ChildHelper offsets this index to actual ViewGroup index.
*/
void removeViewAt(int index) {
final int offset = getOffset(index);
final View view = mCallback.getChildAt(offset);
if (view == null) {
return;
}
mCallback.removeViewAt(offset);
if (mBucket.remove(offset)) {
mHiddenViews.remove(view);
}
if (DEBUG) {
Log.d(TAG, "removeViewAt " + index + ", off:" + offset + ", " + this);
}
}
/**
* Returns the child at provided index.
*
* @param index Index of the child to return in regular perspective.
*/
View getChildAt(int index) {
final int offset = getOffset(index);
return mCallback.getChildAt(offset);
}
/**
* Removes all views from the ViewGroup including the hidden ones.
*/
void removeAllViewsUnfiltered() {
mCallback.removeAllViews();
mBucket.reset();
mHiddenViews.clear();
if (DEBUG) {
Log.d(TAG, "removeAllViewsUnfiltered");
}
}
/**
* This can be used to find a disappearing view by position.
*
* @param position The adapter position of the item.
* @param type View type, can be {@link RecyclerView#INVALID_TYPE}.
* @return A hidden view with a valid ViewHolder that matches the position and type.
*/
View findHiddenNonRemovedView(int position, int type) {
final int count = mHiddenViews.size();
for (int i = 0; i < count; i++) {
final View view = mHiddenViews.get(i);
RecyclerView.ViewHolder holder = mCallback.getChildViewHolder(view);
if (holder.getPosition() == position && !holder.isInvalid() &&
(type == RecyclerView.INVALID_TYPE || holder.getItemViewType() == type)) {
return view;
}
}
return null;
}
/**
* Attaches the provided view to the underlying ViewGroup.
*
* @param child Child to attach.
* @param index Index of the child to attach in regular perspective.
* @param layoutParams LayoutParams for the child.
* @param hidden If set to true, this item will be invisible to the regular methods.
*/
void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams,
boolean hidden) {
final int offset;
if (index < 0) {
offset = mCallback.getChildCount();
} else {
offset = getOffset(index);
}
mCallback.attachViewToParent(child, offset, layoutParams);
mBucket.insert(offset, hidden);
if (DEBUG) {
Log.d(TAG, "attach view to parent index:" + index + ",off:" + offset + "," +
"h:" + hidden + ", " + this);
}
}
/**
* Returns the number of children that are not hidden.
*
* @return Number of children that are not hidden.
* @see #getChildAt(int)
*/
int getChildCount() {
return mCallback.getChildCount() - mHiddenViews.size();
}
/**
* Returns the total number of children.
*
* @return The total number of children including the hidden views.
* @see #getUnfilteredChildAt(int)
*/
int getUnfilteredChildCount() {
return mCallback.getChildCount();
}
/**
* Returns a child by ViewGroup offset. ChildHelper won't offset this index.
*
* @param index ViewGroup index of the child to return.
* @return The view in the provided index.
*/
View getUnfilteredChildAt(int index) {
return mCallback.getChildAt(index);
}
/**
* Detaches the view at the provided index.
*
* @param index Index of the child to return in regular perspective.
*/
void detachViewFromParent(int index) {
final int offset = getOffset(index);
mCallback.detachViewFromParent(offset);
mBucket.remove(offset);
if (DEBUG) {
Log.d(TAG, "detach view from parent " + index + ", off:" + offset);
}
}
/**
* Returns the index of the child in regular perspective.
*
* @param child The child whose index will be returned.
* @return The regular perspective index of the child or -1 if it does not exists.
*/
int indexOfChild(View child) {
final int index = mCallback.indexOfChild(child);
if (index == -1) {
return -1;
}
if (mBucket.get(index)) {
if (DEBUG) {
throw new IllegalArgumentException("cannot get index of a hidden child");
} else {
return -1;
}
}
// reverse the index
return index - mBucket.countOnesBefore(index);
}
/**
* Returns whether a View is visible to LayoutManager or not.
*
* @param view The child view to check. Should be a child of the Callback.
* @return True if the View is not visible to LayoutManager
*/
boolean isHidden(View view) {
return mHiddenViews.contains(view);
}
/**
* Marks a child view as hidden.
*
* @param view The view to hide.
*/
void hide(View view) {
final int offset = mCallback.indexOfChild(view);
if (offset < 0) {
throw new IllegalArgumentException("view is not a child, cannot hide " + view);
}
if (DEBUG && mBucket.get(offset)) {
throw new RuntimeException("trying to hide same view twice, how come ? " + view);
}
mBucket.set(offset);
mHiddenViews.add(view);
if (DEBUG) {
Log.d(TAG, "hiding child " + view + " at offset " + offset+ ", " + this);
}
}
@Override
public String toString() {
return mBucket.toString();
}
/**
* Removes a view from the ViewGroup if it is hidden.
*
* @param view The view to remove.
* @return True if the View is found and it is hidden. False otherwise.
*/
boolean removeViewIfHidden(View view) {
final int index = mCallback.indexOfChild(view);
if (index == -1) {
if (mHiddenViews.remove(view) && DEBUG) {
throw new IllegalStateException("view is in hidden list but not in view group");
}
return true;
}
if (mBucket.get(index)) {
mBucket.remove(index);
mCallback.removeViewAt(index);
if (!mHiddenViews.remove(view) && DEBUG) {
throw new IllegalStateException(
"removed a hidden view but it is not in hidden views list");
}
return true;
}
return false;
}
/**
* Bitset implementation that provides methods to offset indices.
*/
static class Bucket {
final static int BITS_PER_WORD = Long.SIZE;
final static long LAST_BIT = 1L << (Long.SIZE - 1);
long mData = 0;
Bucket next;
void set(int index) {
if (index >= BITS_PER_WORD) {
ensureNext();
next.set(index - BITS_PER_WORD);
} else {
mData |= 1L << index;
}
}
private void ensureNext() {
if (next == null) {
next = new Bucket();
}
}
void clear(int index) {
if (index >= BITS_PER_WORD) {
if (next != null) {
next.clear(index - BITS_PER_WORD);
}
} else {
mData &= ~(1L << index);
}
}
boolean get(int index) {
if (index >= BITS_PER_WORD) {
ensureNext();
return next.get(index - BITS_PER_WORD);
} else {
return (mData & (1L << index)) != 0;
}
}
void reset() {
mData = 0;
if (next != null) {
next.reset();
}
}
void insert(int index, boolean value) {
if (index >= BITS_PER_WORD) {
ensureNext();
next.insert(index - BITS_PER_WORD, value);
} else {
final boolean lastBit = (mData & LAST_BIT) != 0;
long mask = (1L << index) - 1;
final long before = mData & mask;
final long after = ((mData & ~mask)) << 1;
mData = before | after;
if (value) {
set(index);
} else {
clear(index);
}
if (lastBit || next != null) {
ensureNext();
next.insert(0, lastBit);
}
}
}
boolean remove(int index) {
if (index >= BITS_PER_WORD) {
ensureNext();
return next.remove(index - BITS_PER_WORD);
} else {
long mask = (1L << index);
final boolean value = (mData & mask) != 0;
mData &= ~mask;
mask = mask - 1;
final long before = mData & mask;
// cannot use >> because it adds one.
final long after = Long.rotateRight(mData & ~mask, 1);
mData = before | after;
if (next != null) {
if (next.get(0)) {
set(BITS_PER_WORD - 1);
}
next.remove(0);
}
return value;
}
}
int countOnesBefore(int index) {
if (next == null) {
if (index >= BITS_PER_WORD) {
return Long.bitCount(mData);
}
return Long.bitCount(mData & ((1L << index) - 1));
}
if (index < BITS_PER_WORD) {
return Long.bitCount(mData & ((1L << index) - 1));
} else {
return next.countOnesBefore(index - BITS_PER_WORD) + Long.bitCount(mData);
}
}
@Override
public String toString() {
return next == null ? Long.toBinaryString(mData)
: next.toString() + "xx" + Long.toBinaryString(mData);
}
}
static interface Callback {
int getChildCount();
void addView(View child, int index);
int indexOfChild(View view);
void removeViewAt(int index);
View getChildAt(int offset);
void removeAllViews();
RecyclerView.ViewHolder getChildViewHolder(View view);
void attachViewToParent(View child, int index, ViewGroup.LayoutParams layoutParams);
void detachViewFromParent(int offset);
}
}

View File

@ -1,628 +0,0 @@
/*
* Copyright (C) 2014 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 android.support.v7.widget;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPropertyAnimatorCompat;
import android.support.v4.view.ViewPropertyAnimatorListener;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* This implementation of {@link RecyclerView.ItemAnimator} provides basic
* animations on remove, add, and move events that happen to the items in
* a RecyclerView. RecyclerView uses a DefaultItemAnimator by default.
*
* @see RecyclerView#setItemAnimator(RecyclerView.ItemAnimator)
*/
public class DefaultItemAnimator extends RecyclerView.ItemAnimator {
private static final boolean DEBUG = false;
private ArrayList<ViewHolder> mPendingRemovals = new ArrayList<ViewHolder>();
private ArrayList<ViewHolder> mPendingAdditions = new ArrayList<ViewHolder>();
private ArrayList<MoveInfo> mPendingMoves = new ArrayList<MoveInfo>();
private ArrayList<ChangeInfo> mPendingChanges = new ArrayList<ChangeInfo>();
private ArrayList<ArrayList<ViewHolder>> mAdditionsList =
new ArrayList<ArrayList<ViewHolder>>();
private ArrayList<ArrayList<MoveInfo>> mMovesList = new ArrayList<ArrayList<MoveInfo>>();
private ArrayList<ArrayList<ChangeInfo>> mChangesList = new ArrayList<ArrayList<ChangeInfo>>();
private ArrayList<ViewHolder> mAddAnimations = new ArrayList<ViewHolder>();
private ArrayList<ViewHolder> mMoveAnimations = new ArrayList<ViewHolder>();
private ArrayList<ViewHolder> mRemoveAnimations = new ArrayList<ViewHolder>();
private ArrayList<ViewHolder> mChangeAnimations = new ArrayList<ViewHolder>();
private static class MoveInfo {
public ViewHolder holder;
public int fromX, fromY, toX, toY;
private MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) {
this.holder = holder;
this.fromX = fromX;
this.fromY = fromY;
this.toX = toX;
this.toY = toY;
}
}
private static class ChangeInfo {
public ViewHolder oldHolder, newHolder;
public int fromX, fromY, toX, toY;
private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) {
this.oldHolder = oldHolder;
this.newHolder = newHolder;
}
private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder,
int fromX, int fromY, int toX, int toY) {
this(oldHolder, newHolder);
this.fromX = fromX;
this.fromY = fromY;
this.toX = toX;
this.toY = toY;
}
@Override
public String toString() {
return "ChangeInfo{" +
"oldHolder=" + oldHolder +
", newHolder=" + newHolder +
", fromX=" + fromX +
", fromY=" + fromY +
", toX=" + toX +
", toY=" + toY +
'}';
}
}
@Override
public void runPendingAnimations() {
boolean removalsPending = !mPendingRemovals.isEmpty();
boolean movesPending = !mPendingMoves.isEmpty();
boolean changesPending = !mPendingChanges.isEmpty();
boolean additionsPending = !mPendingAdditions.isEmpty();
if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
// nothing to animate
return;
}
// First, remove stuff
for (ViewHolder holder : mPendingRemovals) {
animateRemoveImpl(holder);
}
mPendingRemovals.clear();
// Next, move stuff
if (movesPending) {
final ArrayList<MoveInfo> moves = new ArrayList<MoveInfo>();
moves.addAll(mPendingMoves);
mMovesList.add(moves);
mPendingMoves.clear();
Runnable mover = new Runnable() {
@Override
public void run() {
for (MoveInfo moveInfo : moves) {
animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
moveInfo.toX, moveInfo.toY);
}
moves.clear();
mMovesList.remove(moves);
}
};
if (removalsPending) {
View view = moves.get(0).holder.itemView;
ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
} else {
mover.run();
}
}
// Next, change stuff, to run in parallel with move animations
if (changesPending) {
final ArrayList<ChangeInfo> changes = new ArrayList<ChangeInfo>();
changes.addAll(mPendingChanges);
mChangesList.add(changes);
mPendingChanges.clear();
Runnable changer = new Runnable() {
@Override
public void run() {
for (ChangeInfo change : changes) {
animateChangeImpl(change);
}
changes.clear();
mChangesList.remove(changes);
}
};
if (removalsPending) {
ViewHolder holder = changes.get(0).oldHolder;
ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
} else {
changer.run();
}
}
// Next, add stuff
if (additionsPending) {
final ArrayList<ViewHolder> additions = new ArrayList<ViewHolder>();
additions.addAll(mPendingAdditions);
mAdditionsList.add(additions);
mPendingAdditions.clear();
Runnable adder = new Runnable() {
public void run() {
for (ViewHolder holder : additions) {
animateAddImpl(holder);
}
additions.clear();
mAdditionsList.remove(additions);
}
};
if (removalsPending || movesPending || changesPending) {
long removeDuration = removalsPending ? getRemoveDuration() : 0;
long moveDuration = movesPending ? getMoveDuration() : 0;
long changeDuration = changesPending ? getChangeDuration() : 0;
long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
View view = additions.get(0).itemView;
ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
} else {
adder.run();
}
}
}
@Override
public boolean animateRemove(final ViewHolder holder) {
endAnimation(holder);
mPendingRemovals.add(holder);
return true;
}
private void animateRemoveImpl(final ViewHolder holder) {
final View view = holder.itemView;
final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
animation.setDuration(getRemoveDuration())
.alpha(0).setListener(new VpaListenerAdapter() {
@Override
public void onAnimationStart(View view) {
dispatchRemoveStarting(holder);
}
@Override
public void onAnimationEnd(View view) {
animation.setListener(null);
ViewCompat.setAlpha(view, 1);
dispatchRemoveFinished(holder);
mRemoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
mRemoveAnimations.add(holder);
}
@Override
public boolean animateAdd(final ViewHolder holder) {
endAnimation(holder);
ViewCompat.setAlpha(holder.itemView, 0);
mPendingAdditions.add(holder);
return true;
}
private void animateAddImpl(final ViewHolder holder) {
final View view = holder.itemView;
mAddAnimations.add(holder);
final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
animation.alpha(1).setDuration(getAddDuration()).
setListener(new VpaListenerAdapter() {
@Override
public void onAnimationStart(View view) {
dispatchAddStarting(holder);
}
@Override
public void onAnimationCancel(View view) {
ViewCompat.setAlpha(view, 1);
}
@Override
public void onAnimationEnd(View view) {
animation.setListener(null);
dispatchAddFinished(holder);
mAddAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
@Override
public boolean animateMove(final ViewHolder holder, int fromX, int fromY,
int toX, int toY) {
final View view = holder.itemView;
fromX += ViewCompat.getTranslationX(holder.itemView);
fromY += ViewCompat.getTranslationY(holder.itemView);
endAnimation(holder);
int deltaX = toX - fromX;
int deltaY = toY - fromY;
if (deltaX == 0 && deltaY == 0) {
dispatchMoveFinished(holder);
return false;
}
if (deltaX != 0) {
ViewCompat.setTranslationX(view, -deltaX);
}
if (deltaY != 0) {
ViewCompat.setTranslationY(view, -deltaY);
}
mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY));
return true;
}
private void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) {
final View view = holder.itemView;
final int deltaX = toX - fromX;
final int deltaY = toY - fromY;
if (deltaX != 0) {
ViewCompat.animate(view).translationX(0);
}
if (deltaY != 0) {
ViewCompat.animate(view).translationY(0);
}
// TODO: make EndActions end listeners instead, since end actions aren't called when
// vpas are canceled (and can't end them. why?)
// need listener functionality in VPACompat for this. Ick.
mMoveAnimations.add(holder);
final ViewPropertyAnimatorCompat animation = ViewCompat.animate(view);
animation.setDuration(getMoveDuration()).setListener(new VpaListenerAdapter() {
@Override
public void onAnimationStart(View view) {
dispatchMoveStarting(holder);
}
@Override
public void onAnimationCancel(View view) {
if (deltaX != 0) {
ViewCompat.setTranslationX(view, 0);
}
if (deltaY != 0) {
ViewCompat.setTranslationY(view, 0);
}
}
@Override
public void onAnimationEnd(View view) {
animation.setListener(null);
dispatchMoveFinished(holder);
mMoveAnimations.remove(holder);
dispatchFinishedWhenDone();
}
}).start();
}
@Override
public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder,
int fromX, int fromY, int toX, int toY) {
final float prevTranslationX = ViewCompat.getTranslationX(oldHolder.itemView);
final float prevTranslationY = ViewCompat.getTranslationY(oldHolder.itemView);
final float prevAlpha = ViewCompat.getAlpha(oldHolder.itemView);
endAnimation(oldHolder);
int deltaX = (int) (toX - fromX - prevTranslationX);
int deltaY = (int) (toY - fromY - prevTranslationY);
// recover prev translation state after ending animation
ViewCompat.setTranslationX(oldHolder.itemView, prevTranslationX);
ViewCompat.setTranslationY(oldHolder.itemView, prevTranslationY);
ViewCompat.setAlpha(oldHolder.itemView, prevAlpha);
if (newHolder != null && newHolder.itemView != null) {
// carry over translation values
endAnimation(newHolder);
ViewCompat.setTranslationX(newHolder.itemView, -deltaX);
ViewCompat.setTranslationY(newHolder.itemView, -deltaY);
ViewCompat.setAlpha(newHolder.itemView, 0);
}
mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY));
return true;
}
private void animateChangeImpl(final ChangeInfo changeInfo) {
final ViewHolder holder = changeInfo.oldHolder;
final View view = holder.itemView;
final ViewHolder newHolder = changeInfo.newHolder;
final View newView = newHolder != null ? newHolder.itemView : null;
mChangeAnimations.add(changeInfo.oldHolder);
final ViewPropertyAnimatorCompat oldViewAnim = ViewCompat.animate(view).setDuration(
getChangeDuration());
oldViewAnim.translationX(changeInfo.toX - changeInfo.fromX);
oldViewAnim.translationY(changeInfo.toY - changeInfo.fromY);
oldViewAnim.alpha(0).setListener(new VpaListenerAdapter() {
@Override
public void onAnimationStart(View view) {
dispatchChangeStarting(changeInfo.oldHolder, true);
}
@Override
public void onAnimationEnd(View view) {
oldViewAnim.setListener(null);
ViewCompat.setAlpha(view, 1);
ViewCompat.setTranslationX(view, 0);
ViewCompat.setTranslationY(view, 0);
dispatchChangeFinished(changeInfo.oldHolder, true);
mChangeAnimations.remove(changeInfo.oldHolder);
dispatchFinishedWhenDone();
}
}).start();
if (newView != null) {
mChangeAnimations.add(changeInfo.newHolder);
final ViewPropertyAnimatorCompat newViewAnimation = ViewCompat.animate(newView);
newViewAnimation.translationX(0).translationY(0).setDuration(getChangeDuration()).
alpha(1).setListener(new VpaListenerAdapter() {
@Override
public void onAnimationStart(View view) {
dispatchChangeStarting(changeInfo.newHolder, false);
}
@Override
public void onAnimationEnd(View view) {
newViewAnimation.setListener(null);
ViewCompat.setAlpha(newView, 1);
ViewCompat.setTranslationX(newView, 0);
ViewCompat.setTranslationY(newView, 0);
dispatchChangeFinished(changeInfo.newHolder, false);
mChangeAnimations.remove(changeInfo.newHolder);
dispatchFinishedWhenDone();
}
}).start();
}
}
private void endChangeAnimation(List<ChangeInfo> infoList, ViewHolder item) {
for (int i = infoList.size() - 1; i >= 0; i--) {
ChangeInfo changeInfo = infoList.get(i);
if (endChangeAnimationIfNecessary(changeInfo, item)) {
if (changeInfo.oldHolder == null && changeInfo.newHolder == null) {
infoList.remove(changeInfo);
}
}
}
}
private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) {
if (changeInfo.oldHolder != null) {
endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder);
}
if (changeInfo.newHolder != null) {
endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder);
}
}
private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) {
boolean oldItem = false;
if (changeInfo.newHolder == item) {
changeInfo.newHolder = null;
} else if (changeInfo.oldHolder == item) {
changeInfo.oldHolder = null;
oldItem = true;
} else {
return false;
}
ViewCompat.setAlpha(item.itemView, 1);
ViewCompat.setTranslationX(item.itemView, 0);
ViewCompat.setTranslationY(item.itemView, 0);
dispatchChangeFinished(item, oldItem);
return true;
}
@Override
public void endAnimation(ViewHolder item) {
final View view = item.itemView;
// this will trigger end callback which should set properties to their target values.
ViewCompat.animate(view).cancel();
// TODO if some other animations are chained to end, how do we cancel them as well?
for (int i = mPendingMoves.size() - 1; i >= 0; i--) {
MoveInfo moveInfo = mPendingMoves.get(i);
if (moveInfo.holder == item) {
ViewCompat.setTranslationY(view, 0);
ViewCompat.setTranslationX(view, 0);
dispatchMoveFinished(item);
mPendingMoves.remove(item);
}
}
endChangeAnimation(mPendingChanges, item);
if (mPendingRemovals.remove(item)) {
ViewCompat.setAlpha(view, 1);
dispatchRemoveFinished(item);
}
if (mPendingAdditions.remove(item)) {
ViewCompat.setAlpha(view, 1);
dispatchAddFinished(item);
}
for (int i = mChangesList.size() - 1; i >= 0; i--) {
ArrayList<ChangeInfo> changes = mChangesList.get(i);
endChangeAnimation(changes, item);
if (changes.isEmpty()) {
mChangesList.remove(changes);
}
}
for (int i = mMovesList.size() - 1; i >= 0; i--) {
ArrayList<MoveInfo> moves = mMovesList.get(i);
for (int j = moves.size() - 1; j >= 0; j--) {
MoveInfo moveInfo = moves.get(j);
if (moveInfo.holder == item) {
ViewCompat.setTranslationY(view, 0);
ViewCompat.setTranslationX(view, 0);
dispatchMoveFinished(item);
moves.remove(j);
if (moves.isEmpty()) {
mMovesList.remove(moves);
}
break;
}
}
}
for (int i = mAdditionsList.size() - 1; i >= 0; i--) {
ArrayList<ViewHolder> additions = mAdditionsList.get(i);
if (additions.remove(item)) {
ViewCompat.setAlpha(view, 1);
dispatchAddFinished(item);
if (additions.isEmpty()) {
mAdditionsList.remove(additions);
}
}
}
// animations should be ended by the cancel above.
if (mRemoveAnimations.remove(item) && DEBUG) {
throw new IllegalStateException("after animation is cancelled, item should not be in "
+ "mRemoveAnimations list");
}
if (mAddAnimations.remove(item) && DEBUG) {
throw new IllegalStateException("after animation is cancelled, item should not be in "
+ "mAddAnimations list");
}
if (mChangeAnimations.remove(item) && DEBUG) {
throw new IllegalStateException("after animation is cancelled, item should not be in "
+ "mChangeAnimations list");
}
if (mMoveAnimations.remove(item) && DEBUG) {
throw new IllegalStateException("after animation is cancelled, item should not be in "
+ "mMoveAnimations list");
}
dispatchFinishedWhenDone();
}
@Override
public boolean isRunning() {
return (!mPendingAdditions.isEmpty() ||
!mPendingChanges.isEmpty() ||
!mPendingMoves.isEmpty() ||
!mPendingRemovals.isEmpty() ||
!mMoveAnimations.isEmpty() ||
!mRemoveAnimations.isEmpty() ||
!mAddAnimations.isEmpty() ||
!mChangeAnimations.isEmpty() ||
!mMovesList.isEmpty() ||
!mAdditionsList.isEmpty() ||
!mChangesList.isEmpty());
}
/**
* Check the state of currently pending and running animations. If there are none
* pending/running, call {@link #dispatchAnimationsFinished()} to notify any
* listeners.
*/
private void dispatchFinishedWhenDone() {
if (!isRunning()) {
dispatchAnimationsFinished();
}
}
@Override
public void endAnimations() {
int count = mPendingMoves.size();
for (int i = count - 1; i >= 0; i--) {
MoveInfo item = mPendingMoves.get(i);
View view = item.holder.itemView;
ViewCompat.setTranslationY(view, 0);
ViewCompat.setTranslationX(view, 0);
dispatchMoveFinished(item.holder);
mPendingMoves.remove(i);
}
count = mPendingRemovals.size();
for (int i = count - 1; i >= 0; i--) {
ViewHolder item = mPendingRemovals.get(i);
dispatchRemoveFinished(item);
mPendingRemovals.remove(i);
}
count = mPendingAdditions.size();
for (int i = count - 1; i >= 0; i--) {
ViewHolder item = mPendingAdditions.get(i);
View view = item.itemView;
ViewCompat.setAlpha(view, 1);
dispatchAddFinished(item);
mPendingAdditions.remove(i);
}
count = mPendingChanges.size();
for (int i = count - 1; i >= 0; i--) {
endChangeAnimationIfNecessary(mPendingChanges.get(i));
}
mPendingChanges.clear();
if (!isRunning()) {
return;
}
int listCount = mMovesList.size();
for (int i = listCount - 1; i >= 0; i--) {
ArrayList<MoveInfo> moves = mMovesList.get(i);
count = moves.size();
for (int j = count - 1; j >= 0; j--) {
MoveInfo moveInfo = moves.get(j);
ViewHolder item = moveInfo.holder;
View view = item.itemView;
ViewCompat.setTranslationY(view, 0);
ViewCompat.setTranslationX(view, 0);
dispatchMoveFinished(moveInfo.holder);
moves.remove(j);
if (moves.isEmpty()) {
mMovesList.remove(moves);
}
}
}
listCount = mAdditionsList.size();
for (int i = listCount - 1; i >= 0; i--) {
ArrayList<ViewHolder> additions = mAdditionsList.get(i);
count = additions.size();
for (int j = count - 1; j >= 0; j--) {
ViewHolder item = additions.get(j);
View view = item.itemView;
ViewCompat.setAlpha(view, 1);
dispatchAddFinished(item);
additions.remove(j);
if (additions.isEmpty()) {
mAdditionsList.remove(additions);
}
}
}
listCount = mChangesList.size();
for (int i = listCount - 1; i >= 0; i--) {
ArrayList<ChangeInfo> changes = mChangesList.get(i);
count = changes.size();
for (int j = count - 1; j >= 0; j--) {
endChangeAnimationIfNecessary(changes.get(j));
if (changes.isEmpty()) {
mChangesList.remove(changes);
}
}
}
cancelAll(mRemoveAnimations);
cancelAll(mMoveAnimations);
cancelAll(mAddAnimations);
cancelAll(mChangeAnimations);
dispatchAnimationsFinished();
}
void cancelAll(List<ViewHolder> viewHolders) {
for (int i = viewHolders.size() - 1; i >= 0; i--) {
ViewCompat.animate(viewHolders.get(i).itemView).cancel();
}
}
private static class VpaListenerAdapter implements ViewPropertyAnimatorListener {
@Override
public void onAnimationStart(View view) {}
@Override
public void onAnimationEnd(View view) {}
@Override
public void onAnimationCancel(View view) {}
};
}

View File

@ -1,816 +0,0 @@
/*
* Copyright (C) 2014 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 languag`e governing permissions and
* limitations under the License.
*/
package android.support.v7.widget;
import android.content.Context;
import android.graphics.Rect;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseIntArray;
import android.view.View;
import android.view.ViewGroup;
import java.util.Arrays;
/**
* A {@link RecyclerView.LayoutManager} implementations that lays out items in a grid.
* <p>
* By default, each item occupies 1 span. You can change it by providing a custom
* {@link SpanSizeLookup} instance via {@link #setSpanSizeLookup(SpanSizeLookup)}.
*/
public class GridLayoutManager extends LinearLayoutManager {
private static final boolean DEBUG = false;
private static final String TAG = "GridLayoutManager";
public static final int DEFAULT_SPAN_COUNT = -1;
/**
* The measure spec for the scroll direction.
*/
static final int MAIN_DIR_SPEC =
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
int mSpanCount = DEFAULT_SPAN_COUNT;
/**
* The size of each span
*/
int mSizePerSpan;
/**
* Temporary array to keep views in layoutChunk method
*/
View[] mSet;
final SparseIntArray mPreLayoutSpanSizeCache = new SparseIntArray();
final SparseIntArray mPreLayoutSpanIndexCache = new SparseIntArray();
SpanSizeLookup mSpanSizeLookup = new DefaultSpanSizeLookup();
// re-used variable to acquire decor insets from RecyclerView
final Rect mDecorInsets = new Rect();
/**
* Creates a vertical GridLayoutManager
*
* @param context Current context, will be used to access resources.
* @param spanCount The number of columns in the grid
*/
public GridLayoutManager(Context context, int spanCount) {
super(context);
setSpanCount(spanCount);
}
/**
* @param context Current context, will be used to access resources.
* @param spanCount The number of columns or rows in the grid
* @param orientation Layout orientation. Should be {@link #HORIZONTAL} or {@link
* #VERTICAL}.
* @param reverseLayout When set to true, layouts from end to start.
*/
public GridLayoutManager(Context context, int spanCount, int orientation,
boolean reverseLayout) {
super(context, orientation, reverseLayout);
setSpanCount(spanCount);
}
/**
* stackFromEnd is not supported by GridLayoutManager. Consider using
* {@link #setReverseLayout(boolean)}.
*/
@Override
public void setStackFromEnd(boolean stackFromEnd) {
if (stackFromEnd) {
throw new UnsupportedOperationException(
"GridLayoutManager does not support stack from end."
+ " Consider using reverse layout");
}
super.setStackFromEnd(false);
}
@Override
public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
RecyclerView.State state) {
if (mOrientation == HORIZONTAL) {
return mSpanCount;
}
if (state.getItemCount() < 1) {
return 0;
}
return getSpanGroupIndex(recycler, state, state.getItemCount() - 1);
}
@Override
public int getColumnCountForAccessibility(RecyclerView.Recycler recycler,
RecyclerView.State state) {
if (mOrientation == VERTICAL) {
return mSpanCount;
}
if (state.getItemCount() < 1) {
return 0;
}
return getSpanGroupIndex(recycler, state, state.getItemCount() - 1);
}
@Override
public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {
ViewGroup.LayoutParams lp = host.getLayoutParams();
if (!(lp instanceof LayoutParams)) {
super.onInitializeAccessibilityNodeInfoForItem(host, info);
return;
}
LayoutParams glp = (LayoutParams) lp;
int spanGroupIndex = getSpanGroupIndex(recycler, state, glp.getViewPosition());
if (mOrientation == HORIZONTAL) {
info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
glp.getSpanIndex(), glp.getSpanSize(),
spanGroupIndex, 1,
mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
} else { // VERTICAL
info.setCollectionItemInfo(AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(
spanGroupIndex , 1,
glp.getSpanIndex(), glp.getSpanSize(),
mSpanCount > 1 && glp.getSpanSize() == mSpanCount, false));
}
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (state.isPreLayout()) {
cachePreLayoutSpanMapping();
}
super.onLayoutChildren(recycler, state);
if (DEBUG) {
validateChildOrder();
}
clearPreLayoutSpanMappingCache();
}
private void clearPreLayoutSpanMappingCache() {
mPreLayoutSpanSizeCache.clear();
mPreLayoutSpanIndexCache.clear();
}
private void cachePreLayoutSpanMapping() {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();
final int viewPosition = lp.getViewPosition();
mPreLayoutSpanSizeCache.put(viewPosition, lp.getSpanSize());
mPreLayoutSpanIndexCache.put(viewPosition, lp.getSpanIndex());
}
}
@Override
public void onItemsAdded(RecyclerView recyclerView, int positionStart, int itemCount) {
mSpanSizeLookup.invalidateSpanIndexCache();
}
@Override
public void onItemsChanged(RecyclerView recyclerView) {
mSpanSizeLookup.invalidateSpanIndexCache();
}
@Override
public void onItemsRemoved(RecyclerView recyclerView, int positionStart, int itemCount) {
mSpanSizeLookup.invalidateSpanIndexCache();
}
@Override
public void onItemsUpdated(RecyclerView recyclerView, int positionStart, int itemCount) {
mSpanSizeLookup.invalidateSpanIndexCache();
}
@Override
public void onItemsMoved(RecyclerView recyclerView, int from, int to, int itemCount) {
mSpanSizeLookup.invalidateSpanIndexCache();
}
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public RecyclerView.LayoutParams generateLayoutParams(Context c, AttributeSet attrs) {
return new LayoutParams(c, attrs);
}
@Override
public RecyclerView.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
if (lp instanceof ViewGroup.MarginLayoutParams) {
return new LayoutParams((ViewGroup.MarginLayoutParams) lp);
} else {
return new LayoutParams(lp);
}
}
@Override
public boolean checkLayoutParams(RecyclerView.LayoutParams lp) {
return lp instanceof LayoutParams;
}
/**
* Sets the source to get the number of spans occupied by each item in the adapter.
*
* @param spanSizeLookup {@link SpanSizeLookup} instance to be used to query number of spans
* occupied by each item
*/
public void setSpanSizeLookup(SpanSizeLookup spanSizeLookup) {
mSpanSizeLookup = spanSizeLookup;
}
/**
* Returns the current {@link SpanSizeLookup} used by the GridLayoutManager.
*
* @return The current {@link SpanSizeLookup} used by the GridLayoutManager.
*/
public SpanSizeLookup getSpanSizeLookup() {
return mSpanSizeLookup;
}
private void updateMeasurements() {
int totalSpace;
if (getOrientation() == VERTICAL) {
totalSpace = getWidth() - getPaddingRight() - getPaddingLeft();
} else {
totalSpace = getHeight() - getPaddingBottom() - getPaddingTop();
}
mSizePerSpan = totalSpace / mSpanCount;
}
@Override
void onAnchorReady(RecyclerView.State state, AnchorInfo anchorInfo) {
super.onAnchorReady(state, anchorInfo);
updateMeasurements();
if (state.getItemCount() > 0 && !state.isPreLayout()) {
ensureAnchorIsInFirstSpan(anchorInfo);
}
if (mSet == null || mSet.length != mSpanCount) {
mSet = new View[mSpanCount];
}
}
private void ensureAnchorIsInFirstSpan(AnchorInfo anchorInfo) {
int span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount);
while (span > 0 && anchorInfo.mPosition > 0) {
anchorInfo.mPosition--;
span = mSpanSizeLookup.getCachedSpanIndex(anchorInfo.mPosition, mSpanCount);
}
}
private int getSpanGroupIndex(RecyclerView.Recycler recycler, RecyclerView.State state,
int viewPosition) {
if (!state.isPreLayout()) {
return mSpanSizeLookup.getSpanGroupIndex(viewPosition, mSpanCount);
}
final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(viewPosition);
if (adapterPosition == -1) {
if (DEBUG) {
throw new RuntimeException("Cannot find span group index for position "
+ viewPosition);
}
Log.w(TAG, "Cannot find span size for pre layout position. " + viewPosition);
return 0;
}
return mSpanSizeLookup.getSpanGroupIndex(adapterPosition, mSpanCount);
}
private int getSpanIndex(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {
if (!state.isPreLayout()) {
return mSpanSizeLookup.getCachedSpanIndex(pos, mSpanCount);
}
final int cached = mPreLayoutSpanIndexCache.get(pos, -1);
if (cached != -1) {
return cached;
}
final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);
if (adapterPosition == -1) {
if (DEBUG) {
throw new RuntimeException("Cannot find span index for pre layout position. It is"
+ " not cached, not in the adapter. Pos:" + pos);
}
Log.w(TAG, "Cannot find span size for pre layout position. It is"
+ " not cached, not in the adapter. Pos:" + pos);
return 0;
}
return mSpanSizeLookup.getCachedSpanIndex(adapterPosition, mSpanCount);
}
private int getSpanSize(RecyclerView.Recycler recycler, RecyclerView.State state, int pos) {
if (!state.isPreLayout()) {
return mSpanSizeLookup.getSpanSize(pos);
}
final int cached = mPreLayoutSpanSizeCache.get(pos, -1);
if (cached != -1) {
return cached;
}
final int adapterPosition = recycler.convertPreLayoutPositionToPostLayout(pos);
if (adapterPosition == -1) {
if (DEBUG) {
throw new RuntimeException("Cannot find span size for pre layout position. It is"
+ " not cached, not in the adapter. Pos:" + pos);
}
Log.w(TAG, "Cannot find span size for pre layout position. It is"
+ " not cached, not in the adapter. Pos:" + pos);
return 1;
}
return mSpanSizeLookup.getSpanSize(adapterPosition);
}
@Override
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
final boolean layingOutInPrimaryDirection =
layoutState.mItemDirection == LayoutState.ITEM_DIRECTION_TAIL;
int count = 0;
int consumedSpanCount = 0;
int remainingSpan = mSpanCount;
if (!layingOutInPrimaryDirection) {
int itemSpanIndex = getSpanIndex(recycler, state, layoutState.mCurrentPosition);
int itemSpanSize = getSpanSize(recycler, state, layoutState.mCurrentPosition);
remainingSpan = itemSpanIndex + itemSpanSize;
}
while (count < mSpanCount && layoutState.hasMore(state) && remainingSpan > 0) {
int pos = layoutState.mCurrentPosition;
final int spanSize = getSpanSize(recycler, state, pos);
if (spanSize > mSpanCount) {
throw new IllegalArgumentException("Item at position " + pos + " requires " +
spanSize + " spans but GridLayoutManager has only " + mSpanCount
+ " spans.");
}
remainingSpan -= spanSize;
if (remainingSpan < 0) {
break; // item did not fit into this row or column
}
View view = layoutState.next(recycler);
if (view == null) {
break;
}
consumedSpanCount += spanSize;
mSet[count] = view;
count++;
}
if (count == 0) {
result.mFinished = true;
return;
}
int maxSize = 0;
// we should assign spans before item decor offsets are calculated
assignSpans(recycler, state, count, consumedSpanCount, layingOutInPrimaryDirection);
for (int i = 0; i < count; i++) {
View view = mSet[i];
if (layoutState.mScrapList == null) {
if (layingOutInPrimaryDirection) {
addView(view);
} else {
addView(view, 0);
}
} else {
if (layingOutInPrimaryDirection) {
addDisappearingView(view);
} else {
addDisappearingView(view, 0);
}
}
int spanSize = getSpanSize(recycler, state, getPosition(view));
final int spec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan * spanSize,
View.MeasureSpec.EXACTLY);
final LayoutParams lp = (LayoutParams) view.getLayoutParams();
if (mOrientation == VERTICAL) {
measureChildWithDecorationsAndMargin(view, spec, getMainDirSpec(lp.height));
} else {
measureChildWithDecorationsAndMargin(view, getMainDirSpec(lp.width), spec);
}
final int size = mOrientationHelper.getDecoratedMeasurement(view);
if (size > maxSize) {
maxSize = size;
}
}
// views that did not measure the maxSize has to be re-measured
final int maxMeasureSpec = getMainDirSpec(maxSize);
for (int i = 0; i < count; i ++) {
final View view = mSet[i];
if (mOrientationHelper.getDecoratedMeasurement(view) != maxSize) {
int spanSize = getSpanSize(recycler, state, getPosition(view));
final int spec = View.MeasureSpec.makeMeasureSpec(mSizePerSpan * spanSize,
View.MeasureSpec.EXACTLY);
if (mOrientation == VERTICAL) {
measureChildWithDecorationsAndMargin(view, spec, maxMeasureSpec);
} else {
measureChildWithDecorationsAndMargin(view, maxMeasureSpec, spec);
}
}
}
result.mConsumed = maxSize;
int left = 0, right = 0, top = 0, bottom = 0;
if (mOrientation == VERTICAL) {
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
bottom = layoutState.mOffset;
top = bottom - maxSize;
} else {
top = layoutState.mOffset;
bottom = top + maxSize;
}
} else {
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
right = layoutState.mOffset;
left = right - maxSize;
} else {
left = layoutState.mOffset;
right = left + maxSize;
}
}
for (int i = 0; i < count; i++) {
View view = mSet[i];
LayoutParams params = (LayoutParams) view.getLayoutParams();
if (mOrientation == VERTICAL) {
left = getPaddingLeft() + mSizePerSpan * params.mSpanIndex;
right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
} else {
top = getPaddingTop() + mSizePerSpan * params.mSpanIndex;
bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);
}
// We calculate everything with View's bounding box (which includes decor and margins)
// To calculate correct layout position, we subtract margins.
layoutDecorated(view, left + params.leftMargin, top + params.topMargin,
right - params.rightMargin, bottom - params.bottomMargin);
if (DEBUG) {
Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
+ (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
+ (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin)
+ ", span:" + params.mSpanIndex + ", spanSize:" + params.mSpanSize);
}
// Consume the available space if the view is not removed OR changed
if (params.isItemRemoved() || params.isItemChanged()) {
result.mIgnoreConsumed = true;
}
result.mFocusable |= view.isFocusable();
}
Arrays.fill(mSet, null);
}
private int getMainDirSpec(int dim) {
if (dim < 0) {
return MAIN_DIR_SPEC;
} else {
return View.MeasureSpec.makeMeasureSpec(dim, View.MeasureSpec.EXACTLY);
}
}
private void measureChildWithDecorationsAndMargin(View child, int widthSpec, int heightSpec) {
calculateItemDecorationsForChild(child, mDecorInsets);
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
widthSpec = updateSpecWithExtra(widthSpec, lp.leftMargin + mDecorInsets.left,
lp.rightMargin + mDecorInsets.right);
heightSpec = updateSpecWithExtra(heightSpec, lp.topMargin + mDecorInsets.top,
lp.bottomMargin + mDecorInsets.bottom);
child.measure(widthSpec, heightSpec);
}
private int updateSpecWithExtra(int spec, int startInset, int endInset) {
if (startInset == 0 && endInset == 0) {
return spec;
}
final int mode = View.MeasureSpec.getMode(spec);
if (mode == View.MeasureSpec.AT_MOST || mode == View.MeasureSpec.EXACTLY) {
return View.MeasureSpec.makeMeasureSpec(
View.MeasureSpec.getSize(spec) - startInset - endInset, mode);
}
return spec;
}
private void assignSpans(RecyclerView.Recycler recycler, RecyclerView.State state, int count,
int consumedSpanCount, boolean layingOutInPrimaryDirection) {
int span, spanDiff, start, end, diff;
// make sure we traverse from min position to max position
if (layingOutInPrimaryDirection) {
start = 0;
end = count;
diff = 1;
} else {
start = count - 1;
end = -1;
diff = -1;
}
if (mOrientation == VERTICAL && isLayoutRTL()) { // start from last span
span = consumedSpanCount - 1;
spanDiff = -1;
} else {
span = 0;
spanDiff = 1;
}
for (int i = start; i != end; i += diff) {
View view = mSet[i];
LayoutParams params = (LayoutParams) view.getLayoutParams();
params.mSpanSize = getSpanSize(recycler, state, getPosition(view));
if (spanDiff == -1 && params.mSpanSize > 1) {
params.mSpanIndex = span - (params.mSpanSize - 1);
} else {
params.mSpanIndex = span;
}
span += spanDiff * params.mSpanSize;
}
}
/**
* Returns the number of spans laid out by this grid.
*
* @return The number of spans
* @see #setSpanCount(int)
*/
public int getSpanCount() {
return mSpanCount;
}
/**
* Sets the number of spans to be laid out.
* <p>
* If {@link #getOrientation()} is {@link #VERTICAL}, this is the number of columns.
* If {@link #getOrientation()} is {@link #HORIZONTAL}, this is the number of rows.
*
* @param spanCount The total number of spans in the grid
* @see #getSpanCount()
*/
public void setSpanCount(int spanCount) {
if (spanCount == mSpanCount) {
return;
}
if (spanCount < 1) {
throw new IllegalArgumentException("Span count should be at least 1. Provided "
+ spanCount);
}
mSpanCount = spanCount;
mSpanSizeLookup.invalidateSpanIndexCache();
}
/**
* A helper class to provide the number of spans each item occupies.
* <p>
* Default implementation sets each item to occupy exactly 1 span.
*
* @see GridLayoutManager#setSpanSizeLookup(SpanSizeLookup)
*/
public static abstract class SpanSizeLookup {
final SparseIntArray mSpanIndexCache = new SparseIntArray();
private boolean mCacheSpanIndices = false;
/**
* Returns the number of span occupied by the item at <code>position</code>.
*
* @param position The adapter position of the item
* @return The number of spans occupied by the item at the provided position
*/
abstract public int getSpanSize(int position);
/**
* Sets whether the results of {@link #getSpanIndex(int, int)} method should be cached or
* not. By default these values are not cached. If you are not overriding
* {@link #getSpanIndex(int, int)}, you should set this to true for better performance.
*
* @param cacheSpanIndices Whether results of getSpanIndex should be cached or not.
*/
public void setSpanIndexCacheEnabled(boolean cacheSpanIndices) {
mCacheSpanIndices = cacheSpanIndices;
}
/**
* Clears the span index cache. GridLayoutManager automatically calls this method when
* adapter changes occur.
*/
public void invalidateSpanIndexCache() {
mSpanIndexCache.clear();
}
/**
* Returns whether results of {@link #getSpanIndex(int, int)} method are cached or not.
*
* @return True if results of {@link #getSpanIndex(int, int)} are cached.
*/
public boolean isSpanIndexCacheEnabled() {
return mCacheSpanIndices;
}
int getCachedSpanIndex(int position, int spanCount) {
if (!mCacheSpanIndices) {
return getSpanIndex(position, spanCount);
}
final int existing = mSpanIndexCache.get(position, -1);
if (existing != -1) {
return existing;
}
final int value = getSpanIndex(position, spanCount);
mSpanIndexCache.put(position, value);
return value;
}
/**
* Returns the final span index of the provided position.
* <p>
* If you have a faster way to calculate span index for your items, you should override
* this method. Otherwise, you should enable span index cache
* ({@link #setSpanIndexCacheEnabled(boolean)}) for better performance. When caching is
* disabled, default implementation traverses all items from 0 to
* <code>position</code>. When caching is enabled, it calculates from the closest cached
* value before the <code>position</code>.
* <p>
* If you override this method, you need to make sure it is consistent with
* {@link #getSpanSize(int)}. GridLayoutManager does not call this method for
* each item. It is called only for the reference item and rest of the items
* are assigned to spans based on the reference item. For example, you cannot assign a
* position to span 2 while span 1 is empty.
* <p>
* Note that span offsets always start with 0 and are not affected by RTL.
*
* @param position The position of the item
* @param spanCount The total number of spans in the grid
* @return The final span position of the item. Should be between 0 (inclusive) and
* <code>spanCount</code>(exclusive)
*/
public int getSpanIndex(int position, int spanCount) {
int positionSpanSize = getSpanSize(position);
if (positionSpanSize == spanCount) {
return 0; // quick return for full-span items
}
int span = 0;
int startPos = 0;
// If caching is enabled, try to jump
if (mCacheSpanIndices && mSpanIndexCache.size() > 0) {
int prevKey = findReferenceIndexFromCache(position);
if (prevKey >= 0) {
span = mSpanIndexCache.get(prevKey) + getSpanSize(prevKey);
startPos = prevKey + 1;
}
}
for (int i = startPos; i < position; i++) {
int size = getSpanSize(i);
span += size;
if (span == spanCount) {
span = 0;
} else if (span > spanCount) {
// did not fit, moving to next row / column
span = size;
}
}
if (span + positionSpanSize <= spanCount) {
return span;
}
return 0;
}
int findReferenceIndexFromCache(int position) {
int lo = 0;
int hi = mSpanIndexCache.size() - 1;
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
final int midVal = mSpanIndexCache.keyAt(mid);
if (midVal < position) {
lo = mid + 1;
} else {
hi = mid - 1;
}
}
int index = lo - 1;
if (index >= 0 && index < mSpanIndexCache.size()) {
return mSpanIndexCache.keyAt(index);
}
return -1;
}
/**
* Returns the index of the group this position belongs.
* <p>
* For example, if grid has 3 columns and each item occupies 1 span, span group index
* for item 1 will be 0, item 5 will be 1.
*
* @param adapterPosition The position in adapter
* @param spanCount The total number of spans in the grid
* @return The index of the span group including the item at the given adapter position
*/
public int getSpanGroupIndex(int adapterPosition, int spanCount) {
int span = 0;
int group = 0;
int positionSpanSize = getSpanSize(adapterPosition);
for (int i = 0; i < adapterPosition; i++) {
int size = getSpanSize(i);
span += size;
if (span == spanCount) {
span = 0;
group++;
} else if (span > spanCount) {
// did not fit, moving to next row / column
span = size;
group++;
}
}
if (span + positionSpanSize > spanCount) {
group++;
}
return group;
}
}
@Override
public boolean supportsPredictiveItemAnimations() {
return mPendingSavedState == null;
}
/**
* Default implementation for {@link SpanSizeLookup}. Each item occupies 1 span.
*/
public static final class DefaultSpanSizeLookup extends SpanSizeLookup {
@Override
public int getSpanSize(int position) {
return 1;
}
@Override
public int getSpanIndex(int position, int spanCount) {
return position % spanCount;
}
}
/**
* LayoutParams used by GridLayoutManager.
*/
public static class LayoutParams extends RecyclerView.LayoutParams {
/**
* Span Id for Views that are not laid out yet.
*/
public static final int INVALID_SPAN_ID = -1;
private int mSpanIndex = INVALID_SPAN_ID;
private int mSpanSize = 0;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(ViewGroup.MarginLayoutParams source) {
super(source);
}
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(RecyclerView.LayoutParams source) {
super(source);
}
/**
* Returns the current span index of this View. If the View is not laid out yet, the return
* value is <code>undefined</code>.
* <p>
* Note that span index may change by whether the RecyclerView is RTL or not. For
* example, if the number of spans is 3 and layout is RTL, the rightmost item will have
* span index of 2. If the layout changes back to LTR, span index for this view will be 0.
* If the item was occupying 2 spans, span indices would be 1 and 0 respectively.
* <p>
* If the View occupies multiple spans, span with the minimum index is returned.
*
* @return The span index of the View.
*/
public int getSpanIndex() {
return mSpanIndex;
}
/**
* Returns the number of spans occupied by this View. If the View not laid out yet, the
* return value is <code>undefined</code>.
*
* @return The number of spans occupied by this View.
*/
public int getSpanSize() {
return mSpanSize;
}
}
}

View File

@ -1,87 +0,0 @@
/*
* Copyright (C) 2014 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 languag`e governing permissions and
* limitations under the License.
*/
package android.support.v7.widget;
import android.view.View;
/**
* Helper class that keeps temporary state while {LayoutManager} is filling out the empty
* space.
*/
class LayoutState {
final static String TAG = "LayoutState";
final static int LAYOUT_START = -1;
final static int LAYOUT_END = 1;
final static int INVALID_LAYOUT = Integer.MIN_VALUE;
final static int ITEM_DIRECTION_HEAD = -1;
final static int ITEM_DIRECTION_TAIL = 1;
final static int SCOLLING_OFFSET_NaN = Integer.MIN_VALUE;
/**
* Number of pixels that we should fill, in the layout direction.
*/
int mAvailable;
/**
* Current position on the adapter to get the next item.
*/
int mCurrentPosition;
/**
* Defines the direction in which the data adapter is traversed.
* Should be {@link #ITEM_DIRECTION_HEAD} or {@link #ITEM_DIRECTION_TAIL}
*/
int mItemDirection;
/**
* Defines the direction in which the layout is filled.
* Should be {@link #LAYOUT_START} or {@link #LAYOUT_END}
*/
int mLayoutDirection;
/**
* Used if you want to pre-layout items that are not yet visible.
* The difference with {@link #mAvailable} is that, when recycling, distance rendered for
* {@link #mExtra} is not considered not to recycle visible children.
*/
int mExtra = 0;
/**
* @return true if there are more items in the data adapter
*/
boolean hasMore(RecyclerView.State state) {
return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount();
}
/**
* Gets the view for the next element that we should render.
* Also updates current item index to the next item, based on {@link #mItemDirection}
*
* @return The next element that we should render.
*/
View next(RecyclerView.Recycler recycler) {
final View view = recycler.getViewForPosition(mCurrentPosition);
mCurrentPosition += mItemDirection;
return view;
}
}

View File

@ -1,338 +0,0 @@
/*
* Copyright (C) 2014 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 android.support.v7.widget;
import android.content.Context;
import android.graphics.PointF;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
/**
* {@link RecyclerView.SmoothScroller} implementation which uses
* {@link LinearInterpolator} until the target position becames a child of
* the RecyclerView and then uses
* {@link DecelerateInterpolator} to slowly approach to target position.
*/
abstract public class LinearSmoothScroller extends RecyclerView.SmoothScroller {
private static final String TAG = "LinearSmoothScroller";
private static final boolean DEBUG = false;
private static final float MILLISECONDS_PER_INCH = 25f;
private static final int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000;
/**
* Align child view's left or top with parent view's left or top
*
* @see #calculateDtToFit(int, int, int, int, int)
* @see #calculateDxToMakeVisible(View, int)
* @see #calculateDyToMakeVisible(View, int)
*/
public static final int SNAP_TO_START = -1;
/**
* Align child view's right or bottom with parent view's right or bottom
*
* @see #calculateDtToFit(int, int, int, int, int)
* @see #calculateDxToMakeVisible(View, int)
* @see #calculateDyToMakeVisible(View, int)
*/
public static final int SNAP_TO_END = 1;
/**
* <p>Decides if the child should be snapped from start or end, depending on where it
* currently is in relation to its parent.</p>
* <p>For instance, if the view is virtually on the left of RecyclerView, using
* {@code SNAP_TO_ANY} is the same as using {@code SNAP_TO_START}</p>
*
* @see #calculateDtToFit(int, int, int, int, int)
* @see #calculateDxToMakeVisible(View, int)
* @see #calculateDyToMakeVisible(View, int)
*/
public static final int SNAP_TO_ANY = 0;
// Trigger a scroll to a further distance than TARGET_SEEK_SCROLL_DISTANCE_PX so that if target
// view is not laid out until interim target position is reached, we can detect the case before
// scrolling slows down and reschedule another interim target scroll
private static final float TARGET_SEEK_EXTRA_SCROLL_RATIO = 1.2f;
protected final LinearInterpolator mLinearInterpolator = new LinearInterpolator();
protected final DecelerateInterpolator mDecelerateInterpolator = new DecelerateInterpolator();
protected PointF mTargetVector;
private final float MILLISECONDS_PER_PX;
// Temporary variables to keep track of the interim scroll target. These values do not
// point to a real item position, rather point to an estimated location pixels.
protected int mInterimTargetDx = 0, mInterimTargetDy = 0;
public LinearSmoothScroller(Context context) {
MILLISECONDS_PER_PX = calculateSpeedPerPixel(context.getResources().getDisplayMetrics());
}
/**
* {@inheritDoc}
*/
@Override
protected void onStart() {
}
/**
* {@inheritDoc}
*/
@Override
protected void onTargetFound(View targetView, RecyclerView.State state, Action action) {
final int dx = calculateDxToMakeVisible(targetView, getHorizontalSnapPreference());
final int dy = calculateDyToMakeVisible(targetView, getVerticalSnapPreference());
final int distance = (int) Math.sqrt(dx * dx + dy * dy);
final int time = calculateTimeForDeceleration(distance);
if (time > 0) {
action.update(-dx, -dy, time, mDecelerateInterpolator);
}
}
/**
* {@inheritDoc}
*/
@Override
protected void onSeekTargetStep(int dx, int dy, RecyclerView.State state, Action action) {
if (getChildCount() == 0) {
stop();
return;
}
if (DEBUG && mTargetVector != null
&& ((mTargetVector.x * dx < 0 || mTargetVector.y * dy < 0))) {
throw new IllegalStateException("Scroll happened in the opposite direction"
+ " of the target. Some calculations are wrong");
}
mInterimTargetDx = clampApplyScroll(mInterimTargetDx, dx);
mInterimTargetDy = clampApplyScroll(mInterimTargetDy, dy);
if (mInterimTargetDx == 0 && mInterimTargetDy == 0) {
updateActionForInterimTarget(action);
} // everything is valid, keep going
}
/**
* {@inheritDoc}
*/
@Override
protected void onStop() {
mInterimTargetDx = mInterimTargetDy = 0;
mTargetVector = null;
}
/**
* Calculates the scroll speed.
*
* @param displayMetrics DisplayMetrics to be used for real dimension calculations
* @return The time (in ms) it should take for each pixel. For instance, if returned value is
* 2 ms, it means scrolling 1000 pixels with LinearInterpolation should take 2 seconds.
*/
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
}
/**
* <p>Calculates the time for deceleration so that transition from LinearInterpolator to
* DecelerateInterpolator looks smooth.</p>
*
* @param dx Distance to scroll
* @return Time for DecelerateInterpolator to smoothly traverse the distance when transitioning
* from LinearInterpolation
*/
protected int calculateTimeForDeceleration(int dx) {
// we want to cover same area with the linear interpolator for the first 10% of the
// interpolation. After that, deceleration will take control.
// area under curve (1-(1-x)^2) can be calculated as (1 - x/3) * x * x
// which gives 0.100028 when x = .3356
// this is why we divide linear scrolling time with .3356
return (int) Math.ceil(calculateTimeForScrolling(dx) / .3356);
}
/**
* Calculates the time it should take to scroll the given distance (in pixels)
*
* @param dx Distance in pixels that we want to scroll
* @return Time in milliseconds
* @see #calculateSpeedPerPixel(DisplayMetrics)
*/
protected int calculateTimeForScrolling(int dx) {
// In a case where dx is very small, rounding may return 0 although dx > 0.
// To avoid that issue, ceil the result so that if dx > 0, we'll always return positive
// time.
return (int) Math.ceil(Math.abs(dx) * MILLISECONDS_PER_PX);
}
/**
* When scrolling towards a child view, this method defines whether we should align the left
* or the right edge of the child with the parent RecyclerView.
*
* @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector
* @see #SNAP_TO_START
* @see #SNAP_TO_END
* @see #SNAP_TO_ANY
*/
protected int getHorizontalSnapPreference() {
return mTargetVector == null || mTargetVector.x == 0 ? SNAP_TO_ANY :
mTargetVector.x > 0 ? SNAP_TO_END : SNAP_TO_START;
}
/**
* When scrolling towards a child view, this method defines whether we should align the top
* or the bottom edge of the child with the parent RecyclerView.
*
* @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector
* @see #SNAP_TO_START
* @see #SNAP_TO_END
* @see #SNAP_TO_ANY
*/
protected int getVerticalSnapPreference() {
return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY :
mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START;
}
/**
* When the target scroll position is not a child of the RecyclerView, this method calculates
* a direction vector towards that child and triggers a smooth scroll.
*
* @see #computeScrollVectorForPosition(int)
*/
protected void updateActionForInterimTarget(Action action) {
// find an interim target position
PointF scrollVector = computeScrollVectorForPosition(getTargetPosition());
if (scrollVector == null || (scrollVector.x == 0 && scrollVector.y == 0)) {
Log.e(TAG, "To support smooth scrolling, you should override \n"
+ "LayoutManager#computeScrollVectorForPosition.\n"
+ "Falling back to instant scroll");
final int target = getTargetPosition();
stop();
instantScrollToPosition(target);
return;
}
normalize(scrollVector);
mTargetVector = scrollVector;
mInterimTargetDx = (int) (TARGET_SEEK_SCROLL_DISTANCE_PX * scrollVector.x);
mInterimTargetDy = (int) (TARGET_SEEK_SCROLL_DISTANCE_PX * scrollVector.y);
final int time = calculateTimeForScrolling(TARGET_SEEK_SCROLL_DISTANCE_PX);
// To avoid UI hiccups, trigger a smooth scroll to a distance little further than the
// interim target. Since we track the distance travelled in onSeekTargetStep callback, it
// won't actually scroll more than what we need.
action.update((int) (mInterimTargetDx * TARGET_SEEK_EXTRA_SCROLL_RATIO)
, (int) (mInterimTargetDy * TARGET_SEEK_EXTRA_SCROLL_RATIO)
, (int) (time * TARGET_SEEK_EXTRA_SCROLL_RATIO), mLinearInterpolator);
}
private int clampApplyScroll(int tmpDt, int dt) {
final int before = tmpDt;
tmpDt -= dt;
if (before * tmpDt <= 0) { // changed sign, reached 0 or was 0, reset
return 0;
}
return tmpDt;
}
/**
* Helper method for {@link #calculateDxToMakeVisible(View, int)} and
* {@link #calculateDyToMakeVisible(View, int)}
*/
public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int
snapPreference) {
switch (snapPreference) {
case SNAP_TO_START:
return boxStart - viewStart;
case SNAP_TO_END:
return boxEnd - viewEnd;
case SNAP_TO_ANY:
final int dtStart = boxStart - viewStart;
if (dtStart > 0) {
return dtStart;
}
final int dtEnd = boxEnd - viewEnd;
if (dtEnd < 0) {
return dtEnd;
}
break;
default:
throw new IllegalArgumentException("snap preference should be one of the"
+ " constants defined in SmoothScroller, starting with SNAP_");
}
return 0;
}
/**
* Calculates the vertical scroll amount necessary to make the given view fully visible
* inside the RecyclerView.
*
* @param view The view which we want to make fully visible
* @param snapPreference The edge which the view should snap to when entering the visible
* area. One of {@link #SNAP_TO_START}, {@link #SNAP_TO_END} or
* {@link #SNAP_TO_END}.
* @return The vertical scroll amount necessary to make the view visible with the given
* snap preference.
*/
public int calculateDyToMakeVisible(View view, int snapPreference) {
final RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (!layoutManager.canScrollVertically()) {
return 0;
}
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
final int top = layoutManager.getDecoratedTop(view) - params.topMargin;
final int bottom = layoutManager.getDecoratedBottom(view) + params.bottomMargin;
final int start = layoutManager.getPaddingTop();
final int end = layoutManager.getHeight() - layoutManager.getPaddingBottom();
return calculateDtToFit(top, bottom, start, end, snapPreference);
}
/**
* Calculates the horizontal scroll amount necessary to make the given view fully visible
* inside the RecyclerView.
*
* @param view The view which we want to make fully visible
* @param snapPreference The edge which the view should snap to when entering the visible
* area. One of {@link #SNAP_TO_START}, {@link #SNAP_TO_END} or
* {@link #SNAP_TO_END}
* @return The vertical scroll amount necessary to make the view visible with the given
* snap preference.
*/
public int calculateDxToMakeVisible(View view, int snapPreference) {
final RecyclerView.LayoutManager layoutManager = getLayoutManager();
if (!layoutManager.canScrollHorizontally()) {
return 0;
}
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
final int left = layoutManager.getDecoratedLeft(view) - params.leftMargin;
final int right = layoutManager.getDecoratedRight(view) + params.rightMargin;
final int start = layoutManager.getPaddingLeft();
final int end = layoutManager.getWidth() - layoutManager.getPaddingRight();
return calculateDtToFit(left, right, start, end, snapPreference);
}
abstract public PointF computeScrollVectorForPosition(int targetPosition);
}

View File

@ -1,238 +0,0 @@
/*
* Copyright (C) 2014 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 android.support.v7.widget;
import android.support.v7.widget.AdapterHelper.UpdateOp;
import java.util.List;
import static android.support.v7.widget.AdapterHelper.UpdateOp.ADD;
import static android.support.v7.widget.AdapterHelper.UpdateOp.MOVE;
import static android.support.v7.widget.AdapterHelper.UpdateOp.REMOVE;
import static android.support.v7.widget.AdapterHelper.UpdateOp.UPDATE;
class OpReorderer {
final Callback mCallback;
public OpReorderer(Callback callback) {
mCallback = callback;
}
void reorderOps(List<UpdateOp> ops) {
// since move operations breaks continuity, their effects on ADD/RM are hard to handle.
// we push them to the end of the list so that they can be handled easily.
int badMove;
while ((badMove = getLastMoveOutOfOrder(ops)) != -1) {
swapMoveOp(ops, badMove, badMove + 1);
}
}
private void swapMoveOp(List<UpdateOp> list, int badMove, int next) {
final UpdateOp moveOp = list.get(badMove);
final UpdateOp nextOp = list.get(next);
switch (nextOp.cmd) {
case REMOVE:
swapMoveRemove(list, badMove, moveOp, next, nextOp);
break;
case ADD:
swapMoveAdd(list, badMove, moveOp, next, nextOp);
break;
case UPDATE:
swapMoveUpdate(list, badMove, moveOp, next, nextOp);
break;
}
}
void swapMoveRemove(List<UpdateOp> list, int movePos, UpdateOp moveOp,
int removePos, UpdateOp removeOp) {
UpdateOp extraRm = null;
// check if move is nulled out by remove
boolean revertedMove = false;
final boolean moveIsBackwards;
if (moveOp.positionStart < moveOp.itemCount) {
moveIsBackwards = false;
if (removeOp.positionStart == moveOp.positionStart
&& removeOp.itemCount == moveOp.itemCount - moveOp.positionStart) {
revertedMove = true;
}
} else {
moveIsBackwards = true;
if (removeOp.positionStart == moveOp.itemCount + 1 &&
removeOp.itemCount == moveOp.positionStart - moveOp.itemCount) {
revertedMove = true;
}
}
// going in reverse, first revert the effect of add
if (moveOp.itemCount < removeOp.positionStart) {
removeOp.positionStart--;
} else if (moveOp.itemCount < removeOp.positionStart + removeOp.itemCount) {
// move is removed.
removeOp.itemCount --;
moveOp.cmd = REMOVE;
moveOp.itemCount = 1;
if (removeOp.itemCount == 0) {
list.remove(removePos);
mCallback.recycleUpdateOp(removeOp);
}
// no need to swap, it is already a remove
return;
}
// now affect of add is consumed. now apply effect of first remove
if (moveOp.positionStart <= removeOp.positionStart) {
removeOp.positionStart++;
} else if (moveOp.positionStart < removeOp.positionStart + removeOp.itemCount) {
final int remaining = removeOp.positionStart + removeOp.itemCount
- moveOp.positionStart;
extraRm = mCallback.obtainUpdateOp(REMOVE, moveOp.positionStart + 1, remaining);
removeOp.itemCount = moveOp.positionStart - removeOp.positionStart;
}
// if effects of move is reverted by remove, we are done.
if (revertedMove) {
list.set(movePos, removeOp);
list.remove(removePos);
mCallback.recycleUpdateOp(moveOp);
return;
}
// now find out the new locations for move actions
if (moveIsBackwards) {
if (extraRm != null) {
if (moveOp.positionStart > extraRm.positionStart) {
moveOp.positionStart -= extraRm.itemCount;
}
if (moveOp.itemCount > extraRm.positionStart) {
moveOp.itemCount -= extraRm.itemCount;
}
}
if (moveOp.positionStart > removeOp.positionStart) {
moveOp.positionStart -= removeOp.itemCount;
}
if (moveOp.itemCount > removeOp.positionStart) {
moveOp.itemCount -= removeOp.itemCount;
}
} else {
if (extraRm != null) {
if (moveOp.positionStart >= extraRm.positionStart) {
moveOp.positionStart -= extraRm.itemCount;
}
if (moveOp.itemCount >= extraRm.positionStart) {
moveOp.itemCount -= extraRm.itemCount;
}
}
if (moveOp.positionStart >= removeOp.positionStart) {
moveOp.positionStart -= removeOp.itemCount;
}
if (moveOp.itemCount >= removeOp.positionStart) {
moveOp.itemCount -= removeOp.itemCount;
}
}
list.set(movePos, removeOp);
if (moveOp.positionStart != moveOp.itemCount) {
list.set(removePos, moveOp);
} else {
list.remove(removePos);
}
if (extraRm != null) {
list.add(movePos, extraRm);
}
}
private void swapMoveAdd(List<UpdateOp> list, int move, UpdateOp moveOp, int add,
UpdateOp addOp) {
int offset = 0;
// going in reverse, first revert the effect of add
if (moveOp.itemCount < addOp.positionStart) {
offset--;
}
if (moveOp.positionStart < addOp.positionStart) {
offset++;
}
if (addOp.positionStart <= moveOp.positionStart) {
moveOp.positionStart += addOp.itemCount;
}
if (addOp.positionStart <= moveOp.itemCount) {
moveOp.itemCount += addOp.itemCount;
}
addOp.positionStart += offset;
list.set(move, addOp);
list.set(add, moveOp);
}
void swapMoveUpdate(List<UpdateOp> list, int move, UpdateOp moveOp, int update,
UpdateOp updateOp) {
UpdateOp extraUp1 = null;
UpdateOp extraUp2 = null;
// going in reverse, first revert the effect of add
if (moveOp.itemCount < updateOp.positionStart) {
updateOp.positionStart--;
} else if (moveOp.itemCount < updateOp.positionStart + updateOp.itemCount) {
// moved item is updated. add an update for it
updateOp.itemCount--;
extraUp1 = mCallback.obtainUpdateOp(UPDATE, moveOp.positionStart, 1);
}
// now affect of add is consumed. now apply effect of first remove
if (moveOp.positionStart <= updateOp.positionStart) {
updateOp.positionStart++;
} else if (moveOp.positionStart < updateOp.positionStart + updateOp.itemCount) {
final int remaining = updateOp.positionStart + updateOp.itemCount
- moveOp.positionStart;
extraUp2 = mCallback.obtainUpdateOp(UPDATE, moveOp.positionStart + 1, remaining);
updateOp.itemCount -= remaining;
}
list.set(update, moveOp);
if (updateOp.itemCount > 0) {
list.set(move, updateOp);
} else {
list.remove(move);
mCallback.recycleUpdateOp(updateOp);
}
if (extraUp1 != null) {
list.add(move, extraUp1);
}
if (extraUp2 != null) {
list.add(move, extraUp2);
}
}
private int getLastMoveOutOfOrder(List<UpdateOp> list) {
boolean foundNonMove = false;
for (int i = list.size() - 1; i >= 0; i--) {
final UpdateOp op1 = list.get(i);
if (op1.cmd == MOVE) {
if (foundNonMove) {
return i;
}
} else {
foundNonMove = true;
}
}
return -1;
}
static interface Callback {
UpdateOp obtainUpdateOp(int cmd, int startPosition, int itemCount);
void recycleUpdateOp(UpdateOp op);
}
}

View File

@ -1,338 +0,0 @@
/*
* Copyright (C) 2014 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 android.support.v7.widget;
import android.view.View;
import android.widget.LinearLayout;
/**
* Helper class for LayoutManagers to abstract measurements depending on the View's orientation.
* <p>
* It is developed to easily support vertical and horizontal orientations in a LayoutManager but
* can also be used to abstract calls around view bounds and child measurements with margins and
* decorations.
*
* @see #createHorizontalHelper(RecyclerView.LayoutManager)
* @see #createVerticalHelper(RecyclerView.LayoutManager)
*/
public abstract class OrientationHelper {
private static final int INVALID_SIZE = Integer.MIN_VALUE;
protected final RecyclerView.LayoutManager mLayoutManager;
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL;
private int mLastTotalSpace = INVALID_SIZE;
private OrientationHelper(RecyclerView.LayoutManager layoutManager) {
mLayoutManager = layoutManager;
}
/**
* Call this method after onLayout method is complete if state is NOT pre-layout.
* This method records information like layout bounds that might be useful in the next layout
* calculations.
*/
public void onLayoutComplete() {
mLastTotalSpace = getTotalSpace();
}
/**
* Returns the layout space change between the previous layout pass and current layout pass.
* <p>
* Make sure you call {@link #onLayoutComplete()} at the end of your LayoutManager's
* {@link RecyclerView.LayoutManager#onLayoutChildren(RecyclerView.Recycler,
* RecyclerView.State)} method.
*
* @return The difference between the current total space and previous layout's total space.
* @see #onLayoutComplete()
*/
public int getTotalSpaceChange() {
return INVALID_SIZE == mLastTotalSpace ? 0 : getTotalSpace() - mLastTotalSpace;
}
/**
* Returns the start of the view including its decoration and margin.
* <p>
* For example, for the horizontal helper, if a View's left is at pixel 20, has 2px left
* decoration and 3px left margin, returned value will be 15px.
*
* @param view The view element to check
* @return The first pixel of the element
* @see #getDecoratedEnd(View)
*/
public abstract int getDecoratedStart(View view);
/**
* Returns the end of the view including its decoration and margin.
* <p>
* For example, for the horizontal helper, if a View's right is at pixel 200, has 2px right
* decoration and 3px right margin, returned value will be 205.
*
* @param view The view element to check
* @return The last pixel of the element
* @see #getDecoratedStart(View)
*/
public abstract int getDecoratedEnd(View view);
/**
* Returns the space occupied by this View in the current orientation including decorations and
* margins.
*
* @param view The view element to check
* @return Total space occupied by this view
* @see #getDecoratedMeasurementInOther(View)
*/
public abstract int getDecoratedMeasurement(View view);
/**
* Returns the space occupied by this View in the perpendicular orientation including
* decorations and margins.
*
* @param view The view element to check
* @return Total space occupied by this view in the perpendicular orientation to current one
* @see #getDecoratedMeasurement(View)
*/
public abstract int getDecoratedMeasurementInOther(View view);
/**
* Returns the start position of the layout after the start padding is added.
*
* @return The very first pixel we can draw.
*/
public abstract int getStartAfterPadding();
/**
* Returns the end position of the layout after the end padding is removed.
*
* @return The end boundary for this layout.
*/
public abstract int getEndAfterPadding();
/**
* Returns the end position of the layout without taking padding into account.
*
* @return The end boundary for this layout without considering padding.
*/
public abstract int getEnd();
/**
* Offsets all children's positions by the given amount.
*
* @param amount Value to add to each child's layout parameters
*/
public abstract void offsetChildren(int amount);
/**
* Returns the total space to layout. This number is the difference between
* {@link #getEndAfterPadding()} and {@link #getStartAfterPadding()}.
*
* @return Total space to layout children
*/
public abstract int getTotalSpace();
/**
* Offsets the child in this orientation.
*
* @param view View to offset
* @param offset offset amount
*/
public abstract void offsetChild(View view, int offset);
/**
* Returns the padding at the end of the layout. For horizontal helper, this is the right
* padding and for vertical helper, this is the bottom padding. This method does not check
* whether the layout is RTL or not.
*
* @return The padding at the end of the layout.
*/
public abstract int getEndPadding();
/**
* Creates an OrientationHelper for the given LayoutManager and orientation.
*
* @param layoutManager LayoutManager to attach to
* @param orientation Desired orientation. Should be {@link #HORIZONTAL} or {@link #VERTICAL}
* @return A new OrientationHelper
*/
public static OrientationHelper createOrientationHelper(
RecyclerView.LayoutManager layoutManager, int orientation) {
switch (orientation) {
case HORIZONTAL:
return createHorizontalHelper(layoutManager);
case VERTICAL:
return createVerticalHelper(layoutManager);
}
throw new IllegalArgumentException("invalid orientation");
}
/**
* Creates a horizontal OrientationHelper for the given LayoutManager.
*
* @param layoutManager The LayoutManager to attach to.
* @return A new OrientationHelper
*/
public static OrientationHelper createHorizontalHelper(
RecyclerView.LayoutManager layoutManager) {
return new OrientationHelper(layoutManager) {
@Override
public int getEndAfterPadding() {
return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight();
}
@Override
public int getEnd() {
return mLayoutManager.getWidth();
}
@Override
public void offsetChildren(int amount) {
mLayoutManager.offsetChildrenHorizontal(amount);
}
@Override
public int getStartAfterPadding() {
return mLayoutManager.getPaddingLeft();
}
@Override
public int getDecoratedMeasurement(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
+ params.rightMargin;
}
@Override
public int getDecoratedMeasurementInOther(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
+ params.bottomMargin;
}
@Override
public int getDecoratedEnd(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedRight(view) + params.rightMargin;
}
@Override
public int getDecoratedStart(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedLeft(view) - params.leftMargin;
}
@Override
public int getTotalSpace() {
return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft()
- mLayoutManager.getPaddingRight();
}
@Override
public void offsetChild(View view, int offset) {
view.offsetLeftAndRight(offset);
}
@Override
public int getEndPadding() {
return mLayoutManager.getPaddingRight();
}
};
}
/**
* Creates a vertical OrientationHelper for the given LayoutManager.
*
* @param layoutManager The LayoutManager to attach to.
* @return A new OrientationHelper
*/
public static OrientationHelper createVerticalHelper(RecyclerView.LayoutManager layoutManager) {
return new OrientationHelper(layoutManager) {
@Override
public int getEndAfterPadding() {
return mLayoutManager.getHeight() - mLayoutManager.getPaddingBottom();
}
@Override
public int getEnd() {
return mLayoutManager.getHeight();
}
@Override
public void offsetChildren(int amount) {
mLayoutManager.offsetChildrenVertical(amount);
}
@Override
public int getStartAfterPadding() {
return mLayoutManager.getPaddingTop();
}
@Override
public int getDecoratedMeasurement(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
+ params.bottomMargin;
}
@Override
public int getDecoratedMeasurementInOther(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
+ params.rightMargin;
}
@Override
public int getDecoratedEnd(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedBottom(view) + params.bottomMargin;
}
@Override
public int getDecoratedStart(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedTop(view) - params.topMargin;
}
@Override
public int getTotalSpace() {
return mLayoutManager.getHeight() - mLayoutManager.getPaddingTop()
- mLayoutManager.getPaddingBottom();
}
@Override
public void offsetChild(View view, int offset) {
view.offsetTopAndBottom(offset);
}
@Override
public int getEndPadding() {
return mLayoutManager.getPaddingBottom();
}
};
}
}

View File

@ -1,460 +0,0 @@
/*
* Copyright (C) 2013 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 android.support.v7.widget;
import java.util.ArrayList;
/**
* Like a SparseArray, but with the ability to offset key ranges for bulk insertions/deletions.
*/
class PositionMap<E> implements Cloneable {
private static final Object DELETED = new Object();
private boolean mGarbage = false;
private int[] mKeys;
private Object[] mValues;
private int mSize;
/**
* Creates a new SparseArray containing no mappings.
*/
public PositionMap() {
this(10);
}
/**
* Creates a new PositionMap containing no mappings that will not
* require any additional memory allocation to store the specified
* number of mappings. If you supply an initial capacity of 0, the
* sparse array will be initialized with a light-weight representation
* not requiring any additional array allocations.
*/
public PositionMap(int initialCapacity) {
if (initialCapacity == 0) {
mKeys = ContainerHelpers.EMPTY_INTS;
mValues = ContainerHelpers.EMPTY_OBJECTS;
} else {
initialCapacity = idealIntArraySize(initialCapacity);
mKeys = new int[initialCapacity];
mValues = new Object[initialCapacity];
}
mSize = 0;
}
@Override
@SuppressWarnings("unchecked")
public PositionMap<E> clone() {
PositionMap<E> clone = null;
try {
clone = (PositionMap<E>) super.clone();
clone.mKeys = mKeys.clone();
clone.mValues = mValues.clone();
} catch (CloneNotSupportedException cnse) {
/* ignore */
}
return clone;
}
/**
* Gets the Object mapped from the specified key, or <code>null</code>
* if no such mapping has been made.
*/
public E get(int key) {
return get(key, null);
}
/**
* Gets the Object mapped from the specified key, or the specified Object
* if no such mapping has been made.
*/
@SuppressWarnings("unchecked")
public E get(int key, E valueIfKeyNotFound) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i < 0 || mValues[i] == DELETED) {
return valueIfKeyNotFound;
} else {
return (E) mValues[i];
}
}
/**
* Removes the mapping from the specified key, if there was any.
*/
public void delete(int key) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
if (mValues[i] != DELETED) {
mValues[i] = DELETED;
mGarbage = true;
}
}
}
/**
* Alias for {@link #delete(int)}.
*/
public void remove(int key) {
delete(key);
}
/**
* Removes the mapping at the specified index.
*/
public void removeAt(int index) {
if (mValues[index] != DELETED) {
mValues[index] = DELETED;
mGarbage = true;
}
}
/**
* Remove a range of mappings as a batch.
*
* @param index Index to begin at
* @param size Number of mappings to remove
*/
public void removeAtRange(int index, int size) {
final int end = Math.min(mSize, index + size);
for (int i = index; i < end; i++) {
removeAt(i);
}
}
public void insertKeyRange(int keyStart, int count) {
}
public void removeKeyRange(ArrayList<E> removedItems, int keyStart, int count) {
}
private void gc() {
// Log.e("SparseArray", "gc start with " + mSize);
int n = mSize;
int o = 0;
int[] keys = mKeys;
Object[] values = mValues;
for (int i = 0; i < n; i++) {
Object val = values[i];
if (val != DELETED) {
if (i != o) {
keys[o] = keys[i];
values[o] = val;
values[i] = null;
}
o++;
}
}
mGarbage = false;
mSize = o;
// Log.e("SparseArray", "gc end with " + mSize);
}
/**
* Adds a mapping from the specified key to the specified value,
* replacing the previous mapping from the specified key if there
* was one.
*/
public void put(int key, E value) {
int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
if (i >= 0) {
mValues[i] = value;
} else {
i = ~i;
if (i < mSize && mValues[i] == DELETED) {
mKeys[i] = key;
mValues[i] = value;
return;
}
if (mGarbage && mSize >= mKeys.length) {
gc();
// Search again because indices may have changed.
i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
}
if (mSize >= mKeys.length) {
int n = idealIntArraySize(mSize + 1);
int[] nkeys = new int[n];
Object[] nvalues = new Object[n];
// Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
mKeys = nkeys;
mValues = nvalues;
}
if (mSize - i != 0) {
// Log.e("SparseArray", "move " + (mSize - i));
System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
}
mKeys[i] = key;
mValues[i] = value;
mSize++;
}
}
/**
* Returns the number of key-value mappings that this SparseArray
* currently stores.
*/
public int size() {
if (mGarbage) {
gc();
}
return mSize;
}
/**
* Given an index in the range <code>0...size()-1</code>, returns
* the key from the <code>index</code>th key-value mapping that this
* SparseArray stores.
*/
public int keyAt(int index) {
if (mGarbage) {
gc();
}
return mKeys[index];
}
/**
* Given an index in the range <code>0...size()-1</code>, returns
* the value from the <code>index</code>th key-value mapping that this
* SparseArray stores.
*/
@SuppressWarnings("unchecked")
public E valueAt(int index) {
if (mGarbage) {
gc();
}
return (E) mValues[index];
}
/**
* Given an index in the range <code>0...size()-1</code>, sets a new
* value for the <code>index</code>th key-value mapping that this
* SparseArray stores.
*/
public void setValueAt(int index, E value) {
if (mGarbage) {
gc();
}
mValues[index] = value;
}
/**
* Returns the index for which {@link #keyAt} would return the
* specified key, or a negative number if the specified
* key is not mapped.
*/
public int indexOfKey(int key) {
if (mGarbage) {
gc();
}
return ContainerHelpers.binarySearch(mKeys, mSize, key);
}
/**
* Returns an index for which {@link #valueAt} would return the
* specified key, or a negative number if no keys map to the
* specified value.
* <p>Beware that this is a linear search, unlike lookups by key,
* and that multiple keys can map to the same value and this will
* find only one of them.
* <p>Note also that unlike most collections' {@code indexOf} methods,
* this method compares values using {@code ==} rather than {@code equals}.
*/
public int indexOfValue(E value) {
if (mGarbage) {
gc();
}
for (int i = 0; i < mSize; i++)
if (mValues[i] == value)
return i;
return -1;
}
/**
* Removes all key-value mappings from this SparseArray.
*/
public void clear() {
int n = mSize;
Object[] values = mValues;
for (int i = 0; i < n; i++) {
values[i] = null;
}
mSize = 0;
mGarbage = false;
}
/**
* Puts a key/value pair into the array, optimizing for the case where
* the key is greater than all existing keys in the array.
*/
public void append(int key, E value) {
if (mSize != 0 && key <= mKeys[mSize - 1]) {
put(key, value);
return;
}
if (mGarbage && mSize >= mKeys.length) {
gc();
}
int pos = mSize;
if (pos >= mKeys.length) {
int n = idealIntArraySize(pos + 1);
int[] nkeys = new int[n];
Object[] nvalues = new Object[n];
// Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
mKeys = nkeys;
mValues = nvalues;
}
mKeys[pos] = key;
mValues[pos] = value;
mSize = pos + 1;
}
/**
* {@inheritDoc}
*
* <p>This implementation composes a string by iterating over its mappings. If
* this map contains itself as a value, the string "(this Map)"
* will appear in its place.
*/
@Override
public String toString() {
if (size() <= 0) {
return "{}";
}
StringBuilder buffer = new StringBuilder(mSize * 28);
buffer.append('{');
for (int i=0; i<mSize; i++) {
if (i > 0) {
buffer.append(", ");
}
int key = keyAt(i);
buffer.append(key);
buffer.append('=');
Object value = valueAt(i);
if (value != this) {
buffer.append(value);
} else {
buffer.append("(this Map)");
}
}
buffer.append('}');
return buffer.toString();
}
static int idealByteArraySize(int need) {
for (int i = 4; i < 32; i++)
if (need <= (1 << i) - 12)
return (1 << i) - 12;
return need;
}
static int idealBooleanArraySize(int need) {
return idealByteArraySize(need);
}
static int idealShortArraySize(int need) {
return idealByteArraySize(need * 2) / 2;
}
static int idealCharArraySize(int need) {
return idealByteArraySize(need * 2) / 2;
}
static int idealIntArraySize(int need) {
return idealByteArraySize(need * 4) / 4;
}
static int idealFloatArraySize(int need) {
return idealByteArraySize(need * 4) / 4;
}
static int idealObjectArraySize(int need) {
return idealByteArraySize(need * 4) / 4;
}
static int idealLongArraySize(int need) {
return idealByteArraySize(need * 8) / 8;
}
static class ContainerHelpers {
static final boolean[] EMPTY_BOOLEANS = new boolean[0];
static final int[] EMPTY_INTS = new int[0];
static final long[] EMPTY_LONGS = new long[0];
static final Object[] EMPTY_OBJECTS = new Object[0];
// This is Arrays.binarySearch(), but doesn't do any argument validation.
static int binarySearch(int[] array, int size, int value) {
int lo = 0;
int hi = size - 1;
while (lo <= hi) {
final int mid = (lo + hi) >>> 1;
final int midVal = array[mid];
if (midVal < value) {
lo = mid + 1;
} else if (midVal > value) {
hi = mid - 1;
} else {
return mid; // value found
}
}
return ~lo; // value not present
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,97 +0,0 @@
/*
* Copyright (C) 2014 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 android.support.v7.widget;
import android.os.Bundle;
import android.support.v4.view.AccessibilityDelegateCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
/**
* The AccessibilityDelegate used by RecyclerView.
* <p>
* This class handles basic accessibility actions and delegates them to LayoutManager.
*/
public class RecyclerViewAccessibilityDelegate extends AccessibilityDelegateCompat {
final RecyclerView mRecyclerView;
public RecyclerViewAccessibilityDelegate(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
}
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (super.performAccessibilityAction(host, action, args)) {
return true;
}
if (mRecyclerView.getLayoutManager() != null) {
return mRecyclerView.getLayoutManager().performAccessibilityAction(action, args);
}
return false;
}
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
info.setClassName(RecyclerView.class.getName());
if (mRecyclerView.getLayoutManager() != null) {
mRecyclerView.getLayoutManager().onInitializeAccessibilityNodeInfo(info);
}
}
@Override
public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(host, event);
event.setClassName(RecyclerView.class.getName());
if (host instanceof RecyclerView) {
RecyclerView rv = (RecyclerView) host;
if (rv.getLayoutManager() != null) {
rv.getLayoutManager().onInitializeAccessibilityEvent(event);
}
}
}
AccessibilityDelegateCompat getItemDelegate() {
return mItemDelegate;
}
final AccessibilityDelegateCompat mItemDelegate = new AccessibilityDelegateCompat() {
@Override
public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
if (mRecyclerView.getLayoutManager() != null) {
mRecyclerView.getLayoutManager().
onInitializeAccessibilityNodeInfoForItem(host, info);
}
}
@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (super.performAccessibilityAction(host, action, args)) {
return true;
}
if (mRecyclerView.getLayoutManager() != null) {
return mRecyclerView.getLayoutManager().
performAccessibilityActionForItem(host, action, args);
}
return false;
}
};
}

View File

@ -1,94 +0,0 @@
/*
* Copyright (C) 2014 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 android.support.v7.widget;
import android.view.View;
/**
* A helper class to do scroll offset calculations.
*/
class ScrollbarHelper {
/**
* @param startChild View closest to start of the list. (top or left)
* @param endChild View closest to end of the list (bottom or right)
*/
static int computeScrollOffset(RecyclerView.State state, OrientationHelper orientation,
View startChild, View endChild, RecyclerView.LayoutManager lm,
boolean smoothScrollbarEnabled, boolean reverseLayout) {
if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
endChild == null) {
return 0;
}
final int minPosition = Math.min(lm.getPosition(startChild), lm.getPosition(endChild));
final int maxPosition = Math.max(lm.getPosition(startChild), lm.getPosition(endChild));
final int itemsBefore = reverseLayout
? Math.max(0, state.getItemCount() - maxPosition - 1)
: Math.max(0, minPosition - 1);
if (!smoothScrollbarEnabled) {
return itemsBefore;
}
final int laidOutArea = Math.abs(orientation.getDecoratedEnd(endChild) -
orientation.getDecoratedStart(startChild));
final int itemRange = Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild)) + 1;
final float avgSizePerRow = (float) laidOutArea / itemRange;
return Math.round(itemsBefore * avgSizePerRow + (orientation.getStartAfterPadding()
- orientation.getDecoratedStart(startChild)));
}
/**
* @param startChild View closest to start of the list. (top or left)
* @param endChild View closest to end of the list (bottom or right)
*/
static int computeScrollExtent(RecyclerView.State state, OrientationHelper orientation,
View startChild, View endChild, RecyclerView.LayoutManager lm,
boolean smoothScrollbarEnabled) {
if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
endChild == null) {
return 0;
}
if (!smoothScrollbarEnabled) {
return Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild)) + 1;
}
final int extend = orientation.getDecoratedEnd(endChild)
- orientation.getDecoratedStart(startChild);
return Math.min(orientation.getTotalSpace(), extend);
}
/**
* @param startChild View closest to start of the list. (top or left)
* @param endChild View closest to end of the list (bottom or right)
*/
static int computeScrollRange(RecyclerView.State state, OrientationHelper orientation,
View startChild, View endChild, RecyclerView.LayoutManager lm,
boolean smoothScrollbarEnabled) {
if (lm.getChildCount() == 0 || state.getItemCount() == 0 || startChild == null ||
endChild == null) {
return 0;
}
if (!smoothScrollbarEnabled) {
return state.getItemCount();
}
// smooth scrollbar enabled. try to estimate better.
final int laidOutArea = orientation.getDecoratedEnd(endChild) -
orientation.getDecoratedStart(startChild);
final int laidOutRange = Math.abs(lm.getPosition(startChild) - lm.getPosition(endChild))
+ 1;
// estimate a size for full list.
return (int) ((float) laidOutArea / laidOutRange * state.getItemCount());
}
}

View File

@ -6,50 +6,48 @@ import android.graphics.Color;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
public class CustomView extends RelativeLayout{
final static String MATERIALDESIGNXML = "http://schemas.android.com/apk/res-auto";
final static String ANDROIDXML = "http://schemas.android.com/apk/res/android";
final int disabledBackgroundColor = Color.parseColor("#E2E2E2");
int beforeBackground;
// Indicate if user touched this view the last time
public boolean isLastTouch = false;
public class CustomView extends RelativeLayout {
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if(enabled)
setBackgroundColor(beforeBackground);
else
setBackgroundColor(disabledBackgroundColor);
invalidate();
}
boolean animation = false;
@Override
protected void onAnimationStart() {
super.onAnimationStart();
animation = true;
}
@Override
protected void onAnimationEnd() {
super.onAnimationEnd();
animation = false;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(animation)
invalidate();
}
final static String MATERIALDESIGNXML = "http://schemas.android.com/apk/res-auto";
final static String ANDROIDXML = "http://schemas.android.com/apk/res/android";
final int disabledBackgroundColor = Color.parseColor("#E2E2E2");
// Indicate if user touched this view the last time
public boolean isLastTouch = false;
int beforeBackground;
boolean animation = false;
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
if (enabled)
setBackgroundColor(beforeBackground);
else
setBackgroundColor(disabledBackgroundColor);
invalidate();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (animation)
invalidate();
}
@Override
protected void onAnimationStart() {
super.onAnimationStart();
animation = true;
}
@Override
protected void onAnimationEnd() {
super.onAnimationEnd();
animation = false;
}
}

View File

@ -8,176 +8,167 @@ import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.support.v4.content.ContextCompat;
import android.util.AttributeSet;
public class ProgressBarCircularIndeterminate extends CustomView {
final static String ANDROIDXML = "http://schemas.android.com/apk/res/android";
final static String ANDROIDXML = "http://schemas.android.com/apk/res/android";
int backgroundColor = Color.parseColor("#1E88E5");
int backgroundColor = Color.parseColor("#1E88E5");
float radius1 = 0;
float radius2 = 0;
int cont = 0;
boolean firstAnimationOver = false;
int arcD = 1;
int arcO = 0;
float rotateAngle = 0;
int limite = 0;
public ProgressBarCircularIndeterminate(Context context, AttributeSet attrs) {
super(context, attrs);
setAttributes(attrs);
public ProgressBarCircularIndeterminate(Context context, AttributeSet attrs) {
super(context, attrs);
setAttributes(attrs);
}
}
// Set atributtes of XML to View
protected void setAttributes(AttributeSet attrs) {
// Set atributtes of XML to View
protected void setAttributes(AttributeSet attrs) {
setMinimumHeight(Utils.dpToPx(32, getResources()));
setMinimumWidth(Utils.dpToPx(32, getResources()));
setMinimumHeight(Utils.dpToPx(32, getResources()));
setMinimumWidth(Utils.dpToPx(32, getResources()));
// Set background Color
// Color by resource
int bacgroundColor = attrs.getAttributeResourceValue(ANDROIDXML, "background", -1);
if (bacgroundColor != -1) {
setBackgroundColor(ContextCompat.getColor(getContext(), bacgroundColor));
} else {
// Color by hexadecimal
int background = attrs.getAttributeIntValue(ANDROIDXML, "background", -1);
if (background != -1) {
setBackgroundColor(background);
} else {
setBackgroundColor(Color.parseColor("#1E88E5"));
}
}
// Set background Color
// Color by resource
int bacgroundColor = attrs.getAttributeResourceValue(ANDROIDXML,
"background", -1);
if (bacgroundColor != -1) {
setBackgroundColor(getResources().getColor(bacgroundColor));
} else {
// Color by hexadecimal
int background = attrs.getAttributeIntValue(ANDROIDXML,
"background", -1);
if (background != -1)
setBackgroundColor(background);
else
setBackgroundColor(Color.parseColor("#1E88E5"));
}
setMinimumHeight(Utils.dpToPx(3, getResources()));
setMinimumHeight(Utils.dpToPx(3, getResources()));
}
}
// Set color of background
public void setBackgroundColor(int color) {
super.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
if (isEnabled()) {
beforeBackground = backgroundColor;
}
this.backgroundColor = color;
}
/**
* Make a dark color to ripple effect
*
* @return
*/
protected int makePressColor() {
int r = (this.backgroundColor >> 16) & 0xFF;
int g = (this.backgroundColor >> 8) & 0xFF;
int b = (this.backgroundColor >> 0) & 0xFF;
// r = (r+90 > 245) ? 245 : r+90;
// g = (g+90 > 245) ? 245 : g+90;
// b = (b+90 > 245) ? 245 : b+90;
return Color.argb(128, r, g, b);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!firstAnimationOver) {
drawFirstAnimation(canvas);
}
if (cont > 0) {
drawSecondAnimation(canvas);
}
invalidate();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (firstAnimationOver == false)
drawFirstAnimation(canvas);
if (cont > 0)
drawSecondAnimation(canvas);
invalidate();
}
}
/**
* Draw first animation of view
*
* @param canvas
*/
private void drawFirstAnimation(Canvas canvas) {
if (radius1 < getWidth() / 2) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(makePressColor());
radius1 = (radius1 >= getWidth() / 2) ? (float) getWidth() / 2 : radius1 + 1;
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius1, paint);
} else {
Bitmap bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
Canvas temp = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(makePressColor());
temp.drawCircle(getWidth() / 2, getHeight() / 2, getHeight() / 2, paint);
Paint transparentPaint = new Paint();
transparentPaint.setAntiAlias(true);
transparentPaint.setColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
transparentPaint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
if (cont >= 50) {
radius2 = (radius2 >= getWidth() / 2) ? (float) getWidth() / 2 : radius2 + 1;
} else {
radius2 = (radius2 >= getWidth() / 2 - Utils.dpToPx(4, getResources())) ?
(float) getWidth() / 2 - Utils.dpToPx(4, getResources()) : radius2 + 1;
}
temp.drawCircle(getWidth() / 2, getHeight() / 2, radius2, transparentPaint);
canvas.drawBitmap(bitmap, 0, 0, new Paint());
if (radius2 >= getWidth() / 2 - Utils.dpToPx(4, getResources())) {
cont++;
}
if (radius2 >= getWidth() / 2) {
firstAnimationOver = true;
}
}
}
float radius1 = 0;
float radius2 = 0;
int cont = 0;
boolean firstAnimationOver = false;
/**
* Draw second animation of view
*
* @param canvas
*/
private void drawSecondAnimation(Canvas canvas) {
if (arcO == limite) {
arcD += 6;
}
if (arcD >= 290 || arcO > limite) {
arcO += 6;
arcD -= 6;
}
if (arcO > limite + 290) {
limite = arcO;
arcO = limite;
arcD = 1;
}
rotateAngle += 4;
canvas.rotate(rotateAngle, getWidth() / 2, getHeight() / 2);
/**
* Draw first animation of view
*
* @param canvas
*/
private void drawFirstAnimation(Canvas canvas) {
if (radius1 < getWidth() / 2) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(makePressColor());
radius1 = (radius1 >= getWidth() / 2) ? (float) getWidth() / 2
: radius1 + 1;
canvas.drawCircle(getWidth() / 2, getHeight() / 2, radius1, paint);
} else {
Bitmap bitmap = Bitmap.createBitmap(canvas.getWidth(),
canvas.getHeight(), Bitmap.Config.ARGB_8888);
Canvas temp = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(makePressColor());
temp.drawCircle(getWidth() / 2, getHeight() / 2, getHeight() / 2,
paint);
Paint transparentPaint = new Paint();
transparentPaint.setAntiAlias(true);
transparentPaint.setColor(getResources().getColor(
android.R.color.transparent));
transparentPaint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
if (cont >= 50) {
radius2 = (radius2 >= getWidth() / 2) ? (float) getWidth() / 2
: radius2 + 1;
} else {
radius2 = (radius2 >= getWidth() / 2
- Utils.dpToPx(4, getResources())) ? (float) getWidth()
/ 2 - Utils.dpToPx(4, getResources()) : radius2 + 1;
}
temp.drawCircle(getWidth() / 2, getHeight() / 2, radius2,
transparentPaint);
canvas.drawBitmap(bitmap, 0, 0, new Paint());
if (radius2 >= getWidth() / 2 - Utils.dpToPx(4, getResources()))
cont++;
if (radius2 >= getWidth() / 2)
firstAnimationOver = true;
}
}
Bitmap bitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(), Bitmap.Config.ARGB_8888);
Canvas temp = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(backgroundColor);
// temp.drawARGB(0, 0, 0, 255);
temp.drawArc(new RectF(0, 0, getWidth(), getHeight()), arcO, arcD, true, paint);
Paint transparentPaint = new Paint();
transparentPaint.setAntiAlias(true);
transparentPaint.setColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
transparentPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
temp.drawCircle(getWidth() / 2, getHeight() / 2, (getWidth() / 2)
- Utils.dpToPx(4, getResources()), transparentPaint);
int arcD = 1;
int arcO = 0;
float rotateAngle = 0;
int limite = 0;
canvas.drawBitmap(bitmap, 0, 0, new Paint());
}
/**
* Draw second animation of view
*
* @param canvas
*/
private void drawSecondAnimation(Canvas canvas) {
if (arcO == limite)
arcD += 6;
if (arcD >= 290 || arcO > limite) {
arcO += 6;
arcD -= 6;
}
if (arcO > limite + 290) {
limite = arcO;
arcO = limite;
arcD = 1;
}
rotateAngle += 4;
canvas.rotate(rotateAngle, getWidth() / 2, getHeight() / 2);
Bitmap bitmap = Bitmap.createBitmap(canvas.getWidth(),
canvas.getHeight(), Bitmap.Config.ARGB_8888);
Canvas temp = new Canvas(bitmap);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(backgroundColor);
// temp.drawARGB(0, 0, 0, 255);
temp.drawArc(new RectF(0, 0, getWidth(), getHeight()), arcO, arcD,
true, paint);
Paint transparentPaint = new Paint();
transparentPaint.setAntiAlias(true);
transparentPaint.setColor(getResources().getColor(
android.R.color.transparent));
transparentPaint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
temp.drawCircle(getWidth() / 2, getHeight() / 2, (getWidth() / 2)
- Utils.dpToPx(4, getResources()), transparentPaint);
canvas.drawBitmap(bitmap, 0, 0, new Paint());
}
// Set color of background
public void setBackgroundColor(int color) {
super.setBackgroundColor(getResources().getColor(
android.R.color.transparent));
if (isEnabled())
beforeBackground = backgroundColor;
this.backgroundColor = color;
}
/**
* Make a dark color to ripple effect
*
* @return
*/
protected int makePressColor() {
int r = (this.backgroundColor >> 16) & 0xFF;
int g = (this.backgroundColor >> 8) & 0xFF;
int b = (this.backgroundColor >> 0) & 0xFF;
// r = (r+90 > 245) ? 245 : r+90;
// g = (g+90 > 245) ? 245 : g+90;
// b = (b+90 > 245) ? 245 : b+90;
return Color.argb(128, r, g, b);
}
}

View File

@ -5,30 +5,30 @@ import android.util.TypedValue;
import android.view.View;
public class Utils {
/**
* Convert Dp to Pixel
*/
public static int dpToPx(float dp, Resources resources){
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.getDisplayMetrics());
return (int) px;
}
public static int getRelativeTop(View myView) {
/**
* Convert Dp to Pixel
*/
public static int dpToPx(float dp, Resources resources) {
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.getDisplayMetrics());
return (int) px;
}
public static int getRelativeTop(View myView) {
// if (myView.getParent() == myView.getRootView())
if(myView.getId() == android.R.id.content)
return myView.getTop();
else
return myView.getTop() + getRelativeTop((View) myView.getParent());
}
public static int getRelativeLeft(View myView) {
if (myView.getId() == android.R.id.content)
return myView.getTop();
else
return myView.getTop() + getRelativeTop((View) myView.getParent());
}
public static int getRelativeLeft(View myView) {
// if (myView.getParent() == myView.getRootView())
if(myView.getId() == android.R.id.content)
return myView.getLeft();
else
return myView.getLeft() + getRelativeLeft((View) myView.getParent());
}
if (myView.getId() == android.R.id.content)
return myView.getLeft();
else
return myView.getLeft() + getRelativeLeft((View) myView.getParent());
}
}

View File

@ -1,238 +0,0 @@
package com.gh.base;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.Application;
import android.content.Context;
import android.os.Process;
import android.support.v4.util.ArrayMap;
import android.util.Log;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.gh.common.util.DataUtils;
import com.gh.common.util.HttpsUtils;
import com.gh.common.util.TokenUtils;
import com.gh.common.util.Utils;
import com.umeng.message.IUmengRegisterCallback;
import com.umeng.message.PushAgent;
import com.umeng.message.UTrack;
import com.xiaomi.channel.commonutils.logger.LoggerInterface;
import com.xiaomi.mipush.sdk.Logger;
import com.xiaomi.mipush.sdk.MiPushClient;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class AppController extends Application {
//public class AppController extends TinkerApplication {
public static final String TAG = AppController.class.getSimpleName();
// xiaomi push appid
public static final String APP_ID = "2882303761517352993";
// xiaomi push appkey
public static final String APP_KEY = "5451735292993";
private static AppController mInstance;
private static ArrayMap<String, Object> objectMap = new ArrayMap<>();
private ArrayList<Activity> list = new ArrayList<>();
private boolean isFinish = false;
//快传文件发送单线程
public static Executor FILE_SENDER_EXECUTOR = Executors.newSingleThreadExecutor();
//快传文件发送主要的线程池
public static Executor MAIN_EXECUTOR = Executors.newFixedThreadPool(5);
// public AppController() {
// super(ShareConstants.TINKER_ENABLE_ALL, "com.gh.base.AppControllerLike",
// "com.tencent.tinker.loader.TinkerLoader", false);
// }
@Override
public void onCreate() {
super.onCreate();
//初始化Fresco
Fresco.initialize(this);
DataUtils.init(this);
HttpsUtils.initHttpsUrlConnection(this);
AppUncaHandler uncaHandler = new AppUncaHandler(this);
Thread.setDefaultUncaughtExceptionHandler(uncaHandler);
mInstance = this;
// 注册push服务注册成功后会向GHPushMessageReceiver发送广播
// 可以从GHPushMessageReceiver的onCommandResult方法中MiPushCommandMessage对象参数中获取注册信息
if (shouldInit()) {
MiPushClient.registerPush(this, APP_ID, APP_KEY);
}
LoggerInterface newLogger = new LoggerInterface() {
@Override
public void setTag(String tag) {
// ignore
}
@Override
public void log(String content, Throwable t) {
Log.d(TAG, content, t);
}
@Override
public void log(String content) {
Log.d(TAG, content);
}
};
Logger.setLogger(this, newLogger);
//友盟推送
final PushAgent mPushAgent = PushAgent.getInstance(this);
//注册推送服务每次调用register方法都会回调该接口
mPushAgent.register(new IUmengRegisterCallback() {
@Override
public void onSuccess(String deviceToken) {
//注册成功会返回device token
Utils.log("deviceToken::" + deviceToken);
//设置别名
mPushAgent.addExclusiveAlias(TokenUtils.getDeviceId(getApplicationContext())
, "GHDID", new UTrack.ICallBack() {
@Override
public void onMessage(boolean b, String s) {
Utils.log("ExclusiveAlias::" + b + "==" + s);
}
});
}
@Override
public void onFailure(String s, String s1) {
Utils.log("deviceToken::" + "注册失败");
}
});
// 友盟推送数据处理
mPushAgent.setNotificationClickHandler(new GHUmengNotificationClickHandler());
// // 监听屏幕状态广播
// if (shouldInit()) {
// UnlockScreenReceiver unlockScreenReceiver = new UnlockScreenReceiver();
// IntentFilter intentFilter = new IntentFilter();
// intentFilter.addAction(Intent.ACTION_SCREEN_ON);
// intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
// registerReceiver(unlockScreenReceiver, intentFilter);
//
// // 用户App运行数据统计服务
// Intent intent = new Intent(getApplicationContext(), AppStaticService.class);
// startService(intent);
//
// AppRunTimeDao dao = new AppRunTimeDao(getApplicationContext());
// for (AppRunTimeInfo appRunTimeInfo : dao.getAll()) {
// Utils.log(appRunTimeInfo.getPackageName() + "====1111=====" + appRunTimeInfo.getRunTime());
// }
// }
}
public static void put(String key, Object object) {
if (objectMap == null) {
objectMap = new ArrayMap<>();
}
objectMap.put(key, object);
}
public static Object get(String key, boolean isRemove) {
if (objectMap == null) {
return null;
}
if (isRemove) {
return objectMap.remove(key);
} else {
return objectMap.get(key);
}
}
public static void remove(String key) {
if (objectMap == null) {
return;
}
objectMap.remove(key);
}
/**
* Activity关闭时删除Activity列表中的Activity对象
*/
public void removeActivity(Activity a) {
if (!isFinish) {
list.remove(a);
Utils.log("remove = " + a.getClass().getSimpleName());
}
}
/**
* 向Activity列表中添加Activity对象
*/
public void addActivity(Activity a) {
Utils.log("add = " + a.getClass().getSimpleName());
list.add(a);
}
/**
* 关闭Activity列表中的所有Activity
*/
public void finishActivity() {
isFinish = true;
for (int i = list.size() - 1; i >= 0; i--) {
Activity activity = list.get(i);
if (null != activity) {
Utils.log("finish = " + activity.getClass().getSimpleName());
activity.finish();
}
}
// 杀死该应用进程
Process.killProcess(Process.myPid());
}
public static String getProcessName(Context cxt, int pid) {
ActivityManager am = (ActivityManager) cxt
.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningAppProcessInfo> runningApps = am.getRunningAppProcesses();
if (runningApps == null) {
return null;
}
for (RunningAppProcessInfo procInfo : runningApps) {
if (procInfo.pid == pid) {
return procInfo.processName;
}
}
return null;
}
public static synchronized AppController getInstance() {
return mInstance;
}
private boolean shouldInit() {
ActivityManager am = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE));
List<RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
String mainProcessName = getPackageName();
Log.d(TAG, mainProcessName);
int myPid = Process.myPid();
for (RunningAppProcessInfo info : processInfos) {
if (info.pid == myPid && mainProcessName.equals(info.processName)) {
return true;
}
}
return false;
}
}

View File

@ -1,51 +0,0 @@
//package com.gh.base;
//
//
//import android.annotation.TargetApi;
//import android.app.Application;
//import android.content.Context;
//import android.content.Intent;
//import android.os.Build;
//import android.support.multidex.MultiDex;
//
//import com.tencent.tinker.lib.listener.DefaultPatchListener;
//import com.tencent.tinker.lib.listener.PatchListener;
//import com.tencent.tinker.lib.patch.AbstractPatch;
//import com.tencent.tinker.lib.patch.UpgradePatch;
//import com.tencent.tinker.lib.reporter.DefaultLoadReporter;
//import com.tencent.tinker.lib.reporter.DefaultPatchReporter;
//import com.tencent.tinker.lib.reporter.LoadReporter;
//import com.tencent.tinker.lib.reporter.PatchReporter;
//import com.tencent.tinker.lib.tinker.TinkerInstaller;
//import com.tencent.tinker.loader.app.DefaultApplicationLike;
//
//public class AppControllerLike extends DefaultApplicationLike {
//
// public AppControllerLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,
// long applicationStartElapsedTime, long applicationStartMillisTime,
// Intent tinkerResultIntent) {
// super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime,
// applicationStartMillisTime, tinkerResultIntent);
// }
//
// @Override
// public void onBaseContextAttached(Context base) {
// super.onBaseContextAttached(base);
// MultiDex.install(base);
//
// LoadReporter loadReporter = new DefaultLoadReporter(getApplication());
// PatchReporter patchReporter = new DefaultPatchReporter(getApplication());
// PatchListener patchListener = new DefaultPatchListener(getApplication());
// AbstractPatch upgradePatchProcessor = new UpgradePatch();
//
// TinkerInstaller.install(this, loadReporter, patchReporter, patchListener,
// AppTinkerResultService.class, upgradePatchProcessor);
//// TinkerInstaller.install(this);
// }
//
// @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
// public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
// getApplication().registerActivityLifecycleCallbacks(callback);
// }
//
//}

View File

@ -1,29 +0,0 @@
//package com.gh.base;
//
//import com.gh.common.util.Utils;
//import com.tencent.tinker.lib.service.DefaultTinkerResultService;
//import com.tencent.tinker.lib.service.PatchResult;
//import com.tencent.tinker.lib.util.TinkerServiceInternals;
//
//import java.io.File;
//
//
//public class AppTinkerResultService extends DefaultTinkerResultService {
//
// @Override
// public void onPatchResult(PatchResult result) {
// if (result == null) {
// return;
// }
// Utils.log(result);
//
// //first, we want to kill the recover process
// TinkerServiceInternals.killTinkerPatchServiceProcess(getApplicationContext());
//
// if (result.isSuccess) {
// Utils.log("Tinkder Success");
// deleteRawPatchFile(new File(result.rawPatchFilePath));
// }
// }
//
//}

View File

@ -1,128 +0,0 @@
package com.gh.base;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import com.gh.common.constant.Config;
import com.gh.common.util.DataCollectionUtils;
import com.gh.common.util.FileUtils;
import com.gh.gamecenter.SplashScreenActivity;
import com.tencent.stat.StatService;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class AppUncaHandler implements UncaughtExceptionHandler {
private UncaughtExceptionHandler mDefaultHandler;
private AppController appController;
public AppUncaHandler(AppController appController) {
// 获取系统默认的UncaughtException处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
this.appController = appController;
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, ex);
} else {
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(appController.getApplicationContext(),
"\"光环助手\"发生错误", Toast.LENGTH_SHORT).show();
Looper.loop();
}
}.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 防止重复奔溃导致助手一直重启20秒内不做处理
SharedPreferences sp = appController.getApplicationContext().getSharedPreferences(
Config.PREFERENCE, Context.MODE_PRIVATE);
long time = sp.getLong("last_restart_time", 0);
if (System.currentTimeMillis() - time > 20 * 1000) {
sp.edit().putLong("last_restart_time", System.currentTimeMillis()).apply();
Intent intent = new Intent(appController.getApplicationContext(),
SplashScreenActivity.class);
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent restartIntent = PendingIntent.getActivity(
appController.getApplicationContext(), 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// 退出程序并重启
AlarmManager mgr = (AlarmManager) appController
.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1000,
restartIntent); // 1秒钟后重启应用
}
appController.finishActivity();
}
}
// 保存log到本地
private void saveLog(Throwable ex) {
String errorMsg = Log.getStackTraceString(ex);
// MTA主动上传错误
StatService.reportError(appController.getApplicationContext(), errorMsg);
// 上传错误数据
DataCollectionUtils.uploadError(appController.getApplicationContext(), errorMsg);
// 保存到本地
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault());
File file = new File(FileUtils.getLogPath(appController.getApplicationContext(),
format.format(new Date()) + "_gh_assist" + ".log"));
FileWriter writer = null;
try {
file.createNewFile();
writer = new FileWriter(file);
writer.write(errorMsg);
writer.flush();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
*
* @param ex
* @return true:如果处理了该异常信息;否则返回false.
*/
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}
saveLog(ex);
return true;
}
}

View File

@ -0,0 +1,141 @@
package com.gh.base;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Looper;
import android.preference.PreferenceManager;
import android.util.Log;
import com.gh.common.constant.Config;
import com.gh.common.util.DataCollectionUtils;
import com.gh.gamecenter.SplashScreenActivity;
import com.lightgame.config.CommonDebug;
import com.lightgame.download.FileUtils;
import com.lightgame.utils.AppManager;
import com.lightgame.utils.Utils;
import com.tencent.stat.StatService;
import com.tendcloud.tenddata.TCAgent;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class AppUncaughtHandler implements UncaughtExceptionHandler {
private Context mContext;
public AppUncaughtHandler(Context context) {
// 获取系统默认的UncaughtException处理器
mContext = context;
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Utils.toast(mContext.getApplicationContext(), "\"光环助手\"发生错误");
Looper.loop();
}
});
saveLocalLog(mContext, ex);
restart(mContext);
}
public static void restart(final Context context) {
// 防止重复奔溃导致助手一直重启20秒内不做处理
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
long curTime = System.currentTimeMillis();
long time = sp.getLong("last_restart_time", 0);
if (curTime - time > 20 * 1000) {
sp.edit().putLong("last_restart_time", curTime).apply();
Intent intent = new Intent(context, SplashScreenActivity.class);
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
PendingIntent restartIntent = PendingIntent.getActivity(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// 退出程序并重启
AlarmManager mgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, curTime + 3000, restartIntent); // 1秒钟后重启应用
}
//error restart
// System.exit(2);
AppManager.getInstance().finishAllActivity();
}
// 保存log到本地
public static void saveLocalLog(Context context, Throwable ex) {
String errorMsg = Log.getStackTraceString(ex);
Config.setExceptionMsg(context, errorMsg);
// 保存到本地
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault());
File file = new File(FileUtils.getLogPath(context.getApplicationContext(),
format.format(new Date()) + "_gh_assist" + ".log"));
FileWriter writer = null;
try {
file.createNewFile();
writer = new FileWriter(file);
writer.write(errorMsg);
writer.flush();
} catch (IOException e1) {
e1.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 下次应用启动再上报
*
* @param context
* @param throwable
*/
public static void reportException(Context context, Throwable throwable) {
CommonDebug.logMethodWithParams(context, "ERRMSG", throwable);
// 上传错误数据
try {
DataCollectionUtils.uploadError(context, Log.getStackTraceString(throwable));
} catch (Exception e) {
}
// MTA主动上传错误
try {
StatService.reportException(context, throwable);
} catch (Exception e) {
}
// //bugly 作为默认处理异常的类库,已经上报了,此处不重复上报
// try {
// CrashReport.postCatchedException(throwable);
// } catch (Exception e) {
// }
//talkingdata
try {
TCAgent.onError(context, throwable);
} catch (Exception e) {
}
}
}

View File

@ -1,213 +1,190 @@
package com.gh.base;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.gh.common.constant.Config;
import com.gh.common.util.DataUtils;
import com.gh.common.util.DialogUtils;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.FileUtils;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.RunningUtils;
import com.gh.common.util.ShareUtils;
import com.gh.common.util.Utils;
import com.gh.download.DownloadManager;
import com.gh.common.util.StringUtils;
import com.gh.gamecenter.LoginActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.eventbus.EBShowDialog;
import com.gh.gamecenter.listener.OnCallBackListener;
import com.gh.gamecenter.manager.SystemBarTintManager;
import com.gh.gamecenter.manager.SystemBarTintManager.SystemBarConfig;
import com.lightgame.download.FileUtils;
import com.lightgame.utils.AppManager;
import com.lightgame.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
import butterknife.ButterKnife;
import de.greenrobot.event.EventBus;
import pub.devrel.easypermissions.EasyPermissions;
public class BaseActivity extends Activity implements OnCallBackListener {
import static com.gh.common.util.EntranceUtils.KEY_DATA;
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
private SystemBarTintManager tintManager;
public abstract class BaseActivity extends BaseAppCompatToolBarActivity implements EasyPermissions.PermissionCallbacks{
protected String entrance;
protected String mEntrance;
private boolean mIsPause;
private boolean isPause;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init(mContentView);
AppManager.getInstance().addActivity(this);
EventBus.getDefault().register(this);
mEntrance = getIntent().getStringExtra(KEY_ENTRANCE);
if (getIntent().getBundleExtra(KEY_DATA) != null) {
mEntrance = getIntent().getBundleExtra(KEY_DATA).getString(KEY_ENTRANCE);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Utils.log(this.getClass().getSimpleName());
AppController.getInstance().addActivity(this);
EventBus.getDefault().register(this);
entrance = getIntent().getStringExtra("entrance");
if (getIntent().getBundleExtra("data") != null) {
entrance = getIntent().getBundleExtra("data").getString("entrance");
}
}
@Override
protected boolean onNavigationIconClicked() {
return false;
}
protected void init(View contentView, String title) {
init(contentView);
TextView actionbar_tv_title = (TextView) findViewById(R.id.actionbar_tv_title);
actionbar_tv_title.setText(title);
}
private void init(View contentView) {
setContentView(contentView);
protected void init(View contentView) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setTheme(R.style.AppTheme);
setTranslucentStatus(true);
tintManager = new SystemBarTintManager(this);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintResource(R.color.theme);
SystemBarConfig config = tintManager.getConfig();
contentView.setPadding(0, config.getPixelInsetTop(false), 0,
config.getPixelInsetBottom());
}
ButterKnife.bind(this);
setContentView(contentView);
View back = findViewById(R.id.actionbar_rl_back);
if (back != null)
back.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
ButterKnife.bind(this);
protected void initTitle(String title) {
TextView actionbar_tv_title = (TextView) findViewById(R.id.actionbar_tv_title);
actionbar_tv_title.setText(title);
// setNavigationTitle(title);
}
int actionbar_height = getSharedPreferences(Config.PREFERENCE,
Context.MODE_PRIVATE).getInt("actionbar_height",
DisplayUtils.dip2px(getApplicationContext(), 48));
public void toast(String msg) {
Utils.toast(this, msg);
}
RelativeLayout reuse_actionbar = (RelativeLayout) findViewById(R.id.reuse_actionbar);
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, actionbar_height);
reuse_actionbar.setLayoutParams(lparams);
public void toast(int msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
findViewById(R.id.actionbar_rl_back).setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
//如果是游戏分享newsTitle默认为空
public void showShare(String url, String gameName, String icon, String newsTitle, ArrayList<String> tag, boolean isToolsBox) {
protected SystemBarTintManager getTintManager() {
return tintManager;
}
//判断是否是官方版
boolean isPlugin = false;
if (tag != null) {
for (String s : tag) {
if (!"官方版".equals(s)) {
isPlugin = true;
}
}
}
@Override
public void finish() {
super.finish();
AppController.getInstance().removeActivity(this);
}
ShareUtils.getInstance(this).showShareWindows(getWindow().getDecorView(), url, gameName, icon, newsTitle, isPlugin, true, isToolsBox);
public void toast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
public void toast(int msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
@TargetApi(19)
protected void setTranslucentStatus(boolean status) {
Window window = getWindow();
WindowManager.LayoutParams winParams = window.getAttributes();
final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
if (status) {
winParams.flags |= bits;
} else {
winParams.flags &= ~bits;
}
window.setAttributes(winParams);
}
//如果是游戏分享newsTitle默认为空
public void showShare(String url, String gameName, String icon, String newsTitle, ArrayList<String> tag) {
//判断是否是官方版
boolean isPlugin = false;
if (tag != null){
for (String s : tag) {
if (!"官方版".equals(s)){
isPlugin = true;
}
}
}
ShareUtils.getInstance(this).showShareWindows(new View(this), url, gameName, icon, newsTitle, isPlugin, true);
if (newsTitle == null) {
DataUtils.onEvent(this, "内容分享", gameName);
} else {
DataUtils.onEvent(this, "内容分享", newsTitle);
}
}
if (newsTitle == null) {
DataUtils.onEvent(this, "内容分享", gameName);
} else {
DataUtils.onEvent(this, "内容分享", newsTitle);
}
}
public void onEventMainThread(final EBShowDialog showDialog) {
if (!isPause && this.getClass().getName().equals(RunningUtils.getTopActivity(this))) {
if ("hijack".equals(showDialog.getType())) {
DialogUtils.showQqSessionDialog(this, null);// 建议用户联系客服
} else if ("plugin".equals(showDialog.getType())) {
DialogUtils.showPluginDialog(this, new DialogUtils.ConfiremListener(){
@Override
public void onConfirem() {
if (FileUtils.isEmptyFile(showDialog.getPath())) {
Toast.makeText(BaseActivity.this, "解析包出错(可能被误删了),请重新下载", Toast.LENGTH_SHORT).show();
} else {
startActivity(PackageUtils.getUninstallIntent(BaseActivity.this, showDialog.getPath()));
}
}
});
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Override
protected void onPause() {
super.onPause();
DataUtils.onPause(this);
isPause = true;
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(final EBShowDialog showDialog) {
//TODO 改为缓存到UI可见时才调用参考beier-assist
if (!mIsPause && this.getClass().getName().equals(RunningUtils.getTopActivity(this))) {
if ("hijack".equals(showDialog.getType())) {
DialogUtils.showQqSessionDialog(this, "2586716223");// 建议用户联系客服
} else if ("plugin".equals(showDialog.getType())) {
DialogUtils.showPluginDialog(this, new DialogUtils.ConfirmListener() {
@Override
public void onConfirm() {
if (FileUtils.isEmptyFile(showDialog.getPath())) {
Utils.toast(BaseActivity.this, getString(R.string.install_failure_hint));
} else {
startActivity(PackageUtils.getUninstallIntent(BaseActivity.this, showDialog.getPath()));
}
}
});
} else if ("loginException".equals(showDialog.getType())) {
try {
JSONObject object = new JSONObject(showDialog.getPath());
JSONObject device = object.getJSONObject("device");
String manufacturer = device.getString("manufacturer");
String model = device.getString("model");
DialogUtils.showAlertDialog(this, "你的账号已在另外一台设备登录"
, StringUtils.buildString("", manufacturer, " - ", model, "")
, "知道了", "重新登录"
, null, new DialogUtils.CancelListener() {
@Override
public void onCancel() {
startActivity(LoginActivity.getIntent(BaseActivity.this, false));
}
});
} catch (JSONException e) {
e.printStackTrace();
}
@Override
protected void onResume() {
super.onResume();
DataUtils.onResume(this);
isPause = false;
DownloadManager.getInstance(this).initGameMap();
}
@Override
public void loadDone() {
}
}
}
}
@Override
public void loadDone(Object obj) {
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
AppManager.getInstance().finishActivity(this);
}
}
@Override
protected void onPause() {
super.onPause();
// DataUtils.onPause(this);
mIsPause = true;
}
@Override
public void loadError() {
@Override
protected void onResume() {
super.onResume();
// DataUtils.onResume(this);
mIsPause = false;
// DownloadManager.getInstance(this).initGameMap();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
@Override
public void loadEmpty() {
@Override
public void onPermissionsDenied(int requestCode, List<String> perms) {
}
}
@Override
public void onPermissionsGranted(int requestCode, List<String> perms) {
}
}

View File

@ -0,0 +1,46 @@
package com.gh.base;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import com.gh.gamecenter.R;
import com.lightgame.BaseAppCompatActivity;
/**
* Created by csheng on 15-10-12.
*/
public abstract class BaseAppCompatToolBarActivity extends BaseAppCompatActivity {
private Toolbar mToolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initToolbar();
}
private void initToolbar() {
mToolbar = (Toolbar) findViewById(R.id.toolbar_navigation);
if (mToolbar != null) {
setSupportActionBar(mToolbar);
mToolbar.addView(View.inflate(this, R.layout.reuse_actionbar, null));
getSupportActionBar().setHomeButtonEnabled(false);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
return onNavigationIconClicked();
}
return super.onOptionsItemSelected(item);
}
protected abstract boolean onNavigationIconClicked();
}

View File

@ -1,392 +0,0 @@
package com.gh.base;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.gh.common.constant.Config;
import com.gh.common.util.DataUtils;
import com.gh.common.util.DialogUtils;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.FileUtils;
import com.gh.common.util.GameUtils;
import com.gh.common.util.NetworkUtils;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.ShareUtils;
import com.gh.common.view.DownloadDialog;
import com.gh.download.DataWatcher;
import com.gh.download.DownloadEntity;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.DownloadManagerActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.entity.ApkEntity;
import com.gh.gamecenter.entity.GameEntity;
import com.gh.gamecenter.eventbus.EBDownloadStatus;
import com.gh.gamecenter.eventbus.EBPackage;
import com.gh.gamecenter.manager.PackageManager;
import com.tencent.tauth.Tencent;
/**
* Created by Administrator on 2016/9/19.
* 游戏详情、新闻详情基类(控制底部下载栏)
*/
public abstract class BaseDetailActivity extends BaseActivity implements View.OnClickListener {
protected TextView actionbar_tv_title;
protected RecyclerView detail_rv_show;
protected LinearLayout detail_ll_bottom;
protected TextView detail_tv_download;
protected ProgressBar detail_pb_progressbar;
protected TextView detail_tv_per;
protected LinearLayout reuse_ll_loading;
protected LinearLayout reuse_no_connection;
protected LinearLayout reuse_none_data;
protected TextView reuse_tv_none_data;
protected ImageView iv_share;
protected GameEntity gameEntity;
protected DownloadEntity mDownloadEntity;
protected String entrance;
protected String name;
protected String title;
protected String downloadAddWord;
protected String downloadOffText;
protected Handler handler = new Handler();
private DataWatcher dataWatcher = new DataWatcher() {
@Override
public void onDataChanged(DownloadEntity downloadEntity) {
if (gameEntity != null && gameEntity.getApk().size() == 1) {
String url = gameEntity.getApk().get(0).getUrl();
if (url.equals(downloadEntity.getUrl())) {
if (!"pause".equals(DownloadManager.getInstance(BaseDetailActivity.this).
getStatus(downloadEntity.getUrl()))) {
mDownloadEntity = downloadEntity;
invalidate();
}
}
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
entrance = getIntent().getStringExtra("entrance");
if (getIntent().getBundleExtra("data") != null) {
entrance = getIntent().getBundleExtra("data").getString("entrance");
}
View contentView = View.inflate(this, R.layout.activity_detail, null);
// 添加分享图标
iv_share = new ImageView(this);
iv_share.setImageResource(R.drawable.ic_share);
iv_share.setOnClickListener(this);
iv_share.setVisibility(View.GONE);
iv_share.setPadding(DisplayUtils.dip2px(this, 13),DisplayUtils.dip2px(this, 11)
,DisplayUtils.dip2px(this, 11),DisplayUtils.dip2px(this, 13));
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
DisplayUtils.dip2px(this, 48), DisplayUtils.dip2px(this, 48));
params.addRule( RelativeLayout.CENTER_VERTICAL);
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT );
RelativeLayout reuse_actionbar = (RelativeLayout) contentView.findViewById(
R.id.reuse_actionbar);
reuse_actionbar.addView(iv_share, params);
init(contentView);
actionbar_tv_title = (TextView) findViewById(R.id.actionbar_tv_title);
detail_rv_show = (RecyclerView) findViewById(R.id.detail_rv_show);
detail_ll_bottom = (LinearLayout) findViewById(R.id.detail_ll_bottom);
detail_tv_download = (TextView) findViewById(R.id.detail_tv_download);
detail_pb_progressbar = (ProgressBar) findViewById(R.id.detail_pb_progressbar);
detail_tv_per = (TextView) findViewById(R.id.detail_tv_per);
reuse_ll_loading = (LinearLayout) findViewById(R.id.reuse_ll_loading);
reuse_no_connection = (LinearLayout) findViewById(R.id.reuse_no_connection);
reuse_none_data = (LinearLayout) findViewById(R.id.reuse_none_data);
reuse_tv_none_data = (TextView) findViewById(R.id.reuse_tv_none_data);
detail_ll_bottom.setOnClickListener(this);
detail_tv_download.setOnClickListener(this);
detail_pb_progressbar.setOnClickListener(this);
detail_tv_per.setOnClickListener(this);
reuse_no_connection.setOnClickListener(this);
}
//接收QQ或者QQ空间分享回调
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == com.tencent.connect.common.Constants.REQUEST_QQ_SHARE
|| requestCode == com.tencent.connect.common.Constants.REQUEST_QZONE_SHARE) {
Tencent.onActivityResultData(requestCode, resultCode, data, ShareUtils.getInstance(this).QqShareListener);
}
}
@Override
protected void onResume() {
super.onResume();
if (gameEntity != null
&& gameEntity.getApk() != null
&& gameEntity.getApk().size() == 1) {
initDownload(true);
}
DownloadManager.getInstance(this).addObserver(dataWatcher);
}
@Override
protected void onPause() {
super.onPause();
DownloadManager.getInstance(this).removeObserver(dataWatcher);
}
protected void initDownload(boolean isCheck) {
if (Config.isShow(this)) {
detail_ll_bottom.setVisibility(View.VISIBLE);
detail_rv_show.setPadding(0, 0, 0,
DisplayUtils.dip2px(getApplicationContext(), 60));
} else {
detail_ll_bottom.setVisibility(View.GONE);
detail_rv_show.setPadding(0, 0, 0, 0);
}
if (gameEntity != null && "光环助手".equals(gameEntity.getName())) {
detail_ll_bottom.setVisibility(View.GONE);
detail_rv_show.setPadding(0, 0, 0, 0);
} else if (gameEntity == null || gameEntity.getApk().isEmpty()) {
detail_tv_download.setVisibility(View.VISIBLE);
detail_pb_progressbar.setVisibility(View.GONE);
detail_tv_per.setVisibility(View.GONE);
if (TextUtils.isEmpty(downloadOffText)) {
detail_tv_download.setText("暂无下载");
} else {
detail_tv_download.setText(downloadOffText);
}
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_pause_style);
detail_tv_download.setTextColor(0xFF999999);
detail_tv_download.setClickable(false);
} else {
detail_tv_download.setVisibility(View.VISIBLE);
detail_pb_progressbar.setVisibility(View.GONE);
detail_tv_per.setVisibility(View.GONE);
boolean isInstalled = false;
if (gameEntity.getApk() != null && gameEntity.getApk().size() == 1
&& PackageManager.isInstalled(gameEntity.getApk().get(0).getPackageName())) {
isInstalled = true;
}
if (isInstalled) {
if (PackageManager.isCanUpdate(gameEntity.getId(), gameEntity.getApk().get(0).getPackageName())) {
if (TextUtils.isEmpty(downloadAddWord)) {
detail_tv_download.setBackgroundResource(
R.drawable.game_item_btn_download_style);
detail_tv_download.setText(String.format("更新《%s》",
gameEntity.getName()));
} else {
detail_tv_download.setBackgroundResource(
R.drawable.game_item_btn_download_style);
detail_tv_download.setText(String.format("更新《%s》%s",
gameEntity.getName(), downloadAddWord));
}
} else {
if (gameEntity.getTag() != null && gameEntity.getTag().size() != 0
&& !TextUtils.isEmpty(gameEntity.getApk().get(0).getGhVersion())
&& !PackageUtils.isSignature(this, gameEntity.getApk().get(0).getPackageName())) {
if (TextUtils.isEmpty(downloadAddWord)) {
detail_tv_download.setBackgroundResource(
R.drawable.game_item_btn_plugin_style);
detail_tv_download.setText(String.format("插件化《%s》",
gameEntity.getName()));
} else {
detail_tv_download.setBackgroundResource(
R.drawable.game_item_btn_plugin_style);
detail_tv_download.setText(String.format("插件化《%s》%s",
gameEntity.getName(), downloadAddWord));
}
} else {
if (TextUtils.isEmpty(downloadAddWord)) {
detail_tv_download.setBackgroundResource(
R.drawable.game_item_btn_launch_style);
detail_tv_download.setText(String.format("启动《%s》",
gameEntity.getName()));
} else {
detail_tv_download.setBackgroundResource(
R.drawable.game_item_btn_launch_style);
detail_tv_download.setText(String.format("启动《%s》%s",
gameEntity.getName(), downloadAddWord));
}
}
}
} else {
String status = GameUtils.getDownloadBtnText(this, gameEntity);
if ("插件化".equals(status)) {
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
} else if ("打开".equals(status)) {
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_launch_style);
} else {
detail_tv_download.setBackgroundResource(R.drawable.game_item_btn_download_style);
}
if (TextUtils.isEmpty(downloadAddWord)) {
detail_tv_download.setText(String.format(status + "《%s》",
gameEntity.getName()));
} else {
detail_tv_download.setText(String.format(status + "《%s》%s",
gameEntity.getName(), downloadAddWord));
}
}
}
if (isCheck && gameEntity != null
&& gameEntity.getApk() != null
&& gameEntity.getApk().size() == 1) {
String url = gameEntity.getApk().get(0).getUrl();
DownloadEntity downloadEntity = DownloadManager.getInstance(getApplicationContext()).get(url);
if (downloadEntity != null) {
mDownloadEntity = downloadEntity;
detail_tv_download.setVisibility(View.GONE);
detail_pb_progressbar.setVisibility(View.VISIBLE);
detail_tv_per.setVisibility(View.VISIBLE);
invalidate();
}
}
}
private void invalidate() {
detail_pb_progressbar.setProgress((int) (mDownloadEntity.getPercent() * 10));
detail_tv_per.setTextColor(0xFFFFFFFF);
switch (mDownloadEntity.getStatus()) {
case downloading:
case pause:
case timeout:
case neterror:
case waiting:
detail_tv_per.setText("下载中");
break;
case done:
detail_tv_per.setText("安装");
if (mDownloadEntity.isPluggable()
&& PackageManager.isInstalled(mDownloadEntity.getPackageName())) {
detail_pb_progressbar.setProgressDrawable(getResources().getDrawable(R.drawable.progressbar_plugin_radius_style));
} else {
detail_pb_progressbar.setProgressDrawable(getResources().getDrawable(R.drawable.progressbar_normal_radius_style));
}
break;
case cancel:
case hijack:
case notfound:
initDownload(false);
break;
default:
break;
}
}
// 接收下载被删除消息
public void onEvent(EBDownloadStatus status) {
if ("delete".equals(status.getStatus())
&& gameEntity != null
&& gameEntity.getApk() != null
&& gameEntity.getApk().size() == 1) {
String url = gameEntity.getApk().get(0).getUrl();
if (url.equals(status.getUrl())) {
initDownload(false);
}
}
}
// 接受安装、卸载消息
public void onEventMainThread(EBPackage busFour) {
if (gameEntity != null
&& gameEntity.getApk() != null
&& gameEntity.getApk().size() == 1) {
String packageName = gameEntity.getApk().get(0).getPackageName();
if (packageName.equals(busFour.getPackageName())) {
initDownload(false);
}
}
}
@Override
public void onClick(View v) {
if (v == detail_tv_download) {
if (gameEntity != null && !gameEntity.getApk().isEmpty()) {
if (gameEntity.getApk().size() == 1) {
if (NetworkUtils.isWifiConnected(this)) {
download();
} else {
DialogUtils.showDownloadDialog(this, new DialogUtils.ConfiremListener() {
@Override
public void onConfirem() {
download();
}
});
}
} else {
DownloadDialog.getInstance(this).showPopupWindow(v, gameEntity, entrance, name + ":" + title);
}
} else {
toast("稍等片刻~!游戏正在上传中...");
}
} else if (v == detail_pb_progressbar || v == detail_tv_per) {
String str = detail_tv_per.getText().toString();
if ("下载中".equals(str)) {
Intent intent = new Intent(this, DownloadManagerActivity.class);
intent.putExtra("currentItem", 1);
intent.putExtra("url", gameEntity.getApk().get(0).getUrl());
intent.putExtra("entrance", entrance + "+(" + name + "[" + title + "])");
startActivity(intent);
} else if ("安装".equals(str)) {
PackageUtils.launchSetup(this, mDownloadEntity.getPath());
}
}
}
private void download() {
String str = detail_tv_download.getText().toString();
if (str.contains("启动")) {
DataUtils.onGameLaunchEvent(this, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), name);
PackageUtils.launchApplicationByPackageName(this, gameEntity.getApk().get(0).getPackageName());
} else {
String method;
if (str.contains("更新")) {
method = "更新";
} else if (str.contains("插件化")) {
method = "插件化";
} else {
method = "下载";
}
ApkEntity apkEntity = gameEntity.getApk().get(0);
String msg = FileUtils.isCanDownload(this, apkEntity.getSize());
if (TextUtils.isEmpty(msg)) {
DataUtils.onGameDownloadEvent(this, gameEntity.getName(), apkEntity.getPlatform(), entrance, "下载开始");
DownloadManager.createDownload(this, apkEntity, gameEntity, method, entrance, name + ":" + title);
detail_tv_download.setVisibility(View.GONE);
detail_pb_progressbar.setVisibility(View.VISIBLE);
detail_tv_per.setVisibility(View.VISIBLE);
detail_pb_progressbar.setProgress(0);
detail_tv_per.setText("0.0%");
DownloadManager.getInstance(BaseDetailActivity.this).putStatus(apkEntity.getUrl(), "downloading");
} else {
toast(msg);
}
}
}
}

View File

@ -1,93 +0,0 @@
package com.gh.base;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.gh.gamecenter.listener.OnCallBackListener;
import butterknife.ButterKnife;
import de.greenrobot.event.EventBus;
/**
* Created by LGT on 2016/9/4.
* Fragment 基类
*/
public class BaseFragment extends Fragment implements OnCallBackListener {
protected View view;
protected Handler handler = new Handler();
protected boolean isEverpause;
protected void init(int layout) {
view = View.inflate(getActivity(), layout, null);
ButterKnife.bind(this, view);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
isEverpause = false;
EventBus.getDefault().register(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if(container != null){
container.removeView(view);
}
return view;
}
public boolean isEverpause() {
return isEverpause;
}
@Override
public void onPause() {
super.onPause();
isEverpause = true;
}
@Override
public void onResume() {
super.onResume();
isEverpause = false;
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Override
public void loadDone() {
}
@Override
public void loadDone(Object obj) {
}
@Override
public void loadError() {
}
@Override
public void loadEmpty() {
}
}

View File

@ -1,185 +0,0 @@
package com.gh.base;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.gh.common.constant.Config;
import com.gh.common.util.DataUtils;
import com.gh.common.util.DialogUtils;
import com.gh.common.util.DisplayUtils;
import com.gh.common.util.FileUtils;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.RunningUtils;
import com.gh.common.util.ShareUtils;
import com.gh.common.util.Utils;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.R;
import com.gh.gamecenter.eventbus.EBShowDialog;
import com.gh.gamecenter.manager.SystemBarTintManager;
import com.gh.gamecenter.manager.SystemBarTintManager.SystemBarConfig;
import java.util.ArrayList;
import butterknife.ButterKnife;
import de.greenrobot.event.EventBus;
public class BaseFragmentActivity extends FragmentActivity {
protected String entrance;
private boolean isPause;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Utils.log(this.getClass().getSimpleName());
AppController.getInstance().addActivity(this);
EventBus.getDefault().register(this);
entrance = getIntent().getStringExtra("entrance");
if (getIntent().getBundleExtra("data") != null) {
entrance = getIntent().getBundleExtra("data").getString("entrance");
}
}
public void init(View contentView, String title) {
init(contentView);
TextView actionbar_tv_title = (TextView) findViewById(R.id.actionbar_tv_title);
actionbar_tv_title.setText(title);
}
public void init(View contentView) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setTheme(R.style.AppTheme);
setTranslucentStatus(true);
SystemBarTintManager tintManager = new SystemBarTintManager(this);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintResource(R.color.theme);
SystemBarConfig config = tintManager.getConfig();
contentView.setPadding(0, config.getPixelInsetTop(false), 0,
config.getPixelInsetBottom());
}
setContentView(contentView);
ButterKnife.bind(this);
int actionbar_height = getSharedPreferences(Config.PREFERENCE,
Context.MODE_PRIVATE).getInt("actionbar_height",
DisplayUtils.dip2px(getApplicationContext(), 48));
RelativeLayout reuse_actionbar = (RelativeLayout) findViewById(R.id.reuse_actionbar);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, actionbar_height);
reuse_actionbar.setLayoutParams(params);
findViewById(R.id.actionbar_rl_back).setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
@Override
public void finish() {
super.finish();
AppController.getInstance().removeActivity(this);
}
public void toast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
public void toast(int msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
@TargetApi(19)
protected void setTranslucentStatus(boolean status) {
Window window = getWindow();
WindowManager.LayoutParams winParams = window.getAttributes();
final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
if (status) {
winParams.flags |= bits;
} else {
winParams.flags &= ~bits;
}
window.setAttributes(winParams);
}
//如果是游戏分享newsTitle默认为空
public void showShare(String url, String gameName, String icon, String newsTitle, ArrayList<String> tag) {
//判断是否是官方版
boolean isPlugin = false;
if (tag != null){
for (String s : tag) {
if (!"官方版".equals(s)){
isPlugin = true;
}
}
}
ShareUtils.getInstance(this).showShareWindows(new View(this), url, gameName, icon, newsTitle, isPlugin, true);
if (newsTitle == null){
DataUtils.onEvent(this, "内容分享", gameName);
}else {
DataUtils.onEvent(this, "内容分享", newsTitle);
}
}
public void onEventMainThread(final EBShowDialog showDialog) {
if (!isPause && this.getClass().getName().equals(RunningUtils.getTopActivity(this))) {
if ("hijack".equals(showDialog.getType())) {
DialogUtils.showQqSessionDialog(this, null);// 建议用户联系客服
} else if ("plugin".equals(showDialog.getType())) {
DialogUtils.showPluginDialog(this, new DialogUtils.ConfiremListener(){
@Override
public void onConfirem() {
if (FileUtils.isEmptyFile(showDialog.getPath())) {
Toast.makeText(BaseFragmentActivity.this, "解析包出错(可能被误删了),请重新下载", Toast.LENGTH_SHORT).show();
} else {
startActivity(PackageUtils.getUninstallIntent(BaseFragmentActivity.this, showDialog.getPath()));
}
}
});
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Override
protected void onPause() {
super.onPause();
DataUtils.onPause(this);
isPause = true;
}
@Override
protected void onResume() {
super.onResume();
DataUtils.onResume(this);
isPause = false;
DownloadManager.getInstance(this).initGameMap();
}
}

View File

@ -0,0 +1,43 @@
package com.gh.base;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import butterknife.ButterKnife;
/**
* 目前仅提供butterknife bind方法
*
* @author CsHeng
* @Date 16/06/2017
* @Time 9:55 AM
*/
public abstract class BaseRecyclerViewHolder<T> extends RecyclerView.ViewHolder implements View.OnClickListener {
private T data;
private OnListClickListener mListClickListener;
public BaseRecyclerViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
/**
* 具体的设置监听在childViewHolder 设置
* @param itemView
* @param data 一般情况下只传列表数据
* @param listClickListener 列表事件接口
*/
public BaseRecyclerViewHolder(View itemView, T data, OnListClickListener listClickListener) {
this(itemView);
this.data = data;
this.mListClickListener = listClickListener;
}
@Override
public void onClick(View view) {
mListClickListener.onListClick(view, getAdapterPosition(), data);
}
}

View File

@ -0,0 +1,64 @@
package com.gh.base;
import android.app.Activity;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;
import com.gh.common.util.DataUtils;
import com.gh.download.DownloadManager;
/**
* 1、写点针对生命周期的统计代码
* 2、写点通用的逻辑
* 3、接口解耦
*
* @author CsHeng
* @Date 09/05/2017
* @Time 6:22 PM
*/
public class GHActivityLifecycleCallbacksImpl implements ActivityLifecycleCallbacks {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
DataUtils.onResume(activity);
try {
// 初始化gameMap
DownloadManager.getInstance(activity).initGameMap();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onActivityPaused(Activity activity) {
DataUtils.onPause(activity);
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
}

View File

@ -13,17 +13,17 @@ import android.os.Bundle;
import android.support.v4.app.NotificationCompat;
import android.support.v4.util.ArrayMap;
import android.text.TextUtils;
import android.util.Log;
import android.widget.RemoteViews;
import com.gh.common.util.ClassUtils;
import com.gh.common.util.FileUtils;
import com.gh.common.util.AppDebugConfig;
import com.gh.common.util.EntranceUtils;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.RunningUtils;
import com.gh.common.util.TokenUtils;
import com.gh.common.util.Utils;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.R;
import com.gh.gamecenter.SplashScreenActivity;
import com.halo.assistant.HaloApp;
import com.lightgame.download.FileUtils;
import com.lightgame.utils.Utils;
import com.xiaomi.mipush.sdk.ErrorCode;
import com.xiaomi.mipush.sdk.MiPushClient;
import com.xiaomi.mipush.sdk.MiPushCommandMessage;
@ -41,6 +41,20 @@ import java.util.Date;
import java.util.List;
import java.util.Locale;
import static com.gh.common.util.EntranceUtils.ENTRANCE_MIPUSH;
import static com.gh.common.util.EntranceUtils.HOST_ARTICLE;
import static com.gh.common.util.EntranceUtils.HOST_GAME;
import static com.gh.common.util.EntranceUtils.HOST_WEB;
import static com.gh.common.util.EntranceUtils.HOST_COLUMN;
import static com.gh.common.util.EntranceUtils.KEY_ENTRANCE;
import static com.gh.common.util.EntranceUtils.KEY_GAMEID;
import static com.gh.common.util.EntranceUtils.KEY_ID;
import static com.gh.common.util.EntranceUtils.KEY_NEWSID;
import static com.gh.common.util.EntranceUtils.KEY_TARGET;
import static com.gh.common.util.EntranceUtils.KEY_TO;
import static com.gh.common.util.EntranceUtils.KEY_TYPE;
import static com.gh.common.util.EntranceUtils.KEY_URL;
/**
* 1、PushMessageReceiver是个抽象类该类继承了BroadcastReceiver。
* 2、需要将自定义的DemoMessageReceiver注册在AndroidManifest.xml文件中 <receiver
@ -59,265 +73,250 @@ import java.util.Locale;
* 6、DemoMessageReceiver的onCommandResult方法用来接收客户端向服务器发送命令后的响应结果
* 7、DemoMessageReceiver的onReceiveRegisterResult方法用来接收客户端向服务器发送注册命令后的响应结果
* 8、以上这些方法运行在非UI线程中
*
*
* @author mayixiang
* //TODO 请勿更改此类路径,若需更改时请注意更改./libraries/MiPush/proguard-library.txt混淆对应配置
*/
public class GHPushMessageReceiver extends PushMessageReceiver {
private String mAlias;
@Override
public void onReceivePassThroughMessage(Context context,
MiPushMessage message) {
// 1判断notifyid是否为4
try {
if (message.getNotifyId() == 4) {
JSONObject jsonObject = new JSONObject(message.getContent());
Utils.log(jsonObject.toString());
String channel = jsonObject.getString("channel");
Utils.log("channel = " + channel);
// 1判断渠道号是否一致或是否为ALL
String TD_CHANNEL_ID = (String) PackageUtils.getMetaData(context, context.getPackageName(), "TD_CHANNEL_ID");
if ("ALL".equals(channel) || channel.equalsIgnoreCase(TD_CHANNEL_ID)) {
String type = jsonObject.getString("type");
Utils.log("type = " + type);
if ("NEWS".equals(type)) {
// 新闻推送
JSONArray jsonArray = jsonObject.getJSONArray("package");
ArrayMap<String, Boolean> map = getInstalledMapFromLocal(context);
for (int i = 0; i < jsonArray.length(); i++) {
Boolean b = map.get(jsonArray.getString(i));
if (b != null) {
// 显示推送的消息
showNotification(context, jsonObject, 0);
break;
}
}
} else if ("PLUGIN_UPDATE".equals(type)) {
// 插件更新推送
JSONArray jsonArray = jsonObject.getJSONArray("apk");
ArrayMap<String, Boolean> map = getInstalledMapFromLocal(context);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject apk = jsonArray.getJSONObject(i);
String packageName = apk.getString("package");
Boolean b = map.get(packageName);
if (b != null) {
// 判断是否gh_version是否相同
String gh_version = (String) PackageUtils
.getMetaData(context, packageName, "gh_version");
if (gh_version != null) {
gh_version = gh_version.substring(2);
// 判断gh_version是否相同
if (gh_version.equals(apk.getString("gh_version"))) {
// 判断version是否相同
String version = PackageUtils.getVersionByPackage(
context, packageName);
if (apk.getString("version").equals(version)) {
// 版本相同,无需显示插件更新,继续查看是否有可更新的游戏包
continue;
}
}
}
// 显示推送的消息
showNotification(context, jsonObject, 1);
break;
}
}
} else if ("NEW_GAME".equals(type)) {
// 新游推送
showNotification(context, jsonObject, 2);
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
private String mAlias;
Log.v(AppController.TAG, "onReceivePassThroughMessage is called. " + message.toString());
}
@Override
public void onReceivePassThroughMessage(Context context, MiPushMessage message) {
if (BuildConfig.DEBUG) {
AppDebugConfig.logMethodWithParams(this, message);
}
// 1判断notifyid是否为4
try {
//TODO what is magic number 4?
if (message.getNotifyId() == 4) {
JSONObject jsonObject = new JSONObject(message.getContent());
Utils.log(jsonObject.toString());
String channel = jsonObject.getString("channel");
Utils.log("channel = " + channel);
private void showNotification(Context context, JSONObject jsonObject, int id) throws JSONException {
Intent intent = new Intent();
intent.setAction("com.gh.gamecenter.NOTIFICATION");
intent.putExtra("notifyId", id);
intent.putExtra("notifyData", jsonObject.toString());
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id,
intent, PendingIntent.FLAG_ONE_SHOT);
// 1判断渠道号是否一致或是否为ALL
String packageChannel = HaloApp.getInstance().getChannel();
if ("ALL".equals(channel) || channel.equalsIgnoreCase(packageChannel)) {
String type = jsonObject.getString(KEY_TYPE);
Utils.log("type = " + type);
JSONArray jsonArray;
ArrayMap<String, Boolean> map;
switch (type) {
case "NEWS":
// 新闻推送
jsonArray = jsonObject.getJSONArray("package");
map = getInstalledMapFromLocal(context);
for (int i = 0; i < jsonArray.length(); i++) {
Boolean b = map.get(jsonArray.getString(i));
if (b != null) {
// 显示推送的消息
showNotification(context, jsonObject, 0);
break;
}
}
break;
case "PLUGIN_UPDATE":
// 插件更新推送
jsonArray = jsonObject.getJSONArray("apk");
map = getInstalledMapFromLocal(context);
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject apk = jsonArray.getJSONObject(i);
String packageName = apk.getString("package");
Boolean b = map.get(packageName);
if (b != null) {
// 判断是否gh_version是否相同
String gh_version = (String) PackageUtils
.getMetaData(context, packageName, "gh_version");
if (gh_version != null) {
gh_version = gh_version.substring(2);
// 判断gh_version是否相同
if (gh_version.equals(apk.getString("gh_version"))) {
// 判断version是否相同
String version = PackageUtils.getVersionByPackage(context, packageName);
if (apk.getString("version").equals(version)) {
// 版本相同,无需显示插件更新,继续查看是否有可更新的游戏包
continue;
}
}
}
// 显示推送的消息
showNotification(context, jsonObject, 1);
break;
}
}
break;
case "NEW_GAME":
// 新游推送
showNotification(context, jsonObject, 2);
break;
default:
break;
}
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
NotificationManager nManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
private ArrayMap<String, Boolean> getInstalledMapFromLocal(Context context) {
ArrayMap<String, Boolean> map = new ArrayMap<>();
ArrayList<String> list = getAllPackageName(context);
for (String str : list) {
map.put(str, true);
}
return map;
}
Notification notification = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.logo)
.setTicker(jsonObject.getString("pushTitle"))
.setContentTitle(jsonObject.getString("pushTitle"))
.setContentText(jsonObject.getString("pushDesc"))
.setContentIntent(pendingIntent).build();
private void showNotification(Context context, JSONObject jsonObject, int id) throws JSONException {
Intent intent = new Intent();
intent.setAction("com.gh.gamecenter.NOTIFICATION");
intent.putExtra("notifyId", id);
intent.putExtra("notifyData", jsonObject.toString());
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_ONE_SHOT);
RemoteViews remoteViews = null;
NotificationManager nManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.MANUFACTURER.equals("Meizu")
&& (Build.MODEL.startsWith("m")
|| Build.MODEL.startsWith("MX"))) {
remoteViews = new RemoteViews(context.getPackageName(),
R.layout.notification_meizu);
SimpleDateFormat format = new SimpleDateFormat("HH:mm",
Locale.getDefault());
remoteViews.setTextViewText(R.id.time, format.format(new Date()));
} else if (Build.MANUFACTURER.equals("Xiaomi")
&& (Build.MODEL.startsWith("MI")
|| Build.MODEL.startsWith("HM")
|| Build.MODEL.startsWith("Redmi"))) {
// 小米系统
remoteViews = new RemoteViews(context.getPackageName(),
R.layout.notification_xiaomi);
SimpleDateFormat format = new SimpleDateFormat("ah:mm",
Locale.getDefault());
remoteViews.setTextViewText(R.id.time, format.format(new Date()));
} else if (Build.MANUFACTURER.equals("HUAWEI")) {
// 华为系统
remoteViews = new RemoteViews(context.getPackageName(),
R.layout.notification_huawei);
}
RemoteViews remoteViews = null;
String url = jsonObject.getString("icon");
String path = context.getCacheDir() + File.separator + url.substring(url.lastIndexOf("/") + 1);
int result = FileUtils.downloadFile(url, path);
if (result != 200) {
// 下载出错使用光环logo
path = null;
}
if (Build.MANUFACTURER.equals("Meizu") &&
(Build.MODEL.startsWith("m") || Build.MODEL.startsWith("MX"))) {
remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_meizu);
SimpleDateFormat format = new SimpleDateFormat("HH:mm", Locale.getDefault());
remoteViews.setTextViewText(R.id.time, format.format(new Date()));
} else if (Build.MANUFACTURER.equals("Xiaomi") &&
(Build.MODEL.startsWith("MI") || Build.MODEL.startsWith("HM") || Build.MODEL.startsWith("Redmi"))) {
// 小米系统
remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_xiaomi);
SimpleDateFormat format = new SimpleDateFormat("ah:mm", Locale.getDefault());
remoteViews.setTextViewText(R.id.time, format.format(new Date()));
} else if (Build.MANUFACTURER.equals("HUAWEI")) {
// 华为系统
remoteViews = new RemoteViews(context.getPackageName(), R.layout.notification_huawei);
}
if (remoteViews != null) {
if (path == null) {
remoteViews.setImageViewBitmap(R.id.icon, BitmapFactory.decodeResource(context.getResources(), R.drawable.logo));
} else {
remoteViews.setImageViewBitmap(R.id.icon, BitmapFactory.decodeFile(path));
}
remoteViews.setTextViewText(R.id.title, jsonObject.getString("pushTitle"));
remoteViews.setTextViewText(R.id.intro, jsonObject.getString("pushDesc"));
notification.contentView = remoteViews;
} else {
NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.logo_black)
.setTicker(jsonObject.getString("pushTitle"))
.setContentTitle(jsonObject.getString("pushTitle"))
.setContentText(jsonObject.getString("pushDesc"))
.setContentIntent(pendingIntent);
if (path == null) {
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.logo));
} else {
builder.setLargeIcon(BitmapFactory.decodeFile(path));
}
notification = builder.build();
}
String url = jsonObject.getString("icon");
String path = context.getCacheDir() + File.separator + url.substring(url.lastIndexOf("/") + 1);
int result = FileUtils.downloadFile(url, path);
if (result != 200) {
// 下载出错使用光环logo
path = null;
}
notification.defaults = Notification.DEFAULT_SOUND;// 添加系统默认声音
notification.flags |= Notification.FLAG_AUTO_CANCEL; // FLAG_AUTO_CANCEL表明当通知被用户点击时通知将被清除。
nManager.notify(((int) System.currentTimeMillis() / 1000), notification);// 通过通知管理器来发起通知。如果id不同则每click在status哪里增加一个提示
}
final NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
.setTicker(jsonObject.getString("pushTitle"))
.setContentTitle(jsonObject.getString("pushTitle"))
.setContentText(jsonObject.getString("pushDesc"))
.setContentIntent(pendingIntent)
.setDefaults(Notification.DEFAULT_SOUND) // 添加系统默认声音
.setAutoCancel(true); // FLAG_AUTO_CANCEL表明当通知被用户点击时通知将被清除。
private ArrayMap<String, Boolean> getInstalledMapFromLocal(Context context) {
ArrayMap<String, Boolean> map = new ArrayMap<>();
ArrayList<String> list = getAllPackageName(context);
for (String str : list) {
map.put(str, true);
}
return map;
}
if (remoteViews != null) {
if (path == null) {
remoteViews.setImageViewBitmap(R.id.icon, BitmapFactory.decodeResource(context.getResources(), R.drawable.logo));
} else {
remoteViews.setImageViewBitmap(R.id.icon, BitmapFactory.decodeFile(path));
}
remoteViews.setTextViewText(R.id.title, jsonObject.getString("pushTitle"));
remoteViews.setTextViewText(R.id.intro, jsonObject.getString("pushDesc"));
private ArrayList<String> getAllPackageName(Context context) {
ArrayList<String> list = new ArrayList<>();
List<PackageInfo> packageInfos = context.getPackageManager()
.getInstalledPackages(0);
for (PackageInfo packageInfo : packageInfos) {
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
list.add(packageInfo.packageName);
}
}
return list;
}
builder.setSmallIcon(R.drawable.logo);
builder.setCustomContentView(remoteViews);
@Override
public void onNotificationMessageClicked(Context context, MiPushMessage message) {
Log.v(AppController.TAG, "onNotificationMessageClicked is called. " + message.toString());
} else {
builder.setSmallIcon(R.drawable.logo_black);
if (path == null) {
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.logo));
} else {
builder.setLargeIcon(BitmapFactory.decodeFile(path));
}
}
try {
String content = message.getContent();
JSONObject response = new JSONObject(content);
Bundle bundle = new Bundle();
bundle.putString("entrance", "(小米推送)");
String type = response.getString("type");
if ("article".equals(type)) {
bundle.putString("to", "NewsDetailActivity");
bundle.putString("newsId", response.getString("target"));
} else if ("game".equals(type)) {
bundle.putString("to", "GameDetailActivity");
bundle.putString("gameId", response.getString("target"));
} else if ("column".equals(type)) {
bundle.putString("to", "SubjectActivity");
bundle.putString("id", response.getString("target"));
} else if ("web".equals(type)) {
bundle.putString("to", "WebActivity");
bundle.putString("url", response.getString("target"));
}
if (RunningUtils.isRunning(context)) {
// 应用正在运行,前台或后台
String to = bundle.getString("to");
if (!TextUtils.isEmpty(to)) {
Class<?> clazz = ClassUtils.forName(to);
if (clazz != null) {
Intent intent1 = new Intent(context, clazz);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.putExtra("data", bundle);
context.startActivity(intent1);
}
}
} else {
// 应用未在运行
Intent intent1 = new Intent(context, SplashScreenActivity.class);
intent1.setAction(Intent.ACTION_MAIN);
intent1.addCategory(Intent.CATEGORY_LAUNCHER);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.putExtra("data", bundle);
context.startActivity(intent1);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
nManager.notify(((int) System.currentTimeMillis() / 1000), builder.build());// 通过通知管理器来发起通知。如果id不同则每click在status哪里增加一个提示
}
@Override
public void onNotificationMessageArrived(Context context,
MiPushMessage message) {
Log.v(AppController.TAG, "onNotificationMessageArrived is called. "
+ message.toString());
}
private ArrayList<String> getAllPackageName(Context context) {
ArrayList<String> list = new ArrayList<>();
List<PackageInfo> packageInfos = context.getPackageManager().getInstalledPackages(0);
for (PackageInfo packageInfo : packageInfos) {
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
list.add(packageInfo.packageName);
}
}
return list;
}
@Override
public void onCommandResult(Context context, MiPushCommandMessage message) {
Log.v(AppController.TAG, "onCommandResult is called. "
+ message.toString());
@Override
public void onNotificationMessageClicked(Context context, MiPushMessage message) {
if (BuildConfig.DEBUG) {
AppDebugConfig.logMethodWithParams(this, message);
}
try {
String content = message.getContent();
JSONObject response = new JSONObject(content);
Bundle bundle = new Bundle();
bundle.putString(KEY_ENTRANCE, ENTRANCE_MIPUSH);
String type = response.getString(KEY_TYPE);
String target = response.getString(KEY_TARGET);
String command = message.getCommand();
List<String> arguments = message.getCommandArguments();
switch (type) {
case HOST_ARTICLE:
bundle.putString(KEY_TO, "NewsDetailActivity");
bundle.putString(KEY_NEWSID, target);
break;
case HOST_GAME:
bundle.putString(KEY_TO, "GameDetailActivity");
bundle.putString(KEY_GAMEID, target);
break;
case HOST_COLUMN:
bundle.putString(KEY_TO, "SubjectActivity");
bundle.putString(KEY_ID, target);
break;
case HOST_WEB:
bundle.putString(KEY_TO, "WebActivity");
bundle.putString(KEY_URL, target);
break;
}
EntranceUtils.jumpActivity(context, bundle);
} catch (JSONException e) {
e.printStackTrace();
}
}
if (MiPushClient.COMMAND_SET_ALIAS.equals(command)) {
if (message.getResultCode() == ErrorCode.SUCCESS) {
mAlias = arguments.get(0);
}
}
@Override
public void onNotificationMessageArrived(Context context, MiPushMessage message) {
if (BuildConfig.DEBUG) {
AppDebugConfig.logMethodWithParams(this, message);
}
}
if (TextUtils.isEmpty(mAlias)) {
//添加别名
MiPushClient.setAlias(context, TokenUtils.getDeviceId(context), null);
}
}
@Override
public void onReceiveRegisterResult(Context context, MiPushCommandMessage message) {
if (BuildConfig.DEBUG) {
AppDebugConfig.logMethodWithParams(this, message);
}
}
@Override
public void onReceiveRegisterResult(Context context,
MiPushCommandMessage message) {
Log.v(AppController.TAG, "onReceiveRegisterResult is called. "
+ message.toString());
}
@Override
public void onCommandResult(Context context, MiPushCommandMessage message) {
if (BuildConfig.DEBUG) {
AppDebugConfig.logMethodWithParams(this, message);
}
String command = message.getCommand();
List<String> arguments = message.getCommandArguments();
if (MiPushClient.COMMAND_SET_ALIAS.equals(command)) {
if (message.getResultCode() == ErrorCode.SUCCESS) {
mAlias = arguments.get(0);
}
}
if (TextUtils.isEmpty(mAlias)) {
//添加别名
MiPushClient.setAlias(context, TokenUtils.getDeviceId(context), null);
}
}
}

View File

@ -1,14 +1,9 @@
package com.gh.base;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import com.gh.common.util.ClassUtils;
import com.gh.common.util.RunningUtils;
import com.gh.gamecenter.MainActivity;
import com.gh.gamecenter.SplashScreenActivity;
import com.gh.common.util.EntranceUtils;
import com.umeng.message.UmengNotificationClickHandler;
import com.umeng.message.entity.UMessage;
@ -18,54 +13,39 @@ import org.json.JSONObject;
public class GHUmengNotificationClickHandler extends UmengNotificationClickHandler {
@Override
public void launchApp(Context context, UMessage uMessage) {
@Override
public void launchApp(Context context, UMessage uMessage) {
// super.launchApp(context, uMessage);
try {
String content = uMessage.extra.get("data");
JSONObject response = new JSONObject(content);
Bundle bundle = new Bundle();
bundle.putString("entrance", "(友盟推送)");
String type = response.getString("type");
if ("article".equals(type)) {
bundle.putString("to", "NewsDetailActivity");
bundle.putString("newsId", response.getString("target"));
} else if ("game".equals(type)) {
bundle.putString("to", "GameDetailActivity");
bundle.putString("gameId", response.getString("target"));
} else if ("column".equals(type)) {
bundle.putString("to", "SubjectActivity");
bundle.putString("id", response.getString("target"));
} else if ("web".equals(type)) {
bundle.putString("to", "WebActivity");
bundle.putString("url", response.getString("target"));
}
if (RunningUtils.isRunning(context)
&& MainActivity.class.getName().equals(RunningUtils.getBaseActivity(context))) {
// 应用正在运行,前台或后台
String to = bundle.getString("to");
if (!TextUtils.isEmpty(to)) {
Class<?> clazz = ClassUtils.forName(to);
if (clazz != null) {
Intent intent1 = new Intent(context, clazz);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.putExtra("data", bundle);
context.startActivity(intent1);
}
}
} else {
// 应用未在运行
Intent intent1 = new Intent(context, SplashScreenActivity.class);
intent1.setAction(Intent.ACTION_MAIN);
intent1.addCategory(Intent.CATEGORY_LAUNCHER);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.putExtra("data", bundle);
context.startActivity(intent1);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
try {
String content = uMessage.extra.get(EntranceUtils.KEY_DATA);
JSONObject response = new JSONObject(content);
Bundle bundle = new Bundle();
bundle.putString(EntranceUtils.KEY_ENTRANCE, EntranceUtils.ENTRANCE_UMENG);
String type = response.getString(EntranceUtils.KEY_TYPE);
String target = response.getString(EntranceUtils.KEY_TARGET);
switch (type) {
case EntranceUtils.HOST_ARTICLE:
bundle.putString(EntranceUtils.KEY_TO, "NewsDetailActivity");
bundle.putString(EntranceUtils.KEY_NEWSID, target);
break;
case EntranceUtils.HOST_GAME:
bundle.putString(EntranceUtils.KEY_TO, "GameDetailActivity");
bundle.putString(EntranceUtils.KEY_GAMEID, target);
break;
case EntranceUtils.HOST_COLUMN:
bundle.putString(EntranceUtils.KEY_TO, "SubjectActivity");
bundle.putString(EntranceUtils.KEY_ID, target);
break;
case EntranceUtils.HOST_WEB:
bundle.putString(EntranceUtils.KEY_TO, "WebActivity");
bundle.putString(EntranceUtils.KEY_URL, target);
break;
}
EntranceUtils.jumpActivity(context, bundle);
} catch (JSONException e) {
e.printStackTrace();
}
}
}

View File

@ -1,205 +0,0 @@
package com.gh.base;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.gh.common.constant.Config;
import com.gh.common.util.DataCollectionUtils;
import com.gh.common.util.DataUtils;
import com.gh.common.util.DisplayUtils;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.ConcernActivity;
import com.gh.gamecenter.DownloadManagerActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.SearchActivity;
import com.gh.gamecenter.eventbus.EBDownloadStatus;
import com.gh.gamecenter.eventbus.EBReuse;
import com.gh.gamecenter.manager.PackageManager;
import de.greenrobot.event.EventBus;
/**
* Created by LGT on 2016/9/9.
* 工具栏 搜索控制
*/
public class HomeFragment extends Fragment implements View.OnClickListener {
protected View view;
protected Handler handler = new Handler();
private TextView downloadHint;
private TextView searchHint;
private String hint;
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (!TextUtils.isEmpty(hint)) {
outState.putString("hint", hint);
}
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
hint = savedInstanceState.getString("hint");
}
view = View.inflate(getActivity(), R.layout.fragment_home, null);
SharedPreferences sp = getActivity().getSharedPreferences(
Config.PREFERENCE, Context.MODE_PRIVATE);
LinearLayout home_actionbar = (LinearLayout) view.findViewById(R.id.home_actionbar);
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT, sp.getInt("actionbar_height",
DisplayUtils.dip2px(getActivity(), 48)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
int top = DisplayUtils.getStatusBarHeight(getResources());
home_actionbar.setPadding(0, top, 0, 0);
lparams.height += top;
}
home_actionbar.setLayoutParams(lparams);
initActionBar();
EventBus.getDefault().register(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if (container != null) {
container.removeView(view);
}
return view;
}
private void initActionBar() {
view.findViewById(R.id.actionbar_rl_download).setOnClickListener(this);
view.findViewById(R.id.actionbar_iv_search).setOnClickListener(this);
view.findViewById(R.id.actionbar_notification).setOnClickListener(this);
if (Config.isShow(getActivity())) {
view.findViewById(R.id.actionbar_rl_download).setVisibility(View.VISIBLE);
} else {
view.findViewById(R.id.actionbar_rl_download).setVisibility(View.GONE);
}
downloadHint = (TextView) view.findViewById(R.id.action_tip);
int updateSize = PackageManager.getUpdateListSize();
int downloadSize = DownloadManager.getInstance(getActivity()).getAll().size();
if (downloadSize != 0) {
downloadHint.setVisibility(View.VISIBLE);
downloadHint.setText(String.valueOf(downloadSize));
} else if (updateSize != 0) {
downloadHint.setVisibility(View.VISIBLE);
downloadHint.setText(String.valueOf(updateSize));
} else {
downloadHint.setVisibility(View.GONE);
}
searchHint = (TextView) view.findViewById(R.id.actionbar_search_input);
searchHint.setOnClickListener(this);
if (!TextUtils.isEmpty(hint)) {
searchHint.setHint(hint);
}
}
@Override
public void onClick(View v) {
final int id = v.getId();
if (id == R.id.actionbar_rl_download) {
DataUtils.onEvent(getActivity(), "主页", "下载图标");
DataCollectionUtils.uploadClick(getActivity(), "下载图标", "主页");
Intent intent = new Intent(getActivity(), DownloadManagerActivity.class);
intent.putExtra("entrance", "(工具栏)");
intent.putExtra("currentItem", 0);
startActivity(intent);
} else if (id == R.id.actionbar_iv_search) {
DataUtils.onEvent(getActivity(), "主页", "搜索图标");
DataCollectionUtils.uploadClick(getActivity(), "搜索图标", "主页");
Intent intent = new Intent(getActivity(), SearchActivity.class);
intent.putExtra("clicked", true);
intent.putExtra("hint", hint);
intent.putExtra("entrance", "(工具栏)");
startActivity(intent);
} else if (id == R.id.actionbar_search_input) {
DataUtils.onEvent(getActivity(), "主页", "搜索框");
DataCollectionUtils.uploadClick(getActivity(), "搜索框", "主页");
Intent intent = new Intent(getActivity(), SearchActivity.class);
intent.putExtra("clicked", false);
intent.putExtra("hint", hint);
intent.putExtra("entrance", "(工具栏)");
startActivity(intent);
} else if (id == R.id.actionbar_notification) {
DataUtils.onEvent(getActivity(), "主页", "关注图标");
DataCollectionUtils.uploadClick(getActivity(), "关注图标", "主页");
Intent intent = new Intent(getActivity(), ConcernActivity.class);
intent.putExtra("entrance", "(工具栏)");
startActivity(intent);
}
}
// 打开下载按钮事件
public void onEventMainThread(EBReuse reuse) {
if ("Refresh".equals(reuse.getType())) {
if (Config.isShow(getActivity())) {
view.findViewById(R.id.actionbar_rl_download).setVisibility(View.VISIBLE);
} else {
view.findViewById(R.id.actionbar_rl_download).setVisibility(View.GONE);
}
}
}
public void setHint(String hint) {
if (!TextUtils.isEmpty(hint)) {
this.hint = hint;
if (searchHint != null) {
searchHint.setHint(hint);
}
}
}
public void onEventMainThread(EBDownloadStatus status) {
int updateSize = PackageManager.getUpdateListSize();
int downloadSize = DownloadManager.getInstance(getActivity()).getAll().size();
if (downloadSize != 0) {
downloadHint.setVisibility(View.VISIBLE);
downloadHint.setText(String.valueOf(downloadSize));
} else if (updateSize != 0) {
downloadHint.setVisibility(View.VISIBLE);
downloadHint.setText(String.valueOf(updateSize));
} else {
downloadHint.setVisibility(View.GONE);
}
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}

View File

@ -0,0 +1,19 @@
package com.gh.base;
import android.view.View;
/**
* Created by khy on 26/09/17.
*/
public interface OnListClickListener {
/**
*
* @param view
* @param position list position
* @param data list data (直接强转 如果列表传入不同数据类型 请做好判断)
* @param <T>
*/
<T> void onListClick(View view, int position, T data);
}

View File

@ -1,13 +1,16 @@
package com.gh.gamecenter.listener;
package com.gh.base;
/**
* Created by Administrator on 2016/9/8.
*/
public interface OnCallBackListener {
public interface OnRequestCallBackListener<T> {
void loadDone();
void loadDone(Object obj);
void loadDone(T obj);
void loadError();
void loadEmpty();
}

View File

@ -0,0 +1,13 @@
package com.gh.base;
import java.util.ArrayList;
/**
* @author CsHeng
* @Date 05/05/2017
* @Time 4:52 PM
*/
public interface SearchBarHint {
void setHint(ArrayList<String> hintList);
}

View File

@ -0,0 +1,48 @@
//package com.gh.base;
//
//import java.io.Serializable;
//
//public enum SuggestionType implements Serializable {
//
// FEEDBACK("普通反馈", 1),
// SUGGESTION("功能建议", 2),
// CRASH("发生闪退", 3),
// GAME("游戏问题", 4),
// COLLECT("游戏收录", 5),
// POST("文章投稿", 6);
//
// private String mName;
// private int mIndex;
//
// private SuggestionType(String name, int index) {
// mName = name;
// mIndex = index;
// }
//
// public static String getName(int index) {
// for (SuggestionType c : SuggestionType.values()) {
// if (c.mIndex == index) {
// return c.mName;
// }
// }
// return "";
// }
//
// public static int getIndex(String name) {
// for (SuggestionType c : SuggestionType.values()) {
// if (c.mName == name) {
// return c.mIndex;
// }
// }
// return -1;
// }
//
// public int getIndex() {
// return mIndex;
// }
//
// public String getName() {
// return mName;
// }
//
//}

View File

@ -0,0 +1,32 @@
package com.gh.base.adapter;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.List;
/**
* Created by LGT on 2016/11/17.
* ViewPager FragmentAdapter
*/
public class FragmentAdapter extends FragmentPagerAdapter {
private List<Fragment> mFragmentList;
public FragmentAdapter(FragmentManager fm, List<Fragment> fragmentList) {
super(fm);
this.mFragmentList = fragmentList;
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
}

View File

@ -0,0 +1,152 @@
package com.gh.base.fragment;
import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.gh.base.OnListClickListener;
import com.gh.base.OnRequestCallBackListener;
import com.gh.common.util.EntranceUtils;
import com.gh.gamecenter.eventbus.EBMiPush;
import com.lightgame.utils.RuntimeUtils;
import com.lightgame.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
import butterknife.ButterKnife;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* Created by LGT on 2016/9/4.
* Fragment 基类
*/
public abstract class BaseFragment<T> extends Fragment implements OnRequestCallBackListener<T>,
View.OnClickListener, OnListClickListener{
// TODO private view
protected View view;
protected boolean isEverPause;
protected String mEntrance;
@LayoutRes
protected abstract int getLayoutId();
/**
* 责任链谁处理了就返回true否则返回super.handleOnClick(View view)
*
* @return
*/
protected boolean handleOnClick(View view) {
return true;
}
@Override
public void onClick(View v) {
handleOnClick(v);
}
protected void initView(View view) {
}
protected void postRunnable(Runnable runnable) {
RuntimeUtils.getInstance().runOnUiThread(runnable);
}
// 定时任务全部改用这个方法, 在onDestroy做统一取消定时
protected void postDelayedRunnable(Runnable runnable, long delayMillis) {
RuntimeUtils.getInstance().runOnUiThread(runnable, delayMillis);
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mEntrance = getActivity().getIntent().getStringExtra(EntranceUtils.KEY_ENTRANCE);
isEverPause = false;
EventBus.getDefault().register(this);
view = View.inflate(getContext(), getLayoutId(), null);
ButterKnife.bind(this, view);
initView(view);
}
//TODO 尴尬必须的有subscribe才能register
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onDummyEvent(EBMiPush push) {
//
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
if (container != null) {
container.removeView(view);
}
return view;
}
@Override
public void onResume() {
super.onResume();
isEverPause = false;
}
@Override
public void onPause() {
super.onPause();
isEverPause = true;
}
@Override
public void onDestroy() {
super.onDestroy();
RuntimeUtils.getInstance().removeRunnable();
EventBus.getDefault().unregister(this);
}
public void toast(String msg) {
Utils.toast(getContext(), msg);
}
public boolean isEverPause() {
return isEverPause;
}
@Override
public void loadDone() {
}
@Override
public void loadDone(Object obj) {
}
@Override
public void loadError() {
}
@Override
public void loadEmpty() {
}
@Override
public <T> void onListClick(View view, int position, T data) {
}
protected <K> Observable<K> asyncCall(Observable<K> observable) {
return observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
}

View File

@ -0,0 +1,125 @@
/**
* project: OPlay
* <p/>
* <p/>
* ========================================================================
* amend date amend user amend reason
* 2013-3-6 CsHeng
*/
package com.gh.base.fragment;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.view.View;
import com.lightgame.adapter.BaseFragmentPagerAdapter;
import com.lightgame.config.CommonDebug;
import com.lightgame.view.DoubleTapTextView;
import java.util.ArrayList;
import java.util.List;
/**
* ViewPager 配合RadioGroup实现双切换<br/>
* 记得自己控制onCreateView返回和radioGroup里面radiobutton个数,Viewpager的布局<br/>
*
* @author CsHeng
* @date 2013-3-6
*/
public abstract class BaseFragment_ViewPager extends BaseFragment implements DoubleTapTextView.OnDoubleTapListener {
protected static final String ARGS_INDEX = "index";
protected int mCheckedIndex = 0;
protected PagerAdapter mAdapter;
protected List<Fragment> mFragmentsList;
protected ViewPager mViewPager;
@LayoutRes
protected abstract int getLayoutId();
@IdRes
protected abstract int getViewPagerId();
protected abstract void initFragmentList(List<Fragment> fragments);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mFragmentsList = new ArrayList<>();
initFragmentList(mFragmentsList);
mAdapter = BaseFragmentPagerAdapter.newInstance(getChildFragmentManager(), mFragmentsList);
final Bundle args = getArguments();
if (args != null) {
mCheckedIndex = args.getInt(ARGS_INDEX);
}
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewPager = (ViewPager) view.findViewById(getViewPagerId());
mViewPager.setOffscreenPageLimit(mFragmentsList.size());
mViewPager.setAdapter(mAdapter);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (mCheckedIndex < mFragmentsList.size()) {
mViewPager.setCurrentItem(mCheckedIndex, false);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (getArguments() != null) {
getArguments().putInt(ARGS_INDEX, mCheckedIndex);
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
if (mViewPager != null) {
mViewPager.setAdapter(null);
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mFragmentsList != null) {
mFragmentsList.clear();
}
}
@Override
public boolean onDoubleTap() {
final Fragment fragment = mFragmentsList.get(mViewPager.getCurrentItem());
return fragment instanceof DoubleTapTextView.OnDoubleTapListener && ((DoubleTapTextView.OnDoubleTapListener)
fragment).onDoubleTap();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (CommonDebug.IS_DEBUG) {
CommonDebug.logMethodWithParams(this, requestCode, resultCode, data);
}
List<Fragment> fragments = getChildFragmentManager().getFragments();
if (fragments != null) {
Fragment curFragment = fragments.get(mViewPager.getCurrentItem());
curFragment.onActivityResult(requestCode, resultCode, data);
}
}
}

View File

@ -0,0 +1,109 @@
/**
* project: OPlay
* <p/>
* <p/>
* ========================================================================
* amend date amend user amend reason
* 2013-3-6 CsHeng
*/
package com.gh.base.fragment;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewPager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Checkable;
/**
* ViewPager 配合ViewGroup Checkable实现双切换<br/>
* 记得自己控制onCreateView返回和ViewGroup里面Checkable个数,ViewPager的布局<br/>
*
* @author CsHeng
* @date 2013-3-6
* @update 2014-09-29
*/
public abstract class BaseFragment_ViewPager_Checkable extends BaseFragment_ViewPager implements
ViewPager.OnPageChangeListener {
protected ViewGroup mCheckableGroup;
@IdRes
protected abstract int getCheckableGroupId();
protected boolean getSmoothScroll() {
return false;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mCheckableGroup = (ViewGroup) view.findViewById(getCheckableGroupId());
for (int i = 0, size = mCheckableGroup.getChildCount(); i < size; i++) {
mCheckableGroup.getChildAt(i).setOnClickListener(this);
}
mViewPager.addOnPageChangeListener(this);
}
@Override
public void onDestroyView() {
super.onDestroyView();
mViewPager.removeOnPageChangeListener(this);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
checkIndex(mCheckedIndex);
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int index) {
onPageChanged(index);
}
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
protected boolean handleOnClick(View view) {
final int toCheck = mCheckableGroup.indexOfChild(view);
if (toCheck != -1) {
mViewPager.setCurrentItem(toCheck, getSmoothScroll());
return true;
}
return super.handleOnClick(view);
}
protected void checkIndex(int index) {
final int childCount = mCheckableGroup.getChildCount();
if (index < childCount && mCheckedIndex < childCount) {
final View toChecked = mCheckableGroup.getChildAt(index);
if (toChecked instanceof Checkable) {
((Checkable) toChecked).setChecked(true);
}
if (index != mCheckedIndex) {
final View checkedChild = mCheckableGroup.getChildAt(mCheckedIndex);
if (checkedChild instanceof Checkable) {
((Checkable) checkedChild).setChecked(false);
}
}
mCheckedIndex = index;
}
}
protected void onPageChanged(int index) {
checkIndex(index);
}
}

View File

@ -3,19 +3,57 @@ package com.gh.common.constant;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.gh.gamecenter.BuildConfig;
public class Config {
public static final String HOST = "http://api.ghzhushou.com/v2d4/";
public static final String USER_HOST = "http://user.ghzhushou.com/v1d2/";
public static final String COMMENT_HOST = "http://comment.ghzhushou.com/v1d1/";
public static final String DATA_HOST = "http://data.ghzhushou.com/";
public static final String LIBAO_HOST = "http://libao.ghzhushou.com/v1d1/";
public static final String PREFERENCE = "ghzhushou";
public static final String API_HOST = BuildConfig.API_HOST;
public static final String USER_HOST = BuildConfig.USER_HOST;
public static final String COMMENT_HOST = BuildConfig.COMMENT_HOST;
public static final String DATA_HOST = BuildConfig.DATA_HOST;
public static final String LIBAO_HOST = BuildConfig.LIBAO_HOST;
public static final String MESSAGE_HOST = BuildConfig.MESSAGE_HOST;
public static final String USERSEA_HOST = BuildConfig.USERSEA_HOST;
public static boolean isShow(Context context) {
SharedPreferences sp = context.getSharedPreferences(Config.PREFERENCE, Context.MODE_PRIVATE);
return sp.getBoolean("isShow", true);
}
/**
* 需要配置的请使用{@link PreferenceManager#getDefaultSharedPreferences(Context)}
*/
// @Deprecated
// public static final String PREFERENCE = "ghzhushou";
// Third-Party confs
public static final String WECHAT_APPID = BuildConfig.WECHAT_APPID;
public static final String WECHAT_SECRET = BuildConfig.WECHAT_SECRET;
public static final String TENCENT_APPID = BuildConfig.TENCENT_APPID;
public static final String WEIBO_APPKEY = BuildConfig.WEIBO_APPKEY;
public static final String MIPUSH_APPID = BuildConfig.MIPUSH_APPID;
public static final String MIPUSH_APPKEY = BuildConfig.MIPUSH_APPKEY;
public static final String MTA_APPKEY = BuildConfig.MTA_APPKEY;
public static final String TALKINGDATA_APPID = BuildConfig.TD_APPID;// TalkingData
public static final String UMENG_APPKEY = BuildConfig.UMENG_APPKEY;
public static final String UMENG_MESSAGE_SECRET = BuildConfig.UMENG_MESSAGE_SECRET;
public static final String USERSEA_APP_ID = BuildConfig.USERSEA_APP_ID; // 登录验证
public static final String USERSEA_APP_SECRET = BuildConfig.USERSEA_APP_SECRET; // 登录验证
public static final String BUGLY_APPID = BuildConfig.BUGLY_APPID;
public static final String PATCH_VERSION_NAME = BuildConfig.PATCH_VERSION_NAME; // 补丁包版本 对应关于->版本号
// http://www.ghzs666.com/article/${articleId}.html
public static final String URL_ARTICLE = "http://www.ghzs666.com/article/"; // TODO ghzs/ghzs666 统一
public static final String PATCHES = "patches";
public static boolean isShow(Context context) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
return sp.getBoolean("isShow", true);
}
public static String getExceptionMsg(Context context) {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
return sp.getString("errMsg", null);
}
public static void setExceptionMsg(Context context, String errMsg) {
PreferenceManager.getDefaultSharedPreferences(context).edit().putString("errMsg", errMsg).apply();
}
}

View File

@ -2,41 +2,36 @@ package com.gh.common.constant;
public class Constants {
public static final int CONTINUE_DOWNLOAD_TASK = 0x123;
public static final int PAUSE_DOWNLOAD_TASK = 0x124;
public static final int DOWNLOAD_ROLL = 0x125;
public static final int SEND_NEWS_FEEDBACK = 0x126;
public static final int SEND_COMMENT_FEEDBACK = 0x127;
public static final String KEY_DOWNLOAD_ENTRY = "key_download_entry";
public static final String KEY_DOWNLOAD_ACTION = "key_download_action";
public static final int MAX_DOWNLOAD_THREAD_SIZE = 3;
public static final int MAX_DOWNLOADING_SIZE = 3;
public static final long SPEED_CHECK_INTERVAL = 1000;//速度监测频率
//手机号码匹配规则
public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";
public static final String REGEX_ACCOUNT = "^[a-zA-Z_]\\w{5,17}$";
public static final String REGEX_PASSWORD = "^[a-zA-Z]\\w{5,31}$";
//输入规则
public static final String INPUT_RULE = "0123456789abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLNMOPQRSTUVWXYZ_";
//最少需要多少数据才能上传
public static final int DATA_AMOUNT = 20;
//游戏 cd间隔
public static final int GAME_CD = 5 * 60 * 1000;
//新闻 cd间隔
public static final int NEWS_CD = 10 * 60 * 1000;
//platform cd间隔
public static final int PLATFORM_CD = 10 * 60 * 1000;
//update cd间隔
public static final int UPDATE_CD = 5 * 60 * 1000;
//搜索 cd间隔
public static final int SEARCH_CD = 5 * 60 * 1000;
//评论 cd间隔
public static final int COMMENT_CD = 60 * 1000;
public static final int SEND_NEWS_FEEDBACK = 0x126;
public static final int SEND_COMMENT_FEEDBACK = 0x127;
public final static int LIST_FOOTER_ITEM = 1;
public final static int LIST_HEAD_ITEM = 1;
public final static int NOT_NETWORK_CODE = 504; // 没有网络的状态码(应该是这个吧!)
//手机号码匹配规则
public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";
public static final String REGEX_ACCOUNT = "^[a-zA-Z_]\\w{5,17}$";
public static final String REGEX_PASSWORD = "^[a-zA-Z]\\w{5,31}$";
//输入规则
public static final String INPUT_RULE = "0123456789abcdefghijklnmopqrstuvwxyzABCDEFGHIJKLNMOPQRSTUVWXYZ_";
//最少需要多少数据才能上传
public static final int DATA_AMOUNT = 20;
//游戏 cd间隔
public static final int GAME_CD = 5 * 60 * 1000;
//新闻 cd间隔
public static final int NEWS_CD = 10 * 60 * 1000;
//platform cd间隔
public static final int PLATFORM_CD = 10 * 60 * 1000;
//update cd间隔
public static final int UPDATE_CD = 5 * 60 * 1000;
//搜索 cd间隔
public static final int SEARCH_CD = 5 * 60 * 1000;
//评论 cd间隔
public static final int COMMENT_CD = 60 * 1000;
}

View File

@ -5,23 +5,23 @@ package com.gh.common.constant;
*/
public class ItemViewType {
public static final int COLUMN_HEADER = 0; // 专题头部布局
public static final int GAME_SLIDE = 1; // 滚动图布局
public static final int GAME_NORMAL = 2; // 正常游戏布局
public static final int GAME_TEST = 3; // 测试游戏布局
public static final int GAME_IMAGE = 4; // 游戏大图布局
public static final int NEWS_HEADER = 5; // 新闻头部布局
public static final int NEWS_TEXT = 6; // 新闻文本布局
public static final int NEWS_IMAGE = 7; // 新闻带图布局
public static final int NEWS_IMAGE1 = 8; // 新闻带一张小图布局
public static final int NEWS_IMAGE2 = 9; // 新闻带三张小图布局
public static final int NEWS_IMAGE3 = 10; // 新闻带一张大图布局
public static final int NEWS_DIGEST = 11; // 新闻摘要布局
public static final int SEARCH_NORMAL = 12; // 搜索正常布局
public static final int SEARCH_DELETE = 13; // 清空历史记录布局
public static final int LOADING = 14; // 加载布局
public static final int LIBAO_NORMAL = 15; // 礼包正常布局
public static final int LIBAO_SKIP_CONCERN = 16; // 跳转关注管理页面布局
public static final int KC_HINT = 16;
public static final int COLUMN_HEADER = 0; // 专题头部布局
public static final int GAME_SLIDE = 1; // 滚动图布局
public static final int GAME_NORMAL = 2; // 正常游戏布局
public static final int GAME_TEST = 3; // 测试游戏布局
public static final int GAME_IMAGE = 4; // 游戏大图布局
public static final int NEWS_HEADER = 5; // 新闻头部布局
public static final int NEWS_TEXT = 6; // 新闻文本布局
public static final int NEWS_IMAGE = 7; // 新闻带图布局
public static final int NEWS_IMAGE1 = 8; // 新闻带一张小图布局
public static final int NEWS_IMAGE2 = 9; // 新闻带三张小图布局
public static final int NEWS_IMAGE3 = 10; // 新闻带一张大图布局
public static final int NEWS_DIGEST = 11; // 新闻摘要布局
public static final int SEARCH_NORMAL = 12; // 搜索正常布局
public static final int LOADING = 14; // 加载布局
public static final int LIBAO_NORMAL = 15; // 礼包正常布局
public static final int LIBAO_SKIP_CONCERN = 16; // 跳转关注管理页面布局
public static final int KC_HINT = 17;
public static final int GAME_PULGIN = 18; // 游戏插件模块
}

View File

@ -10,11 +10,11 @@ import com.sina.weibo.sdk.auth.Oauth2AccessToken;
* 该类定义了微博授权时所需要的参数。
*/
public class AccessTokenKeeper {
private static final String PREFERENCES_NAME = "com_weibo_sdk_android";
private static final String PREFERENCES_NAME = "com_weibo_sdk_android";
private static final String KEY_UID = "uid";
private static final String KEY_ACCESS_TOKEN = "access_token";
private static final String KEY_EXPIRES_IN = "expires_in";
private static final String KEY_UID = "uid";
private static final String KEY_ACCESS_TOKEN = "access_token";
private static final String KEY_EXPIRES_IN = "expires_in";
private static final String KEY_REFRESH_TOKEN = "refresh_token";
/**
@ -41,7 +41,6 @@ public class AccessTokenKeeper {
* 从 SharedPreferences 读取 Token 信息。
*
* @param context 应用程序上下文环境
*
* @return 返回 Token 对象
*/
public static Oauth2AccessToken readAccessToken(Context context) {

View File

@ -0,0 +1,31 @@
package com.gh.common.util;
import com.gh.gamecenter.entity.ApkEntity;
import com.gh.gamecenter.entity.GameEntity;
import com.gh.gamecenter.manager.PackageManager;
import java.util.List;
/**
* Created by khy on 10/05/17.
*/
public class ApkActiveUtils {
// 过滤隐藏apk包
public static void filterHideApk(GameEntity gameEntity) {
if (gameEntity == null || gameEntity.getApk() == null
|| gameEntity.getApk().size() == 0) return;
List<ApkEntity> apkList = gameEntity.getApk();
for (int i = 0; i < apkList.size(); i++) {
ApkEntity apkEntity = apkList.get(i);
String packageName = apkEntity.getPackageName();
String id = gameEntity.getId();
if (!apkEntity.isActive() && !PackageManager.isCanPluggable(id, packageName)) {
apkList.remove(i);
i--;
}
}
}
}

View File

@ -0,0 +1,116 @@
package com.gh.common.util;
import android.os.Debug;
import android.util.Log;
import com.gh.gamecenter.BuildConfig;
import java.lang.reflect.Field;
/**
* App的测试配置项
*/
public class AppDebugConfig {
/**
* debug模式发布打包需要置为false可以通过混淆让调试的log文本从代码文件中消除避免被反编译时漏泄相关信息。
*/
public static final boolean IS_DEBUG = BuildConfig.DEBUG;
public static void logMethodName(Object object) {
if (IS_DEBUG) {
try {
Log.v(getLogTag(object), getMethodName());
} catch (Throwable e) {
}
}
}
private static String getLogTag(Object object) {
return object.getClass().getSimpleName() + "[" + object.hashCode() + "]";
}
private static String getMethodName() {
final Thread current = Thread.currentThread();
final StackTraceElement trace = current.getStackTrace()[4];
return trace.getMethodName();
}
public static void logMethodName(Class<?> cls) {
if (IS_DEBUG) {
try {
Log.v(getLogTag(cls), getMethodName());
} catch (Throwable e) {
}
}
}
public static void logParams(String tag, Object... params) {
if (IS_DEBUG) {
for (Object obj : params) {
Log.i(tag, "" + obj);
}
}
}
public static void logNetworkRequest(Object object, String request, String response) {
if (IS_DEBUG) {
Log.i(getLogTag(object), String.format("【Request】:%s", request));
Log.i(getLogTag(object), String.format("【Response】:%s", response));
}
}
public static void logFields(Class<?> classType) {
if (IS_DEBUG) {
try {
final String name = classType.getSimpleName();
final Field[] fs = classType.getDeclaredFields();
for (Field f : fs) {
Log.i(name, "Filed:" + f.getName());
}
} catch (Exception e) {
}
}
}
public static void logMethodWithParams(Object object, Object... params) {
if (IS_DEBUG) {
try {
final StringBuilder sb = new StringBuilder();
sb.append(getMethodName()).append(":");
for (Object obj : params) {
sb.append('[').append(obj).append("], ");
}
Log.v(getLogTag(object), sb.toString());
} catch (Exception e) {
}
}
}
public static void logMemoryInfo() {
if (IS_DEBUG) {
try {
// final ActivityManager activityManager = (ActivityManager) getActivity().getSystemService(Context
// .ACTIVITY_SERVICE);
// activityManager.getMemoryClass();
final String tag = "MM_INFO";
// Log.i(tag, "Class " + activityManager.getMemoryClass());
final long mb = 1024 * 1024;
//Get VM Heap Size by calling:
Log.i(tag, "VM Heap Size:" + Runtime.getRuntime().totalMemory() / mb);
// Get VM Heap Size Limit by calling:
Log.i(tag, "VM Heap Size Limit:" + Runtime.getRuntime().maxMemory() / mb);
// Get Allocated VM Memory by calling:
Log.i(tag, "Allocated VM Memory:" + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime()
.freeMemory()) / mb);
//Get Native Allocated Memory by calling:
Log.i(tag, "Native Allocated Memory:" + Debug.getNativeHeapAllocatedSize() / mb);
} catch (Exception e) {
}
}
}
}

View File

@ -8,6 +8,9 @@ import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.media.ExifInterface;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
@ -15,102 +18,135 @@ import java.io.IOException;
*/
public class BitmapUtils {
/**
* 根据文件路径返回bitmap
* @param filepath 文件路径
* @param w 宽
* @param h 高
* @return bitmap
*/
public static Bitmap getBitmapByFile(String filepath, int w, int h) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
// 设置为ture只获取图片大小
options.inJustDecodeBounds = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
// 返回为空
BitmapFactory.decodeFile(filepath, options);
int width = options.outWidth;
int height = options.outHeight;
float scaleWidth = 0.f, scaleHeight = 0.f;
if (width > w || height > h) {
// 缩放
scaleWidth = ((float) width) / w;
scaleHeight = ((float) height) / h;
}
options.inJustDecodeBounds = false;
int scale = (int) Math.ceil(Math.max(scaleWidth, scaleHeight));
if (scale % 2 == 1) {
scale += 1;
}
options.inSampleSize = scale;
Bitmap bitmap = BitmapFactory.decodeFile(filepath, options);
bitmap = rotatePicture(filepath, bitmap);
if (bitmap != null) {
return bitmap;
}
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 保存图片
*
* @param newPath
* @param filePath
* @return
*/
public static boolean savePicture(String newPath, String filePath) {
BitmapFactory.Options options = new BitmapFactory.Options();
// options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, options);
public static Bitmap rotatePicture(String path, Bitmap bitmap) {
int rotate = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
if (rotate != 0) {
Matrix matrix = new Matrix();
matrix.setRotate(rotate);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
} else {
return bitmap;
}
}
File file = new File(newPath);
int quality = 80;
do {
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, bos);
bos.flush();
bos.close();
} catch (IOException e) {
file.delete();
e.printStackTrace();
return false;
}
quality -= 10;
if (quality < 10) {
quality = 10;
}
} while (file.length() > 81920 && quality > 10);
return true;
}
/**
* Drawable转Bitmap
*
*/
public static Bitmap drawableToBitmap(Drawable drawable){
if(drawable == null){
return null;
}
/**
* 根据文件路径返回bitmap
*
* @param filepath 文件路径
* @param w 宽
* @param h 高
* @return bitmap
*/
public static Bitmap getBitmapByFile(String filepath, int w, int h) {
try {
BitmapFactory.Options options = new BitmapFactory.Options();
// 设置为ture只获取图片大小
options.inJustDecodeBounds = true;
options.inPreferredConfig = Bitmap.Config.RGB_565;
// 返回为空
BitmapFactory.decodeFile(filepath, options);
int width = options.outWidth;
int height = options.outHeight;
float scaleWidth = 0.f, scaleHeight = 0.f;
if (width > w || height > h) {
// 缩放
scaleWidth = ((float) width) / w;
scaleHeight = ((float) height) / h;
}
options.inJustDecodeBounds = false;
int scale = (int) Math.ceil(Math.max(scaleWidth, scaleHeight));
if (scale % 2 == 1) {
scale += 1;
}
options.inSampleSize = scale;
Bitmap bitmap = BitmapFactory.decodeFile(filepath, options);
bitmap = rotatePicture(filepath, bitmap);
if (bitmap != null) {
return bitmap;
}
return null;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// 取 drawable 的长宽
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
// 取 drawable 的颜色格式
Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565;
//建立对应的Bitmap
Bitmap bitmap = Bitmap.createBitmap(w, h, config);
// 建立对应 bitmap 的画布
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
// 把 drawable 内容画到画布中
drawable.draw(canvas);
public static Bitmap rotatePicture(String path, Bitmap bitmap) {
int rotate = 0;
try {
ExifInterface exifInterface = new ExifInterface(path);
int orientation = exifInterface.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL);
switch (orientation) {
case ExifInterface.ORIENTATION_ROTATE_90:
rotate = 90;
break;
case ExifInterface.ORIENTATION_ROTATE_180:
rotate = 180;
break;
case ExifInterface.ORIENTATION_ROTATE_270:
rotate = 270;
break;
}
} catch (IOException e) {
e.printStackTrace();
}
if (rotate != 0) {
Matrix matrix = new Matrix();
matrix.setRotate(rotate);
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
bitmap.getHeight(), matrix, true);
} else {
return bitmap;
}
}
return bitmap;
}
/**
* Drawable转Bitmap
*/
public static Bitmap drawableToBitmap(Drawable drawable) {
if (drawable == null) {
return null;
}
// 取 drawable 的长宽
int w = drawable.getIntrinsicWidth();
int h = drawable.getIntrinsicHeight();
// 取 drawable 的颜色格式
Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565;
//建立对应的Bitmap
Bitmap bitmap = Bitmap.createBitmap(w, h, config);
// 建立对应 bitmap 的画布
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, w, h);
// 把 drawable 内容画到画布中
drawable.draw(canvas);
return bitmap;
}
}

View File

@ -0,0 +1,35 @@
package com.gh.common.util;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import com.gh.gamecenter.LoginActivity;
/**
* Created by khy on 28/06/17.
*/
public class CheckLoginUtils {
public static void checkLogin(final Context context, OnLoggenInListener listener) {
String token = LoginUtils.getToken(context);
if (TextUtils.isEmpty(token)) {
DialogUtils.showWarningDialog(context, "登录提示", "需要登录才能使用该功能喔!", "取消", "快速登录",
new DialogUtils.ConfirmListener() {
@Override
public void onConfirm() {
Intent intent = LoginActivity.getIntent(context, false);
context.startActivity(intent);
}
}, null);
} else {
listener.onLoggedIn();
}
}
public interface OnLoggenInListener {
void onLoggedIn();
}
}

View File

@ -3,18 +3,18 @@ package com.gh.common.util;
public class ClassUtils {
public static Class<?> forName(String name) {
if ("NewsActivity".equals(name)) {
name = "NewsDetailActivity";
} else if ("GameDetailsActivity".equals(name)) {
name = "GameDetailActivity";
}
try {
return Class.forName("com.gh.gamecenter." + name);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
public static Class<?> forName(String name) {
if ("NewsActivity".equals(name)) {
name = "NewsDetailActivity";
} else if ("GameDetailsActivity".equals(name)) {
name = "GameDetailActivity";
}
try {
return Class.forName("com.gh.gamecenter." + name);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,103 @@
package com.gh.common.util
import android.content.Context
import com.gh.gamecenter.eventbus.EBCollectionChanged
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import okhttp3.MediaType
import okhttp3.RequestBody
import okhttp3.ResponseBody
import org.greenrobot.eventbus.EventBus
import org.json.JSONObject
import retrofit2.HttpException
import rx.Observable
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
/**
* Created by khy on 26/07/17.
*/
object CollectionUtils {
enum class CollectionType {
toolkit, article
}
fun postCollection(context: Context, content: String, type: CollectionType, listener: OnCollectionListener) {
val body = RequestBody.create(MediaType.parse("application/json"), content)
val postCollection = when (type) {
CollectionType.article -> RetrofitManager.getInstance(context).getApi().postCollectionArticle(body)
CollectionType.toolkit -> RetrofitManager.getInstance(context).getApi().postCollectionTools(body)
}
postCollection
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
super.onResponse(response)
listener.onSuccess()
EventBus.getDefault().post(EBCollectionChanged(JSONObject(content).getString("_id"), true, type))
}
override fun onFailure(e: HttpException?) {
super.onFailure(e)
if (e != null) {
try {
val string = e.response()?.errorBody()?.string()
val errorBody = JSONObject(string)
if (errorBody.getInt("status") == 40031) {
listener.onSuccess()
return
}
} catch (e: Exception) {
e.printStackTrace()
}
}
listener.onError()
}
})
}
fun deleteCollection(context: Context, id: String, type: CollectionType, listener: OnCollectionListener) {
val postCollection: Observable<ResponseBody>
when (type) {
CollectionType.article -> postCollection = RetrofitManager.getInstance(context).getApi().deletaCollectionArticle(id)
CollectionType.toolkit -> postCollection = RetrofitManager.getInstance(context).getApi().deleteCollectionTools(id)
}
postCollection
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody?) {
super.onResponse(response)
listener.onSuccess()
EventBus.getDefault().post(EBCollectionChanged(id, false, type))
}
override fun onFailure(e: HttpException?) {
super.onFailure(e)
listener.onError()
}
})
}
fun patchCollection(context: Context, id: String, type: CollectionType) {
val postCollection = when (type) {
CollectionType.article -> RetrofitManager.getInstance(context).getApi().patchCollectionArticle(id)
CollectionType.toolkit -> RetrofitManager.getInstance(context).getApi().patchCollectionTools(id)
}
postCollection
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {})
}
interface OnCollectionListener {
fun onSuccess()
fun onError()
}
}

View File

@ -0,0 +1,303 @@
package com.gh.common.util;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Color;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.gh.gamecenter.CommentDetailActivity;
import com.gh.gamecenter.MessageDetailActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.adapter.OnCommentCallBackListener;
import com.gh.gamecenter.adapter.viewholder.CommentViewHolder;
import com.gh.gamecenter.entity.CommentEntity;
import com.gh.gamecenter.entity.UserDataEntity;
import com.gh.gamecenter.entity.UserInfoEntity;
import com.lightgame.utils.Utils;
import org.json.JSONException;
import org.json.JSONObject;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import retrofit2.HttpException;
/**
* Created by khy on 2017/3/22.
*/
public class CommentUtils {
public static void setCommentTime(TextView textView, long time) {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd", Locale.getDefault());
try {
long today = format.parse(format.format(new Date())).getTime();
long day = time * 1000;
if (day >= today && day < today + 86400 * 1000) {
long min = new Date().getTime() / 1000 - day / 1000;
int hour = (int) (min / (60 * 60));
if (hour == 0) {
if (min < 60) {
textView.setText("刚刚");
} else {
textView.setText(String.format(Locale.getDefault(), "%d分钟前", (int) (min / 60)));
}
} else {
textView.setText(String.format(Locale.getDefault(), "%d小时前", hour));
}
} else if (day >= today - 86400 * 1000 && day < today) {
format.applyPattern("HH:mm");
textView.setText("昨天 ");
} else {
format.applyPattern("yyyy-MM-dd");
textView.setText(format.format(day));
}
} catch (ParseException e) {
e.printStackTrace();
format.applyPattern("yyyy-MM-dd");
textView.setText(format.format(time * 1000));
}
}
public static void showReportDialog(final CommentEntity commentEntity, final Context context,
final OnCommentCallBackListener listener, final String newsId) {
final Dialog dialog = new Dialog(context);
LinearLayout container = new LinearLayout(context);
container.setOrientation(LinearLayout.VERTICAL);
container.setBackgroundColor(Color.WHITE);
container.setPadding(0, DisplayUtils.dip2px(context, 12), 0, DisplayUtils.dip2px(context, 12));
List<String> dialogType = new ArrayList<>();
if (commentEntity.getUserData() == null || !commentEntity.getUserData().isCommentOwn()) {
dialogType.add("回复");
}
dialogType.add("复制");
dialogType.add("举报");
if (commentEntity.getParent() != null) {
dialogType.add("查看对话");
}
for (String s : dialogType) {
final TextView reportTv = new TextView(context);
reportTv.setText(s);
reportTv.setTextSize(17);
reportTv.setTextColor(ContextCompat.getColor(context, R.color.title));
reportTv.setBackgroundResource(R.drawable.textview_white_style);
int widthPixels = context.getResources().getDisplayMetrics().widthPixels;
reportTv.setLayoutParams(new LinearLayout.LayoutParams((widthPixels * 9) / 10,
LinearLayout.LayoutParams.WRAP_CONTENT));
reportTv.setPadding(DisplayUtils.dip2px(context, 20), DisplayUtils.dip2px(context, 12),
0, DisplayUtils.dip2px(context, 12));
container.addView(reportTv);
reportTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.cancel();
switch (reportTv.getText().toString()) {
case "回复":
CheckLoginUtils.checkLogin(context, new CheckLoginUtils.OnLoggenInListener() {
@Override
public void onLoggedIn() {
if (listener != null) {
listener.onCommentCallback(commentEntity);
} else if (!TextUtils.isEmpty(newsId)) {
context.startActivity(MessageDetailActivity.getMessageDetailIntent(context, commentEntity, newsId));
} else {
Utils.toast(context, "缺少关键属性");
}
}
});
break;
case "复制":
LibaoUtils.copyLink(commentEntity.getContent(), context);
break;
case "举报":
CheckLoginUtils.checkLogin(context, new CheckLoginUtils.OnLoggenInListener() {
@Override
public void onLoggedIn() {
showReportTypeDialog(commentEntity, context);
}
});
break;
case "查看对话":
context.startActivity(CommentDetailActivity.getCommentDetailIntent(context, commentEntity.getId()));
break;
}
}
});
}
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(container);
dialog.show();
}
private static void showReportTypeDialog(final CommentEntity commentEntity, final Context mContext) {
final String[] arrReportType = new String[]{"垃圾广告营销", "恶意攻击谩骂", "淫秽色情信息",
"违法有害信息", "其它"};
int widthPixels = mContext.getResources().getDisplayMetrics().widthPixels;
final Dialog reportTypeDialog = new Dialog(mContext);
LinearLayout container = new LinearLayout(mContext);
container.setOrientation(LinearLayout.VERTICAL);
container.setPadding(0, DisplayUtils.dip2px(mContext, 12), 0, DisplayUtils.dip2px(mContext, 12));
container.setBackgroundColor(Color.WHITE);
for (final String s : arrReportType) {
TextView reportTypeTv = new TextView(mContext);
reportTypeTv.setText(s);
reportTypeTv.setTextSize(17);
reportTypeTv.setTextColor(ContextCompat.getColor(mContext, R.color.title));
reportTypeTv.setBackgroundResource(R.drawable.textview_white_style);
reportTypeTv.setLayoutParams(new LinearLayout.LayoutParams((widthPixels * 9) / 10,
LinearLayout.LayoutParams.WRAP_CONTENT));
reportTypeTv.setPadding(DisplayUtils.dip2px(mContext, 20), DisplayUtils.dip2px(mContext, 12),
0, DisplayUtils.dip2px(mContext, 12));
container.addView(reportTypeTv);
reportTypeTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("comment_id", commentEntity.getId());
jsonObject.put("reason", s);
} catch (JSONException e) {
e.printStackTrace();
}
PostCommentUtils.addReportData(mContext, jsonObject.toString(),
new PostCommentUtils.PostCommentListener() {
@Override
public void postSuccess(JSONObject response) {
Utils.toast(mContext, "感谢您的举报");
}
@Override
public void postFailed(Throwable error) {
Utils.toast(mContext, "举报失败,请检查网络设置");
}
});
reportTypeDialog.cancel();
}
});
}
reportTypeDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
reportTypeDialog.setContentView(container);
reportTypeDialog.show();
}
public static void postVote(final Context context, final CommentEntity commentEntity,
final TextView commentLikeCountTv, final ImageView commentLikeIv, final OnVoteListener listener) {
CheckLoginUtils.checkLogin(context, new CheckLoginUtils.OnLoggenInListener() {
@Override
public void onLoggedIn() {
if (commentLikeCountTv.getCurrentTextColor() == ContextCompat.getColor(context, R.color.theme)) {
Utils.toast(context, "已经点过赞啦!");
return;
}
commentEntity.setVote(commentEntity.getVote() + 1);
commentLikeCountTv.setTextColor(ContextCompat.getColor(context, R.color.theme));
commentLikeIv.setImageResource(R.drawable.ic_like_select);
commentLikeCountTv.setText(String.valueOf(commentEntity.getVote()));
commentLikeCountTv.setVisibility(View.VISIBLE);
PostCommentUtils.addCommentVoto(context, commentEntity.getId(),
new PostCommentUtils.PostCommentListener() {
@Override
public void postSuccess(JSONObject response) {
if (listener != null) {
listener.onVote();
}
}
@Override
public void postFailed(Throwable e) {
commentEntity.setVote(commentEntity.getVote() - 1);
commentLikeCountTv.setTextColor(ContextCompat.getColor(context, R.color.hint));
commentLikeIv.setImageResource(R.drawable.ic_like_unselect);
commentLikeCountTv.setText(String.valueOf(commentEntity.getVote()));
if (commentEntity.getVote() == 0) {
commentLikeCountTv.setVisibility(View.GONE);
} else {
commentLikeCountTv.setVisibility(View.VISIBLE);
}
if (e instanceof HttpException) {
HttpException exception = (HttpException) e;
if (exception.code() == 403) {
try {
String detail = new JSONObject(exception.response().errorBody().string()).getString("detail");
if ("voted".equals(detail)) {
Utils.toast(context, "已经点过赞啦!");
}
} catch (Exception ex) {
ex.printStackTrace();
}
return;
}
}
Utils.toast(context, "网络异常,点赞失败");
}
});
}
});
}
// 设置评论item 用户相关的view(点赞/头像/用户名)
public static void setCommentUserView(Context mContext, CommentViewHolder holder, CommentEntity entity) {
UserDataEntity userDataEntity = entity.getUserData();
holder.commentLikeCountTv.setTextColor(ContextCompat.getColor(mContext, R.color.hint));
holder.commentLikeIv.setImageResource(R.drawable.ic_like_unselect);
if (entity.getVote() == 0) {
holder.commentLikeCountTv.setVisibility(View.GONE);
} else { // 检查是否已点赞
if (userDataEntity != null && userDataEntity.isCommentVoted()) {
holder.commentLikeCountTv.setTextColor(ContextCompat.getColor(mContext, R.color.theme));
holder.commentLikeIv.setImageResource(R.drawable.ic_like_select);
}
holder.commentLikeCountTv.setVisibility(View.VISIBLE);
holder.commentLikeCountTv.setText(String.valueOf(entity.getVote()));
}
//检查是否是自身评论
UserInfoEntity userInfo = LoginUtils.getUserInfo(mContext);
if (userDataEntity != null && userDataEntity.isCommentOwn() && userInfo != null) {
holder.commentUserNameTv.setText(userInfo.getName());
ImageUtils.Companion.display(holder.commentUserIconDv, userInfo.getIcon());
} else {
holder.commentUserNameTv.setText(entity.getUser().getName());
if (TextUtils.isEmpty(entity.getUser().getIcon())) {
ImageUtils.Companion.display(holder.commentUserIconDv, R.drawable.user_default_icon_comment);
} else {
ImageUtils.Companion.display(holder.commentUserIconDv, entity.getUser().getIcon());
}
}
}
public interface OnVoteListener {
void onVote();
}
}

View File

@ -14,9 +14,8 @@ import java.util.List;
/**
* Created by khy on 2016/11/8.
*
* <p>
* 初始化资讯关注-内容图片
*
**/
public class ConcernContentUtils {
@ -26,75 +25,82 @@ public class ConcernContentUtils {
int index = 0;
for (int i = 0, size = (int) Math.ceil(list.size() / 3.0f); i < size; i++) {
int type = count % 3;
if (type == 0) {
LinearLayout ll = new LinearLayout(context);
ll.setOrientation(LinearLayout.HORIZONTAL);
for (int j = 0; j < 3; j++) {
ll.addView(getImageView(context, list, entrance, index, width, 0));
LinearLayout ll;
switch (type) {
case 0:
ll = new LinearLayout(context);
ll.setOrientation(LinearLayout.HORIZONTAL);
for (int j = 0; j < 3; j++) {
ll.addView(getImageView(context, list, entrance, index, width, 0));
index += 1;
}
linearLayout.addView(ll);
count -= 3;
break;
case 1:
linearLayout.addView(getImageView(context, list, entrance, index, width, 1));
count -= 1;
index += 1;
}
linearLayout.addView(ll);
count -= 3;
} else if (type == 1) {
linearLayout.addView(getImageView(context, list, entrance, index, width, 1));
count -= 1;
index += 1;
} else if (type == 2) {
LinearLayout ll = new LinearLayout(context);
ll.setOrientation(LinearLayout.HORIZONTAL);
for (int j = 0; j < 2; j++) {
ll.addView(getImageView(context, list, entrance, index, width, 2));
index += 1;
}
linearLayout.addView(ll);
count -= 2;
break;
case 2:
ll = new LinearLayout(context);
ll.setOrientation(LinearLayout.HORIZONTAL);
for (int j = 0; j < 2; j++) {
ll.addView(getImageView(context, list, entrance, index, width, 2));
index += 1;
}
linearLayout.addView(ll);
count -= 2;
break;
}
}
}
private static SimpleDraweeView getImageView(final Context context, final List<String> list, final String entrance,
final int position, int width, int type) {
private static SimpleDraweeView getImageView(final Context context, final List<String> list, final String entrance,
final int position, int width, int type) {
SimpleDraweeView imageView;
if (type == 0) {
imageView = new SimpleDraweeView(context);
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
0, width / 3 - DisplayUtils.dip2px(context, 4));
lparams.setMargins(DisplayUtils.dip2px(context, 2), 0,
DisplayUtils.dip2px(context, 2), DisplayUtils.dip2px(context, 4));
lparams.weight = 1;
imageView.setLayoutParams(lparams);
ImageUtils.getInstance().display(context.getResources(), imageView,
ScalingUtils.ScaleType.CENTER_CROP, list.get(position));
} else if (type == 1) {
imageView = new SimpleDraweeView(context);
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(width, width / 2);
lparams.setMargins(DisplayUtils.dip2px(context, 2), 0,
DisplayUtils.dip2px(context, 2), DisplayUtils.dip2px(context, 4));
imageView.setLayoutParams(lparams);
ImageUtils.getInstance().display(context.getResources(), imageView,
ScalingUtils.ScaleType.CENTER_CROP, list.get(position));
} else {
imageView = new SimpleDraweeView(context);
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
0, width / 2 - DisplayUtils.dip2px(context, 4));
lparams.setMargins(DisplayUtils.dip2px(context, 2), 0,
DisplayUtils.dip2px(context, 2), DisplayUtils.dip2px(context, 4));
lparams.weight = 1;
imageView.setLayoutParams(lparams);
ImageUtils.getInstance().display(context.getResources(), imageView,
ScalingUtils.ScaleType.CENTER_CROP, list.get(position));
LinearLayout.LayoutParams lparams;
switch (type) {
case 0:
imageView = new SimpleDraweeView(context);
lparams = new LinearLayout.LayoutParams(
0, width / 3 - DisplayUtils.dip2px(context, 4));
lparams.setMargins(DisplayUtils.dip2px(context, 2), 0,
DisplayUtils.dip2px(context, 2), DisplayUtils.dip2px(context, 4));
lparams.weight = 1;
imageView.setLayoutParams(lparams);
ImageUtils.Companion.getInstance().display(context.getResources(), imageView,
ScalingUtils.ScaleType.CENTER_CROP, list.get(position));
break;
case 1:
imageView = new SimpleDraweeView(context);
lparams = new LinearLayout.LayoutParams(width, width / 2);
lparams.setMargins(DisplayUtils.dip2px(context, 2), 0,
DisplayUtils.dip2px(context, 2), DisplayUtils.dip2px(context, 4));
imageView.setLayoutParams(lparams);
ImageUtils.Companion.getInstance().display(context.getResources(), imageView,
ScalingUtils.ScaleType.CENTER_CROP, list.get(position));
break;
default:
imageView = new SimpleDraweeView(context);
lparams = new LinearLayout.LayoutParams(
0, width / 2 - DisplayUtils.dip2px(context, 4));
lparams.setMargins(DisplayUtils.dip2px(context, 2), 0,
DisplayUtils.dip2px(context, 2), DisplayUtils.dip2px(context, 4));
lparams.weight = 1;
imageView.setLayoutParams(lparams);
ImageUtils.Companion.getInstance().display(context.getResources(), imageView,
ScalingUtils.ScaleType.CENTER_CROP, list.get(position));
break;
}
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent checkIntent = new Intent(context, ViewImageActivity.class);
checkIntent.putExtra("urls", (ArrayList<String>) list);
checkIntent.putExtra("current", position);
checkIntent.putExtra("ScaleType", "FIT_CENTER");
checkIntent.putExtra("entrance", entrance);
Intent checkIntent = ViewImageActivity.getViewImageIntent(context, (ArrayList<String>) list, position, entrance);
context.startActivity(checkIntent);
}
});
return imageView;
}
}

View File

@ -1,81 +0,0 @@
package com.gh.common.util;
import android.content.Context;
import com.gh.gamecenter.eventbus.EBReuse;
import com.gh.gamecenter.retrofit.Response;
import com.gh.gamecenter.retrofit.RetrofitManager;
import org.json.JSONArray;
import de.greenrobot.event.EventBus;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.adapter.rxjava.HttpException;
import rx.Observable;
import rx.functions.Func1;
import rx.schedulers.Schedulers;
/**
* Created by khy on 2016/8/24.
* croncern 工具类
*/
public class ConcernUtils {
public static void postConcernGameId(final Context context, final String gameId) {
TokenUtils.getToken(context, true)
.flatMap(new Func1<String, Observable<ResponseBody>>() {
@Override
public Observable<ResponseBody> call(String token) {
JSONArray params = new JSONArray();
params.put(gameId);
RequestBody body = RequestBody.create(MediaType.parse("application/json"), params.toString());
return RetrofitManager.getUser().postConcern(token, body);
}
}).subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(new Response<ResponseBody>());
}
public static void deleteConcernData(final Context context, final String gameId) {
TokenUtils.getToken(context, true)
.flatMap(new Func1<String, Observable<ResponseBody>>() {
@Override
public Observable<ResponseBody> call(String token) {
return RetrofitManager.getUser().deleteConcern(token, gameId);
}
}).subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(new Response<ResponseBody>());
}
public static void updateConcernData(final Context context, final JSONArray data) {
TokenUtils.getToken(context, true)
.flatMap(new Func1<String, Observable<ResponseBody>>() {
@Override
public Observable<ResponseBody> call(String token) {
RequestBody body = RequestBody.create(MediaType.parse("application/json"),
data.toString());
return RetrofitManager.getUser().putConcern(token, body);
}
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(new Response<ResponseBody>(){
@Override
public void onResponse(ResponseBody response) {
super.onResponse(response);
EventBus.getDefault().post(new EBReuse("UpdateConcernSuccess"));
}
@Override
public void onFailure(HttpException e) {
super.onFailure(e);
EventBus.getDefault().post(new EBReuse("UpdateConcernFailure"));
}
});
}
}

View File

@ -0,0 +1,85 @@
package com.gh.common.util
import android.content.Context
import com.gh.gamecenter.eventbus.EBConcernChanged
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import okhttp3.MediaType
import okhttp3.RequestBody
import okhttp3.ResponseBody
import org.greenrobot.eventbus.EventBus
import org.json.JSONArray
import retrofit2.HttpException
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
/**
* Created by khy on 2016/8/24.
* croncern 工具类
*/
object ConcernUtils {
fun postConcernGameId(context: Context, gameId: String, listener: onConcernListener?) {
val params = JSONArray()
params.put(gameId)
val body = RequestBody.create(MediaType.parse("application/json"), params.toString())
RetrofitManager.getInstance(context).getApi()
.postConcern(body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>(){
override fun onResponse(response: ResponseBody?) {
super.onResponse(response)
listener?.onSuccess()
EventBus.getDefault().post(EBConcernChanged(gameId, true))
}
override fun onFailure(e: HttpException?) {
super.onFailure(e)
listener?.onError()
}
})
}
fun deleteConcernData(context: Context, gameId: String, listener: onConcernListener?) {
RetrofitManager.getInstance(context).getApi()
.deleteConcern(gameId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>(){
override fun onResponse(response: ResponseBody?) {
super.onResponse(response)
listener?.onSuccess()
EventBus.getDefault().post(EBConcernChanged(gameId, false))
}
override fun onFailure(e: HttpException?) {
super.onFailure(e)
listener?.onError()
}
})
}
fun updateConcernData(context: Context, data: JSONArray) {
val body = RequestBody.create(MediaType.parse("application/json"),
data.toString())
RetrofitManager.getInstance(context).getApi()
.putConcern(body)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(object : Response<ResponseBody>() {
override fun onResponse(response: ResponseBody) {
super.onResponse(response)
EventBus.getDefault().post(EBConcernChanged())
}
})
}
interface onConcernListener {
fun onSuccess()
fun onError()
}
}

View File

@ -3,7 +3,7 @@ package com.gh.common.util;
import android.content.Context;
import android.os.Build;
import com.gh.download.DownloadEntity;
import com.lightgame.download.DownloadEntity;
import com.gh.gamecenter.db.info.ConcernInfo;
import com.gh.gamecenter.entity.GameEntity;
import com.gh.gamecenter.entity.NewsDetailEntity;
@ -11,150 +11,193 @@ import com.gh.gamecenter.manager.ConcernManager;
import com.gh.gamecenter.manager.DataCollectionManager;
import com.gh.gamecenter.manager.PackageManager;
import org.json.JSONArray;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* Created by LGT on 2016/12/9.
* 数据收集 工具类data.ghzhushou.com
* 数据收集 工具类data.ghzs666.com
*/
public class DataCollectionUtils {
// 上传助手奔溃数据
public static void uploadError(Context context, String msg) {
Map<String, Object> map = new HashMap<>();
map.put("content", msg);
map.put("type", android.os.Build.MODEL);
map.put("system", android.os.Build.VERSION.SDK_INT + "=" + android.os.Build.VERSION.RELEASE);
// WIFI实时
DataCollectionManager.onEvent(context, "error", map, NetworkUtils.isWifiConnected(context));
}
// 上传助手奔溃数据
public static void uploadError(Context context, String msg) {
Map<String, Object> map = new HashMap<>();
map.put("content", msg);
map.put("type", android.os.Build.MODEL);
map.put("system", android.os.Build.VERSION.SDK_INT + "=" + android.os.Build.VERSION.RELEASE);
// WIFI实时
DataCollectionManager.onEvent(context, "error", map, NetworkUtils.isWifiConnected(context));
}
// 上传下载数据(开始、完成)
public static void uploadDownload(Context context, DownloadEntity downloadEntity, String status) {
Map<String, Object> map = new HashMap<>();
map.put("game", downloadEntity.getName());
map.put("game_id", downloadEntity.getGameId());
if (downloadEntity.isPluggable()) {
map.put("method", "插件化");
map.put("btn_status", "插件化");
} else if (downloadEntity.isUpdate()) {
map.put("method", "更新");
map.put("btn_status", "更新");
} else {
map.put("method", "正常");
map.put("btn_status", "下载");
}
map.put("platform", PlatformUtils.getInstance(context).getPlatformName(downloadEntity.getPlatform()));
map.put("status", status);
map.put("location", downloadEntity.getLocation());
map.put("entrance", downloadEntity.getEntrance());
map.put("installed", downloadEntity.getInstalled());
map.put("network", NetworkUtils.getConnectedType(context));
DataCollectionManager.onEvent(context, "download", map);
}
// 上传下载数据(开始、完成)
public static void uploadDownload(Context context, DownloadEntity downloadEntity, String status) {
Map<String, Object> map = new HashMap<>();
map.put("game", downloadEntity.getName());
map.put("game_id", downloadEntity.getGameId());
if (downloadEntity.isPluggable()) {
map.put("method", "插件化");
map.put("btn_status", "插件化");
} else if (downloadEntity.isUpdate()) {
map.put("method", "更新");
map.put("btn_status", "更新");
} else {
map.put("method", "正常");
map.put("btn_status", "下载");
}
map.put("platform", PlatformUtils.getInstance(context).getPlatformName(downloadEntity.getPlatform()));
map.put("status", status);
map.put("location", downloadEntity.getLocation());
map.put(EntranceUtils.KEY_ENTRANCE, downloadEntity.getEntrance());
map.put("installed", downloadEntity.getInstalled());
map.put("network", NetworkUtils.getConnectedType(context));
DataCollectionManager.onEvent(context, "download", map);
}
// 上传点击数据
public static void uploadClick(Context context, String... args) {
if (args.length < 2) {
return;
}
Map<String, Object> map = new HashMap<>();
map.put("location", args[0]);
map.put("page", args[1]);
if (args.length == 3) {
map.put("title", args[2]);
}
DataCollectionManager.onEvent(context, "click-item", map);
}
// 上传点击数据
public static void uploadClick(Context context, String... args) {
if (args.length < 2) {
return;
}
Map<String, Object> map = new HashMap<>();
map.put("location", args[0]);
map.put("page", args[1]);
if (args.length == 3) {
map.put("title", args[2]);
}
DataCollectionManager.onEvent(context, "click-item", map);
}
// 上传游戏数据
public static void uploadGame(Context context, GameEntity gameEntity, int seconds, String from) {
Map<String, Object> map = new HashMap<>();
map.put("game", gameEntity.getName());
map.put("game_id", gameEntity.getId());
map.put("time", seconds);
map.put("from", from);
DataCollectionManager.onEvent(context, "game", map);
}
// 上传游戏数据
public static void uploadGame(Context context, GameEntity gameEntity, int seconds, String from) {
Map<String, Object> map = new HashMap<>();
map.put("game", gameEntity.getName());
map.put("game_id", gameEntity.getId());
map.put("time", seconds);
map.put("from", from);
DataCollectionManager.onEvent(context, "game", map);
}
// 上传新闻数据
public static void uploadNews(Context context, NewsDetailEntity newsDetailEntity,
GameEntity gameEntity, int seconds, String from) {
Map<String, Object> map = new HashMap<>();
map.put("title", newsDetailEntity.getTitle());
map.put("type", newsDetailEntity.getType());
map.put("author", newsDetailEntity.getAuthor());
if (gameEntity != null) {
map.put("game", gameEntity.getName());
map.put("game_id", gameEntity.getId());
}
map.put("time", seconds);
map.put("from", from);
DataCollectionManager.onEvent(context, "news", map);
}
// 上传新闻数据
public static void uploadNews(Context context, NewsDetailEntity newsDetailEntity,
GameEntity gameEntity, int seconds, String from) {
Map<String, Object> map = new HashMap<>();
map.put("title", newsDetailEntity.getTitle());
map.put("type", newsDetailEntity.getType());
map.put("author", newsDetailEntity.getAuthor());
if (gameEntity != null) {
map.put("game", gameEntity.getName());
map.put("game_id", gameEntity.getId());
}
map.put("time", seconds);
map.put("from", from);
DataCollectionManager.onEvent(context, "news", map);
}
// 上传安装或卸载数据
public static void uploadInorunstall(Context context, String type, String packageName) {
Map<String, Object> map = new HashMap<>();
map.put("type", type);
map.put("packageName", packageName);
DataCollectionManager.onEvent(context, "inorunstall", map);
}
// 上传安装或卸载数据
public static void uploadInorunstall(Context context, String type, String packageName) {
Map<String, Object> map = new HashMap<>();
map.put("type", type);
map.put("packageName", packageName);
DataCollectionManager.onEvent(context, "inorunstall", map);
}
// 上传劫持数据
public static void uploadHijack(Context context, DownloadEntity downloadEntity) {
Map<String, Object> map = new HashMap<>();
map.put("url", downloadEntity.getUrl());
map.put("game", downloadEntity.getName());
map.put("game_id", downloadEntity.getGameId());
map.put("platform", downloadEntity.getPlatform());
map.put("hijack_url", downloadEntity.getError());
DataCollectionManager.onEvent(context, "hijack", map);
}
// 上传劫持数据
public static void uploadHijack(Context context, DownloadEntity downloadEntity) {
Map<String, Object> map = new HashMap<>();
map.put("url", downloadEntity.getUrl());
map.put("game", downloadEntity.getName());
map.put("game_id", downloadEntity.getGameId());
map.put("platform", downloadEntity.getPlatform());
map.put("hijack_url", downloadEntity.getError());
DataCollectionManager.onEvent(context, "hijack", map);
}
// 上传用户数据
public static void uploadUser(Context context) {
ConcernManager concernManager = new ConcernManager(context);
ArrayList<String> concernList = new ArrayList<>();
for (ConcernInfo entity : concernManager.getAllConcern()) {
if (entity.isConcern()) {
concernList.add(entity.getGameName());
}
}
Map<String, Object> map = new HashMap<>();
map.put("type", Build.MODEL);
map.put("system", Build.VERSION.SDK_INT + "=" + Build.VERSION.RELEASE);
map.put("install", PackageManager.getInstalledList());
map.put("concern", concernList);
DataCollectionManager.upsert(context, "user", map);
}
// 上传用户数据
public static void uploadUser(Context context) {
ConcernManager concernManager = new ConcernManager(context);
ArrayList<String> concernList = new ArrayList<>();
for (ConcernInfo entity : concernManager.getAllConcern()) {
if (entity.isConcern()) {
concernList.add(entity.getGameName());
}
}
Map<String, Object> map = new HashMap<>();
map.put("type", Build.MODEL);
map.put("system", Build.VERSION.SDK_INT + "=" + Build.VERSION.RELEASE);
map.put("install", PackageManager.getInstalledList());
map.put("concern", concernList);
DataCollectionManager.upsert(context, "user", map);
}
// 上传搜索数据
public static void uploadSearch(Context context, String... args) {
if (args.length < 2) {
return;
}
Map<String, Object> map = new HashMap<>();
map.put("key", args[0]);
map.put("from", args[1]);
if (args.length == 3) {
map.put("click", args[2]);
}
DataCollectionManager.onEvent(context, "search", map);
}
// 上传搜索数据
public static void uploadSearch(Context context, String... args) {
if (args.length != 5) {
return;
}
Map<String, Object> map = new HashMap<>();
map.put("from", args[2]);
map.put("search_type", args[1]);
map.put("key", args[0]);
map.put("game_id", args[3]);
map.put("game_name", args[4]);
map.put("netword", NetworkUtils.getConnectedType(context));
map.put("type", "search");
map.put("device_type", android.os.Build.MODEL);
map.put("device_system", android.os.Build.VERSION.SDK_INT + "=" + android.os.Build.VERSION.RELEASE);
DataCollectionManager.onEvent(context, "search", map);
}
// 上传关注数据
public static void uploadConcern(Context context, String... args) {
if (args.length < 3) {
return;
}
Map<String, Object> map = new HashMap<>();
map.put("game", args[0]);
map.put("game_id", args[1]);
map.put("type", args[2]);
DataCollectionManager.onEvent(context, "concern", map);
}
// 上传搜索点击数据
public static void uploadSearchClick(Context context, String... args) {
if (args.length != 5) {
return;
}
Map<String, Object> map = new HashMap<>();
map.put("from", args[2]);
map.put("search_type", args[1]);
map.put("key", args[0]);
map.put("game_id", args[3]);
map.put("game_name", args[4]);
map.put("netword", NetworkUtils.getConnectedType(context));
map.put("type", "click");
map.put("device_type", android.os.Build.MODEL);
map.put("device_system", android.os.Build.VERSION.SDK_INT + "=" + android.os.Build.VERSION.RELEASE);
DataCollectionManager.onEvent(context, "search", map);
}
// 上传关注数据
public static void uploadConcern(Context context, String... args) {
if (args.length < 3) {
return;
}
Map<String, Object> map = new HashMap<>();
map.put("game", args[0]);
map.put("game_id", args[1]);
map.put("type", args[2]);
DataCollectionManager.onEvent(context, "concern", map);
}
//上传推荐位数据
public static void uploadPosition(Context context, String... args) {
if (args.length < 3) {
return;
}
Map<String, Object> map = new HashMap<>();
map.put("page", args[0]);
map.put("location", args[1]);
map.put("name", args[2]);
DataCollectionManager.onEvent(context, "position", map);
}
//上传应用列表
public static void uploadAppList(Context context, JSONArray applist) {
Map<String, Object> map = new HashMap<>();
map.put("applist", applist);
DataCollectionManager.onEvent(context, "applist", map);
}
}

View File

@ -2,9 +2,11 @@ package com.gh.common.util;
import android.content.Context;
import com.gh.download.DownloadEntity;
import com.halo.assistant.HaloApp;
import com.lightgame.download.DownloadEntity;
import com.gh.gamecenter.retrofit.Response;
import com.gh.gamecenter.retrofit.RetrofitManager;
import com.lightgame.utils.Utils;
import org.json.JSONObject;
@ -23,67 +25,67 @@ import rx.schedulers.Schedulers;
*/
public class DataLogUtils {
// 上传日志
public static void uploadLog(Context context, String topic, Map<String, Object> map) {
String version = PackageUtils.getVersionName(context);
String user = Installation.getUUID(context);
String channel = (String) PackageUtils.getMetaData(context, context.getPackageName(), "TD_CHANNEL_ID");
map.put("version", version);
map.put("user", user);
map.put("device_id", TokenUtils.getDeviceId(context));
map.put("channel", channel);
// 轮播图
public static void uploadLunbotuLog(Context context, String type, String title, String location) {
Map<String, Object> map = new HashMap<>();
map.put("location", location);
map.put("type", type);
map.put("title", title);
map.put("form", "click");
uploadLog(context, "lunbotu", map);
}
Map<String, String> params = new HashMap<>();
params.put("topic", topic);
params.put("source", "GH-ASSIST-Client");
params.put("time", String.valueOf(Utils.getTime(context)));
params.put("content", new JSONObject(map).toString());
// 上传日志
public static void uploadLog(Context context, String topic, Map<String, Object> map) {
String version = PackageUtils.getPatchVersionName();
String user = Installation.getUUID(context);
String channel = HaloApp.getInstance().getChannel();
map.put("version", version);
map.put("user", user);
map.put("device_id", TokenUtils.getDeviceId(context));
map.put("channel", channel);
RequestBody body = RequestBody.create(MediaType.parse("application/json"),
new JSONObject(params).toString());
RetrofitManager.getData().postLog(body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Response<ResponseBody>());
}
Map<String, String> params = new HashMap<>();
params.put("topic", topic);
params.put("source", "GH-ASSIST-Client");
params.put("time", String.valueOf(Utils.getTime(context)));
params.put("content", new JSONObject(map).toString());
// 轮播图
public static void uploadLunbotuLog(Context context, String type, String title, String location) {
Map<String, Object> map = new HashMap<>();
map.put("location", location);
map.put("type", type);
map.put("title", title);
map.put("form", "click");
uploadLog(context, "lunbotu", map);
}
RequestBody body = RequestBody.create(MediaType.parse("application/json"),
new JSONObject(params).toString());
RetrofitManager.getInstance(context).getData().postLog(body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Response<ResponseBody>());
}
// 网络错误
public static void uploadNeterrorLog(Context context, DownloadEntity downloadEntity) {
Map<String, Object> map = new HashMap<>();
map.put("url", downloadEntity.getUrl());
map.put("game", downloadEntity.getName());
map.put("game_id", downloadEntity.getGameId());
map.put("platform", downloadEntity.getPlatform());
map.put("error", downloadEntity.getError());
uploadLog(context, "neterror", map);
}
// 网络错误
public static void uploadNeterrorLog(Context context, DownloadEntity downloadEntity) {
Map<String, Object> map = new HashMap<>();
map.put("url", downloadEntity.getUrl());
map.put("game", downloadEntity.getName());
map.put("game_id", downloadEntity.getGameId());
map.put("platform", downloadEntity.getPlatform());
map.put("error", downloadEntity.getError());
uploadLog(context, "neterror", map);
}
// 助手更新
public static void uploadUpgradeLog(Context context, String step) {
Map<String, Object> map = new HashMap<>();
map.put("step", step);
uploadLog(context, "upgrade", map);
}
// 助手更新
public static void uploadUpgradeLog(Context context, String step) {
Map<String, Object> map = new HashMap<>();
map.put("step", step);
uploadLog(context, "upgrade", map);
}
// 链接劫持
public static void uploadHijack(Context context, DownloadEntity downloadEntity) {
Map<String, Object> map = new HashMap<>();
map.put("url", downloadEntity.getUrl());
map.put("game", downloadEntity.getName());
map.put("game_id", downloadEntity.getGameId());
map.put("platform", downloadEntity.getPlatform());
map.put("hijack_url", downloadEntity.getError());
uploadLog(context, "hijack", map);
}
// 链接劫持
public static void uploadHijack(Context context, DownloadEntity downloadEntity) {
Map<String, Object> map = new HashMap<>();
map.put("url", downloadEntity.getUrl());
map.put("game", downloadEntity.getName());
map.put("game_id", downloadEntity.getGameId());
map.put("platform", downloadEntity.getPlatform());
map.put("hijack_url", downloadEntity.getError());
uploadLog(context, "hijack", map);
}
}

View File

@ -4,10 +4,17 @@ import android.app.Activity;
import android.app.Application;
import android.content.Context;
import com.gh.common.constant.Config;
import com.halo.assistant.TinkerApp;
import com.tencent.bugly.beta.tinker.TinkerManager;
import com.tencent.bugly.crashreport.CrashReport;
import com.tencent.mta.track.StatisticsDataAPI;
import com.tencent.stat.MtaSDkException;
import com.tencent.stat.StatConfig;
import com.tencent.stat.StatCrashReporter;
import com.tencent.stat.StatReportStrategy;
import com.tencent.stat.StatService;
import com.tencent.tinker.lib.tinker.Tinker;
import com.tendcloud.tenddata.TCAgent;
import java.util.HashMap;
@ -20,29 +27,85 @@ import java.util.Properties;
*/
public class DataUtils {
public static void init(Application application) {
/**
* 初始化各种统计工具仅在release build非debug模式启用统计
*
* @param context
* @param debug 是否debug模式
* @param channel
*/
public static void init(final Application context, final boolean debug, String channel) {
//TalkingData
//dubug true release false
TCAgent.LOG_ON = true;
TCAgent.init(application);
TCAgent.setReportUncaughtExceptions(true);
try {
TCAgent.LOG_ON = debug;
TCAgent.init(context, Config.TALKINGDATA_APPID, channel);
/**
*
* 不要启用!!!!不要启用,全部由{@link com.gh.base.AppUncaughtHandler}处理
*/
TCAgent.setReportUncaughtExceptions(false);
} catch (Exception e) {
e.printStackTrace();
}
//MTA
// 打开debug开关可查看mta上报日志或错误
// dubug true release false
StatConfig.setDebugEnable(true);
// 收集未处理的异常
StatConfig.setAutoExceptionCaught(true);
// 设置数据上报策略
StatConfig.setStatSendStrategy(StatReportStrategy.PERIOD);
StatConfig.setSendPeriodMinutes(5);
// 开启收集服务
String TA_APPKEY = (String) PackageUtils.getMetaData(application, application.getPackageName(), "TA_APPKEY");
try {
StatService.startStatService(application, TA_APPKEY, com.tencent.stat.common.StatConstants.VERSION);
/**
*
* 不要启用!!!!全部由{@link com.gh.base.AppUncaughtHandler}处理
*/
StatConfig.setAutoExceptionCaught(false);
StatCrashReporter crashReporter = StatCrashReporter.getStatCrashReporter(context);
crashReporter.setJavaCrashHandlerStatus(false);
// crashReporter.setEnableInstantReporting(true);
StatConfig.setDebugEnable(debug);
// 设置数据上报策略
if (debug) {
StatConfig.setStatSendStrategy(StatReportStrategy.INSTANT);
} else {
StatConfig.setStatSendStrategy(StatReportStrategy.PERIOD);
StatConfig.setSendPeriodMinutes(5);
}
// 设置启用Tlink
StatConfig.setTLinkStatus(true);
// 设置启用可视化埋点
StatisticsDataAPI.instance(context);
StatConfig.init(context);
StatConfig.setInstallChannel(channel);
StatConfig.setAntoActivityLifecycleStat(true);
StatConfig.setAppVersion(PackageUtils.getPatchVersionName());
StatService.setContext(context);
StatService.registerActivityLifecycleCallbacks(context);
// 开启收集服务
StatService.startStatService(context, Config.MTA_APPKEY, com.tencent.stat.common.StatConstants.VERSION);
} catch (MtaSDkException e) {
e.printStackTrace();
}
// init bugly
try {
CrashReport.setIsDevelopmentDevice(context, debug);
CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(context);
strategy.setEnableANRCrashMonitor(false);
strategy.setEnableNativeCrashMonitor(false);
strategy.setAppChannel(channel);
strategy.setAppVersion(PackageUtils.getPatchVersionName());
CrashReport.initCrashReport(context, Config.BUGLY_APPID, debug, strategy);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void onEvent(Context var0, String var1, String var2) {
@ -50,6 +113,24 @@ public class DataUtils {
StatService.trackCustomEvent(var0, var1, var2);
}
public static void onPause(Activity var0) {
TCAgent.onPageEnd(var0, var0.getClass().getSimpleName());
StatService.onPause(var0);
}
public static void onResume(Activity var0) {
TCAgent.onPageStart(var0, var0.getClass().getSimpleName());
StatService.onResume(var0);
}
// 游戏启动
public static void onGameLaunchEvent(Context context, String gameName, String platform, String page) {
Map<String, Object> kv = new HashMap<>();
kv.put("版本", platform);
kv.put("页面", page);
onEvent(context, "游戏启动", gameName, kv);
}
public static void onEvent(Context var0, String var1, String var2, Map<String, Object> var3) {
TCAgent.onEvent(var0, var1, var2, var3);
Properties prop = new Properties();
@ -60,41 +141,23 @@ public class DataUtils {
StatService.trackCustomKVEvent(var0, var1, prop);
}
public static void onPause(Activity var0) {
TCAgent.onPause(var0);
StatService.onPause(var0);
}
public static void onResume(Activity var0) {
TCAgent.onResume(var0);
StatService.onResume(var0);
}
// 游戏启动
public static void onGameLaunchEvent(Context context, String gameName, String platform, String page) {
Map<String, Object> kv = new HashMap<>();
kv.put("版本", platform);
kv.put("页面", page);
onEvent(context, "游戏启动", gameName, kv);
}
// 游戏下载
public static void onGameDownloadEvent(Context context, String gameName, String platform, String entrance, String status) {
Map<String, Object> kv = new HashMap<>();
kv.put("版本", platform);
kv.put("状态", status);
DataUtils.onEvent(context, "游戏下载", gameName, kv);
onEvent(context, "游戏下载", gameName, kv);
Map<String, Object> kv2 = new HashMap<>();
kv2.put("版本", platform);
kv2.put("状态", status);
kv2.put("位置", entrance);
DataUtils.onEvent(context, "游戏下载位置", gameName, kv2);
onEvent(context, "游戏下载位置", gameName, kv2);
Map<String, Object> kv3 = new HashMap<>();
kv3.put(entrance, "下载数");
kv3.put(entrance, status);
DataUtils.onEvent(context, "应用数据", gameName, kv3);
onEvent(context, "应用数据", gameName, kv3);
}
// 游戏更新
@ -102,7 +165,7 @@ public class DataUtils {
Map<String, Object> kv = new HashMap<>();
kv.put("版本", paltform);
kv.put("状态", status);
DataUtils.onEvent(context, "游戏更新", gameName, kv);
onEvent(context, "游戏更新", gameName, kv);
}
}

View File

@ -0,0 +1,259 @@
package com.gh.common.util;
import android.content.Intent;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.view.View;
import com.gh.common.constant.Config;
import com.gh.common.view.DownloadDialog;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.DownloadManagerActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.adapter.viewholder.DetailViewHolder;
import com.gh.gamecenter.entity.ApkEntity;
import com.gh.gamecenter.entity.GameEntity;
import com.gh.gamecenter.manager.PackageManager;
import com.lightgame.download.DownloadEntity;
import com.lightgame.download.FileUtils;
import com.lightgame.utils.Utils;
/**
* Created by khy on 27/06/17.
* 详情下载工具类
*/
public class DetailDownloadUtils {
public static void detailInitDownload(DetailViewHolder viewHolder, boolean isCheck) {
if (Config.isShow(viewHolder.context)) {
viewHolder.downloadBottom.setVisibility(View.VISIBLE);
} else {
viewHolder.downloadBottom.setVisibility(View.GONE);
}
if (viewHolder.gameEntity != null && "光环助手".equals(viewHolder.gameEntity.getName())) {
viewHolder.downloadBottom.setVisibility(View.GONE);
} else if (viewHolder.gameEntity == null || viewHolder.gameEntity.getApk().isEmpty()) {
viewHolder.downloadTv.setVisibility(View.VISIBLE);
viewHolder.downloadPb.setVisibility(View.GONE);
viewHolder.downloadPer.setVisibility(View.GONE);
if (TextUtils.isEmpty(viewHolder.downloadOffText)) {
viewHolder.downloadTv.setText("暂无下载");
} else {
viewHolder.downloadTv.setText(viewHolder.downloadOffText);
}
viewHolder.downloadTv.setBackgroundResource(R.drawable.game_item_btn_pause_style);
viewHolder.downloadTv.setTextColor(0xFF999999);
viewHolder.downloadTv.setClickable(false);
} else {
viewHolder.downloadTv.setVisibility(View.VISIBLE);
viewHolder.downloadPb.setVisibility(View.GONE);
viewHolder.downloadPer.setVisibility(View.GONE);
boolean isInstalled = false;
if (viewHolder.gameEntity.getApk().size() == 1
&& PackageManager.isInstalled(viewHolder.gameEntity.getApk().get(0).getPackageName())) {
isInstalled = true;
}
if (isInstalled) {
if (PackageManager.isCanUpdate(viewHolder.gameEntity.getId(), viewHolder.gameEntity.getApk().get(0).getPackageName())) {
if (viewHolder.isNewsDetail) {
viewHolder.downloadTv.setText(R.string.update);
} else if (TextUtils.isEmpty(viewHolder.downloadAddWord)) {
viewHolder.downloadTv.setText(String.format("更新《%s》",
viewHolder.gameEntity.getName()));
} else {
viewHolder.downloadTv.setText(String.format("更新《%s》%s",
viewHolder.gameEntity.getName(), viewHolder.downloadAddWord));
}
viewHolder.downloadTv.setBackgroundResource(
R.drawable.game_item_btn_download_style);
} else {
if (viewHolder.gameEntity.getTag() != null && viewHolder.gameEntity.getTag().size() != 0
&& !TextUtils.isEmpty(viewHolder.gameEntity.getApk().get(0).getGhVersion())
&& !PackageUtils.isSignature(viewHolder.context, viewHolder.gameEntity.getApk().get(0).getPackageName())) {
if (viewHolder.isNewsDetail) {
viewHolder.downloadTv.setText(R.string.pluggable);
} else if (TextUtils.isEmpty(viewHolder.downloadAddWord)) {
viewHolder.downloadTv.setText(String.format("插件化《%s》",
viewHolder.gameEntity.getName()));
} else {
viewHolder.downloadTv.setText(String.format("插件化《%s》%s",
viewHolder.gameEntity.getName(), viewHolder.downloadAddWord));
}
viewHolder.downloadTv.setBackgroundResource(
R.drawable.game_item_btn_plugin_style);
} else {
if (viewHolder.isNewsDetail) {
viewHolder.downloadTv.setText(R.string.launch);
} else if (TextUtils.isEmpty(viewHolder.downloadAddWord)) {
viewHolder.downloadTv.setText(String.format("启动《%s》",
viewHolder.gameEntity.getName()));
} else {
viewHolder.downloadTv.setText(String.format("启动《%s》%s",
viewHolder.gameEntity.getName(), viewHolder.downloadAddWord));
}
viewHolder.downloadTv.setBackgroundResource(
R.drawable.game_item_btn_launch_style);
}
}
} else {
String status = GameUtils.getDownloadBtnText(viewHolder.context, viewHolder.gameEntity);
switch (status) {
case "插件化":
viewHolder.downloadTv.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
break;
case "打开":
viewHolder.downloadTv.setBackgroundResource(R.drawable.game_item_btn_launch_style);
break;
default:
viewHolder.downloadTv.setBackgroundResource(R.drawable.game_item_btn_download_style);
break;
}
if (viewHolder.isNewsDetail) {
viewHolder.downloadTv.setText(status);
} else if (TextUtils.isEmpty(viewHolder.downloadAddWord)) {
viewHolder.downloadTv.setText(String.format(status + "《%s》",
viewHolder.gameEntity.getName()));
} else {
viewHolder.downloadTv.setText(String.format(status + "《%s》%s",
viewHolder.gameEntity.getName(), viewHolder.downloadAddWord));
}
}
}
if (isCheck && viewHolder.gameEntity != null
&& viewHolder.gameEntity.getApk().size() == 1) {
String url = viewHolder.gameEntity.getApk().get(0).getUrl();
DownloadEntity downloadEntity = DownloadManager.getInstance(viewHolder.context).getDownloadEntityByUrl(url);
if (downloadEntity != null) {
viewHolder.downloadEntity = downloadEntity;
viewHolder.downloadTv.setVisibility(View.GONE);
viewHolder.downloadPb.setVisibility(View.VISIBLE);
viewHolder.downloadPer.setVisibility(View.VISIBLE);
detailInvalidate(viewHolder);
}
}
}
public static void detailInvalidate(DetailViewHolder viewHolder) {
viewHolder.downloadPb.setProgress((int) (viewHolder.downloadEntity.getPercent() * 10));
viewHolder.downloadPer.setTextColor(0xFFFFFFFF);
DownloadEntity downloadEntity = viewHolder.downloadEntity;
switch (downloadEntity.getStatus()) {
case downloading:
case pause:
case timeout:
case neterror:
case waiting:
viewHolder.downloadPer.setText(R.string.downloading);
break;
case done:
viewHolder.downloadPer.setText("安装");
if (downloadEntity.isPluggable()
&& PackageManager.isInstalled(downloadEntity.getPackageName())) {
viewHolder.downloadPb.setProgressDrawable(ContextCompat.getDrawable(viewHolder.context, R.drawable.progressbar_plugin_radius_style));
} else {
viewHolder.downloadPb.setProgressDrawable(ContextCompat.getDrawable(viewHolder.context, R.drawable.progressbar_normal_radius_style));
}
break;
case cancel:
case hijack:
case notfound:
detailInitDownload(viewHolder, false);
break;
default:
break;
}
}
public static class OnDetailDownloadClickListener implements View.OnClickListener {
private DetailViewHolder mViewHolder;
private GameEntity mGameEntity;
private DownloadEntity mDownloadEntity;
private String mEntrance;
private String mName;
private String mTitle;
public OnDetailDownloadClickListener(DetailViewHolder viewHolder, String entrance, String name, String title) {
mViewHolder = viewHolder;
mGameEntity = viewHolder.gameEntity;
mDownloadEntity = viewHolder.downloadEntity;
mEntrance = entrance;
mName = name;
mTitle = title;
}
@Override
public void onClick(View v) {
if (v == mViewHolder.downloadTv) {
if (mGameEntity != null && !mGameEntity.getApk().isEmpty()) {
if (mGameEntity.getApk().size() == 1) {
String str = mViewHolder.downloadTv.getText().toString();
if (str.contains("启动")) {
DataUtils.onGameLaunchEvent(mViewHolder.context, mGameEntity.getName(), mGameEntity.getApk().get(0).getPlatform(), mName);
PackageUtils.launchApplicationByPackageName(mViewHolder.context, mGameEntity.getApk().get(0).getPackageName());
} else if (NetworkUtils.isWifiConnected(mViewHolder.context)) {
download();
} else {
DialogUtils.showDownloadDialog(mViewHolder.context, new DialogUtils.ConfirmListener() {
@Override
public void onConfirm() {
download();
}
});
}
} else {
DownloadDialog.getInstance(mViewHolder.context)
.showPopupWindow(v, mGameEntity, StringUtils.buildString(mEntrance, "+(", mName, "[", mTitle, "])"), mName + ":" + mTitle);
}
} else {
Utils.toast(mViewHolder.context, "稍等片刻~!游戏正在上传中...");
}
} else if (v == mViewHolder.downloadPb || v == mViewHolder.downloadPer) {
String str = mViewHolder.downloadPer.getText().toString();
if ("下载中".equals(str)) {
Intent intent = DownloadManagerActivity.getDownloadMangerIntent(mViewHolder.context,
mGameEntity.getApk().get(0).getUrl(), StringUtils.buildString(mEntrance, "+(", mName, "[", mTitle, "])"));
mViewHolder.context.startActivity(intent);
} else if ("安装".equals(str)) {
if (mDownloadEntity == null) {
mDownloadEntity = DownloadManager.getInstance(mViewHolder.context).getDownloadEntityByUrl(mGameEntity.getApk().get(0).getUrl());
}
if (mDownloadEntity != null) {
PackageUtils.launchSetup(mViewHolder.context, mDownloadEntity.getPath());
}
}
}
}
private void download() {
String str = mViewHolder.downloadTv.getText().toString();
String method;
if (str.contains("更新")) {
method = "更新";
} else if (str.contains("插件化")) {
method = "插件化";
} else {
method = mViewHolder.context.getString(R.string.download);
}
ApkEntity apkEntity = mGameEntity.getApk().get(0);
String msg = FileUtils.isCanDownload(mViewHolder.context, apkEntity.getSize());
if (TextUtils.isEmpty(msg)) {
DataUtils.onGameDownloadEvent(mViewHolder.context, mGameEntity.getName(), apkEntity.getPlatform(), StringUtils.buildString(mEntrance, "+(", mName, "[", mTitle, "])"), "下载开始");
DownloadManager.createDownload(mViewHolder.context, apkEntity, mGameEntity, method, StringUtils.buildString(mEntrance, "+(", mName, "[", mTitle, "])"), mName + ":" + mTitle);
mViewHolder.downloadTv.setVisibility(View.GONE);
mViewHolder.downloadPb.setVisibility(View.VISIBLE);
mViewHolder.downloadPer.setVisibility(View.VISIBLE);
mViewHolder.downloadPb.setProgress(0);
mViewHolder.downloadPer.setText("0.0%");
// DownloadManager.getInstance(mViewHolder.context).putStatus(apkEntity.getUrl(), "downloading");
} else {
Utils.toast(mViewHolder.context, msg);
}
}
}
}

View File

@ -0,0 +1,197 @@
package com.gh.common.util;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.provider.Settings;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import com.gh.gamecenter.kuaichuan.WifiMgr;
import com.lightgame.utils.Util_System_Phone_State;
import com.tencent.stat.StatConfig;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.Reader;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import static android.os.Build.MANUFACTURER;
import static android.os.Build.MODEL;
/**
* Created by khy on 2/08/17.
*/
public class DeviceUtils {
public static JSONObject getLoginDevice(Context context) throws JSONException { // device数据
context = context.getApplicationContext();
JSONObject object = new JSONObject();
object.put("os", "Android");
object.put("imei", Util_System_Phone_State.getDeviceId(context));
object.put("mac", getMac(context));
object.put("model", MODEL);
object.put("manufacturer", MANUFACTURER);
object.put("android_id", Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID));
object.put("android_sdk", Build.VERSION.SDK_INT);
object.put("android_version", android.os.Build.VERSION.RELEASE);
object.put("ip", getIPAddress(context));
object.put("network", getNetwork(context));
return object;
}
public static JSONObject getUserDevice(Context context) { // 判断新老用户device数据
JSONObject object = new JSONObject();
try {
object.put("IMEI", Util_System_Phone_State.getDeviceId(context));
object.put("ANDROID_ID", Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID));
object.put("MAC", getMac(context));
object.put("MTA_ID", StatConfig.getMid(context));
object.put("MANUFACTURER", MANUFACTURER);
object.put("MODEL", MODEL);
object.put("ANDROID_SDK", Build.VERSION.SDK_INT);
object.put("ANDROID_VERSION", android.os.Build.VERSION.RELEASE);
} catch (JSONException e) {
e.printStackTrace();
}
return object;
}
private static String getMac(Context context) {
String str = "";
String macSerial = "";
try {
Process pp = Runtime.getRuntime().exec(
"cat /sys/class/net/wlan0/address ");
InputStreamReader ir = new InputStreamReader(pp.getInputStream());
LineNumberReader input = new LineNumberReader(ir);
for (; null != str;) {
str = input.readLine();
if (str != null) {
macSerial = str.trim();// 去空格
break;
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
if ("".equals(macSerial)) {
try {
return loadFileAsString("/sys/class/net/eth0/address")
.toUpperCase().substring(0, 17);
} catch (Exception e) {
e.printStackTrace();
}
}
if (TextUtils.isEmpty(macSerial)) { // 备用方案
macSerial = ((WifiManager) context.getSystemService(Context.WIFI_SERVICE)).getConnectionInfo().getMacAddress();
}
return macSerial;
}
private static String loadFileAsString(String fileName) throws Exception {
FileReader reader = new FileReader(fileName);
String text = loadReaderAsString(reader);
reader.close();
return text;
}
private static String loadReaderAsString(Reader reader) throws Exception {
StringBuilder builder = new StringBuilder();
char[] buffer = new char[4096];
int readLength = reader.read(buffer);
while (readLength >= 0) {
builder.append(buffer, 0, readLength);
readLength = reader.read(buffer);
}
return builder.toString();
}
private static String getIPAddress(Context context) {
NetworkInfo info = ((ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
if (info != null && info.isConnected()) {
if (info.getType() == ConnectivityManager.TYPE_MOBILE) {//当前使用2G/3G/4G网络
try {
for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
NetworkInterface intf = en.nextElement();
for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
return inetAddress.getHostAddress();
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}
} else if (info.getType() == ConnectivityManager.TYPE_WIFI) {//当前使用无线网络
return WifiMgr.getInstance(context).getCurrentIpAddress();
}
} else {
//当前无网络连接,请在设置中打开网络
}
return null;
}
private static String getNetwork(Context context) {
NetworkInfo info = ((ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
if (info != null && info.isConnected()) {
int typeMobile = info.getType();
if (typeMobile == ConnectivityManager.TYPE_WIFI) {
return "WIFI";
} else if (typeMobile == ConnectivityManager.TYPE_MOBILE) {
String status;
switch (typeMobile) {
case TelephonyManager.NETWORK_TYPE_GPRS:
case TelephonyManager.NETWORK_TYPE_EDGE:
case TelephonyManager.NETWORK_TYPE_CDMA:
case TelephonyManager.NETWORK_TYPE_1xRTT:
case TelephonyManager.NETWORK_TYPE_IDEN:
status = "2G";
break;
case TelephonyManager.NETWORK_TYPE_UMTS:
case TelephonyManager.NETWORK_TYPE_EVDO_0:
case TelephonyManager.NETWORK_TYPE_EVDO_A:
case TelephonyManager.NETWORK_TYPE_HSDPA:
case TelephonyManager.NETWORK_TYPE_HSUPA:
case TelephonyManager.NETWORK_TYPE_HSPA:
case TelephonyManager.NETWORK_TYPE_EVDO_B:
case TelephonyManager.NETWORK_TYPE_EHRPD:
case TelephonyManager.NETWORK_TYPE_HSPAP:
status = "3G";
break;
case TelephonyManager.NETWORK_TYPE_LTE:
status = "4G";
break;
default:
status = "未知";
break;
}
return status;
}
}
return null;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -5,64 +5,63 @@ import android.content.res.Resources;
public class DisplayUtils {
/**
* 根据手机的分辨率从 dip(像素) 的单位 转成为 px
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 dip(像素) 的单位 转成为 px
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dip
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dip
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
/**
* 将px值转换为sp值保证文字大小不变
*
* @param pxValue
* @param pxValue
* DisplayMetrics类中属性scaledDensity
* @return
*/
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
/**
* 将px值转换为sp值保证文字大小不变
*
* @param pxValue
* @param pxValue DisplayMetrics类中属性scaledDensity
* @return
*/
public static int px2sp(Context context, float pxValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (pxValue / fontScale + 0.5f);
}
/**
* 将sp值转换为px值保证文字大小不变
*
* @param spValue
* @param spValue
* DisplayMetrics类中属性scaledDensity
* @return
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
/**
* 获取状态栏的高度
* @param resources 资源
* @return height
*/
public static int getStatusBarHeight(Resources resources) {
return getInternalDimensionSize(resources, "status_bar_height");
}
/**
* 将sp值转换为px值保证文字大小不变
*
* @param spValue
* @param spValue DisplayMetrics类中属性scaledDensity
* @return
*/
public static int sp2px(Context context, float spValue) {
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
/**
* 获取状态栏的高度
*
* @param resources 资源
* @return height
*/
public static int getStatusBarHeight(Resources resources) {
return getInternalDimensionSize(resources, "status_bar_height");
}
public static int getInternalDimensionSize(Resources res, String key) {
int result = 0;
int resourceId = res.getIdentifier(key, "dimen", "android");
if (resourceId > 0) {
result = res.getDimensionPixelSize(resourceId);
}
return result;
}
public static int getInternalDimensionSize(Resources res, String key) {
int result = 0;
int resourceId = res.getIdentifier(key, "dimen", "android");
if (resourceId > 0) {
result = res.getDimensionPixelSize(resourceId);
}
return result;
}
}

View File

@ -1,477 +1,459 @@
package com.gh.common.util;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Message;
import android.support.v4.content.ContextCompat;
import android.support.v4.util.ArrayMap;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import android.widget.Toast;
import com.gh.common.constant.Config;
import com.gh.common.constant.Constants;
import com.gh.common.view.DownloadDialog;
import com.gh.download.DownloadEntity;
import com.gh.download.DownloadManager;
import com.gh.download.DownloadStatus;
import com.gh.gamecenter.DownloadManagerActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.adapter.viewholder.GameViewHolder;
import com.gh.gamecenter.entity.ApkEntity;
import com.gh.gamecenter.entity.GameEntity;
import com.gh.gamecenter.manager.PackageManager;
import com.lightgame.download.DownloadConfig;
import com.lightgame.download.DownloadEntity;
import com.lightgame.download.DownloadStatus;
import com.lightgame.download.FileUtils;
import com.lightgame.utils.Utils;
import java.util.concurrent.LinkedBlockingQueue;
public class DownloadItemUtils {
// 更新下载进度条
public static void processDate(Context context,
GameEntity gameEntity,
DownloadEntity downloadEntity,
RecyclerView.Adapter<RecyclerView.ViewHolder> adapter,
int index) {
// 更新下载进度条
public static void processDate(Context context,
GameEntity gameEntity,
DownloadEntity downloadEntity,
RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter,
int index) {
if (!gameEntity.getId().equals(downloadEntity.getGameId())) {
adapter.notifyItemChanged(index);
return;
}
if (!gameEntity.getId().equals(downloadEntity.getGameId())) {
adapter.notifyItemChanged(index);
return;
}
LinkedBlockingQueue<String> queue = DownloadManager.getInstance(context).getQueue(downloadEntity.getName());
if (queue == null) {
queue = new LinkedBlockingQueue<>();
DownloadManager.getInstance(context).putQueue(downloadEntity.getName(), queue);
}
LinkedBlockingQueue<String> queue = DownloadManager.getInstance(context).getQueue(downloadEntity.getName());
if (queue == null) {
queue = new LinkedBlockingQueue<>();
DownloadManager.getInstance(context).putQueue(downloadEntity.getName(), queue);
}
String platform = downloadEntity.getPlatform();
ArrayMap<String, DownloadEntity> entryMap = gameEntity.getEntryMap();
String platform = downloadEntity.getPlatform();
ArrayMap<String, DownloadEntity> entryMap = gameEntity.getEntryMap();
DownloadStatus status = downloadEntity.getStatus();
if (status.equals(DownloadStatus.pause)
|| status.equals(DownloadStatus.cancel)
|| status.equals(DownloadStatus.done)) {
queue.remove(platform);
if (entryMap == null) {
entryMap = new ArrayMap<>();
gameEntity.setEntryMap(entryMap);
}
entryMap.put(platform, downloadEntity);
if (!"pause".equals(DownloadManager.getInstance(context).getStatus(downloadEntity.getUrl()))) {
adapter.notifyItemChanged(index);
}
} else {
if (!queue.contains(platform)) {
queue.offer(platform);
if (queue.size() == 2) {
Message msg = Message.obtain();
msg.obj = downloadEntity.getName();
msg.what = Constants.DOWNLOAD_ROLL;
DownloadManager.getInstance(context).sendMessageDelayed(msg, 3000);
}
}
if (platform.equals(queue.peek())) {
if (entryMap == null) {
entryMap = new ArrayMap<>();
gameEntity.setEntryMap(entryMap);
}
entryMap.put(platform, downloadEntity);
if (!"pause".equals(DownloadManager.getInstance(context).getStatus(downloadEntity.getUrl()))) {
adapter.notifyItemChanged(index);
}
}
}
}
DownloadStatus status = downloadEntity.getStatus();
if (status.equals(DownloadStatus.pause)
|| status.equals(DownloadStatus.cancel)
|| status.equals(DownloadStatus.done)) {
queue.remove(platform);
if (entryMap == null) {
entryMap = new ArrayMap<>();
gameEntity.setEntryMap(entryMap);
}
entryMap.put(platform, downloadEntity);
if (!"pause".equals(DownloadManager.getInstance(context).getStatus(downloadEntity.getUrl()))) {
adapter.notifyItemChanged(index);
}
} else {
if (!queue.contains(platform)) {
queue.offer(platform);
if (AppDebugConfig.IS_DEBUG) {
AppDebugConfig.logMethodWithParams(DownloadItemUtils.class, queue.size(), gameEntity.getBrief(), downloadEntity.getPlatform(), index);
}
// 有两个平台同时下载的时候启用
if (queue.size() == 2) {
//TODO fuck this
Message msg = Message.obtain();
msg.obj = downloadEntity.getName();
msg.what = DownloadConfig.DOWNLOAD_ROLL;
DownloadManager.getInstance(context).sendMessageDelayed(msg, 3000);
}
}
if (platform.equals(queue.peek())) {
if (entryMap == null) {
entryMap = new ArrayMap<>();
gameEntity.setEntryMap(entryMap);
}
entryMap.put(platform, downloadEntity);
if (!"pause".equals(DownloadManager.getInstance(context).getStatus(downloadEntity.getUrl()))) {
adapter.notifyItemChanged(index);
}
}
}
}
// 更新正常的条目只有一个apk包
public static void updateNormalItem(Context context, GameViewHolder holder, GameEntity gameEntity,
boolean isShowPlatform) {
public static void updateItem(Context context, GameEntity gameEntity, GameViewHolder holder, boolean isShowPlatform) {
ArrayMap<String, DownloadEntity> entryMap = gameEntity.getEntryMap();
if (entryMap != null && !entryMap.isEmpty()) {
DownloadEntity downloadEntity = entryMap.get(gameEntity.getApk().get(0).getPlatform());
if (downloadEntity != null) {
// 更改进度条和提示文本的状态
changeStatus(context, holder, downloadEntity, isShowPlatform, true);
return;
}
}
// 控制是否显示下载按钮
if (!Config.isShow(context) || context.getString(R.string.app_name).equals(gameEntity.getName())) {
holder.gameDownloadBtn.setVisibility(View.GONE);
} else {
holder.gameDownloadBtn.setVisibility(View.VISIBLE);
}
holder.gameDes.setVisibility(View.VISIBLE);
holder.gameProgressbar.setVisibility(View.GONE);
holder.gameInfo.setVisibility(View.GONE);
if (gameEntity.isLibaoExists()) {
holder.gameLibaoIcon.setVisibility(View.VISIBLE);
} else {
holder.gameLibaoIcon.setVisibility(View.GONE);
}
holder.gameDownloadBtn.setTextColor(Color.WHITE);
if (gameEntity.isPluggable()) {
holder.gameDownloadBtn.setText("插件化");
DownloadEntity downloadEntity = DownloadManager.getInstance(context).getByPackage(
gameEntity.getApk().get(0).getPackageName());
if (downloadEntity == null
|| downloadEntity.getUrl().equals(gameEntity.getApk().get(0).getUrl())) {
holder.gameDownloadBtn.setClickable(true);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
} else {
holder.gameDownloadBtn.setClickable(false);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_up);
}
} else if (PackageManager.isInstalled(gameEntity.getApk().get(0).getPackageName())) {
if (PackageManager.isCanUpdate(gameEntity.getId(), gameEntity.getApk().get(0).getPackageName())) {
holder.gameDownloadBtn.setText("更新");
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
} else {
if (gameEntity.getTag() != null && gameEntity.getTag().size() != 0
&& !TextUtils.isEmpty(gameEntity.getApk().get(0).getGhVersion())
&& !PackageUtils.isSignature(context, gameEntity.getApk().get(0).getPackageName())) {
holder.gameDownloadBtn.setText("插件化");
DownloadEntity downloadEntity = DownloadManager.getInstance(context).getByPackage(
gameEntity.getApk().get(0).getPackageName());
if (downloadEntity == null
|| downloadEntity.getUrl().equals(gameEntity.getApk().get(0).getUrl())) {
holder.gameDownloadBtn.setClickable(true);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
} else {
holder.gameDownloadBtn.setClickable(false);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_up);
}
} else {
holder.gameDownloadBtn.setText("启动");
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_launch_style);
}
}
} else {
holder.gameDownloadBtn.setText("下载");
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
}
}
if (gameEntity.getApk() == null || gameEntity.getApk().isEmpty()) {
holder.gameDes.setVisibility(View.VISIBLE);
holder.gameProgressbar.setVisibility(View.GONE);
holder.gameInfo.setVisibility(View.GONE);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_style);
holder.gameDownloadBtn.setText("暂无");
holder.gameDownloadBtn.setClickable(false);
} else if (gameEntity.getApk().size() == 1) {
updateNormalItem(context, holder, gameEntity, isShowPlatform);
} else {
// updateNormalItem(context, holder, gameEntity, isShowPlatform);
updatePluginItem(context, holder, gameEntity, isShowPlatform);
}
// 更新插件的条目有多个apk包
public static void updatePluginItem(Context context, GameViewHolder holder, GameEntity gameEntity,
boolean isShowPlatform) {
}
GameUtils.setDownloadBtnStatus(context, gameEntity, holder.gameDownloadBtn);
// 更新正常的条目只有一个apk包
static void updateNormalItem(Context context, GameViewHolder holder, GameEntity gameEntity,
boolean isShowPlatform) {
ArrayMap<String, DownloadEntity> entryMap = gameEntity.getEntryMap();
if (entryMap != null && !entryMap.isEmpty()) {
final ArrayMap<String, DownloadEntity> entryMap = gameEntity.getEntryMap();
final ApkEntity apkEntity = gameEntity.getApk().get(0);
DownloadEntity downloadEntity;
if (entryMap != null && !entryMap.isEmpty()) {
DownloadEntity downloadEntity = entryMap.get(apkEntity.getPlatform());
if (downloadEntity != null) {
// 更改进度条和提示文本的状态
changeStatus(context, holder, downloadEntity, isShowPlatform, true);
return;
}
}
LinkedBlockingQueue<String> queue = DownloadManager.getInstance(context).getQueue(gameEntity.getName());
if (queue != null && !queue.isEmpty()) {
downloadEntity = entryMap.get(queue.peek());
} else {
downloadEntity = entryMap.get(entryMap.keyAt(0));
}
holder.gameDes.setVisibility(View.VISIBLE);
holder.gameProgressbar.setVisibility(View.GONE);
holder.gameInfo.setVisibility(View.GONE);
if (downloadEntity != null) {
// 更改进度条和提示文本的状态
changeStatus(context, holder, downloadEntity, isShowPlatform, false);
return;
}
}
holder.gameDownloadBtn.setTextColor(Color.WHITE);
holder.gameDes.setVisibility(View.VISIBLE);
holder.gameProgressbar.setVisibility(View.GONE);
holder.gameInfo.setVisibility(View.GONE);
}
final String packageName = apkEntity.getPackageName();
// 更改进度条和提示文本的状态
public static void changeStatus(Context context, GameViewHolder holder, DownloadEntity downloadEntity,
boolean isShowPlatform, boolean isNormal) {
holder.gameDes.setVisibility(View.GONE);
holder.gameProgressbar.setVisibility(View.VISIBLE);
holder.gameInfo.setVisibility(View.VISIBLE);
if (gameEntity.isPluggable()) {
holder.gameDownloadBtn.setText(R.string.pluggable);
setwhat(context, holder, apkEntity, packageName);
} else if (PackageManager.isInstalled(packageName)) {
if (PackageManager.isCanUpdate(gameEntity.getId(), packageName)) {
holder.gameDownloadBtn.setText(R.string.update);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
} else {
if (gameEntity.getTag() != null && gameEntity.getTag().size() != 0
&& !TextUtils.isEmpty(apkEntity.getGhVersion())
&& !PackageUtils.isSignature(context, packageName)) {
holder.gameDownloadBtn.setText(R.string.pluggable);
setwhat(context, holder, apkEntity, packageName);
} else {
holder.gameDownloadBtn.setText(R.string.launch);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_launch_style);
}
}
} else {
holder.gameDownloadBtn.setText(R.string.download);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
}
}
String platform = PlatformUtils.getInstance(context)
.getPlatformName(downloadEntity.getPlatform());
/**
* 这个干什么鬼?
*
* @param context
* @param holder
* @param apkEntity
* @param packageName
*/
public static void setwhat(Context context, GameViewHolder holder, ApkEntity apkEntity, String packageName) {
DownloadEntity downloadEntity = DownloadManager.getInstance(context).getDownloadEntityByPackageName(packageName);
if (downloadEntity == null || downloadEntity.getUrl().equals(apkEntity.getUrl())) {
holder.gameDownloadBtn.setClickable(true);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
} else {
holder.gameDownloadBtn.setClickable(false);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_up);
}
}
DownloadStatus status = downloadEntity.getStatus();
if (status.equals(DownloadStatus.downloading)) {
if (!"pause".equals(DownloadManager.getInstance(context).getStatus(downloadEntity.getUrl()))) {
holder.gameProgressbar.setProgress((int) (downloadEntity.getPercent() * 10));
if (isShowPlatform && platform != null) {
holder.gameDownloadSpeed.setText(String.format("%s - %s(剩%s)", platform,
SpeedUtils.getSpeed(downloadEntity.getSpeed()),
SpeedUtils.getRemainTime(downloadEntity.getSize(), downloadEntity.getProgress(), downloadEntity.getSpeed() * 1024)));
} else {
holder.gameDownloadSpeed.setText(String.format("%s(剩%s)", SpeedUtils.getSpeed(downloadEntity.getSpeed()),
SpeedUtils.getRemainTime(downloadEntity.getSize(), downloadEntity.getProgress(), downloadEntity.getSpeed() * 1024)));
}
holder.gameDownloadPercentage.setText(downloadEntity.getPercent() + "%");
}
// 更新插件的条目有多个apk包
static void updatePluginItem(Context context, GameViewHolder holder, GameEntity gameEntity,
boolean isShowPlatform) {
if (isNormal) {
holder.gameDownloadBtn.setText("下载中");
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
holder.gameDownloadBtn.setTextColor(context.getResources().getColorStateList(R.color.text_downloading_style));
}
} else if (status.equals(DownloadStatus.waiting)) {
holder.gameProgressbar.setProgress((int) (downloadEntity.getPercent() * 10));
if (isShowPlatform && platform != null) {
holder.gameDownloadSpeed.setText(String.format("%s - 等待", platform));
} else {
holder.gameDownloadSpeed.setText("等待");
}
holder.gameDownloadPercentage.setText(downloadEntity.getPercent() + "%");
GameUtils.setDownloadBtnStatus(context, gameEntity, holder.gameDownloadBtn);
if (isNormal) {
holder.gameDownloadBtn.setText("下载中");
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
holder.gameDownloadBtn.setTextColor(context.getResources().getColorStateList(R.color.text_downloading_style));
}
} else if (status.equals(DownloadStatus.pause)
|| status.equals(DownloadStatus.timeout)
|| status.equals(DownloadStatus.neterror)) {
holder.gameProgressbar.setProgress((int) (downloadEntity.getPercent() * 10));
if (isShowPlatform && platform != null) {
holder.gameDownloadSpeed.setText(String.format("%s - 暂停", platform));
} else {
holder.gameDownloadSpeed.setText("暂停");
}
holder.gameDownloadPercentage.setText(downloadEntity.getPercent() + "%");
ArrayMap<String, DownloadEntity> entryMap = gameEntity.getEntryMap();
if (entryMap != null && !entryMap.isEmpty()) {
if (isNormal) {
holder.gameDownloadBtn.setText("下载中");
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
holder.gameDownloadBtn.setTextColor(context.getResources().getColorStateList(R.color.text_downloading_style));
}
} else if (status.equals(DownloadStatus.done)) {
holder.gameProgressbar.setProgress(1000);
if (isShowPlatform && platform != null) {
holder.gameDownloadSpeed.setText(String.format("%s - 下载完成", platform));
} else {
holder.gameDownloadSpeed.setText("下载完成");
}
holder.gameDownloadPercentage.setText(R.string.hundred_percent);
DownloadEntity downloadEntity;
if (isNormal) {
holder.gameDownloadBtn.setText("安装");
holder.gameDownloadBtn.setTextColor(Color.WHITE);
if (downloadEntity.isPluggable()
&& PackageManager.isInstalled(downloadEntity.getPackageName())) {
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
} else {
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
}
}
}
}
LinkedBlockingQueue<String> queue = DownloadManager.getInstance(context).getQueue(gameEntity.getName());
if (queue != null && !queue.isEmpty()) {
downloadEntity = entryMap.get(queue.peek());
} else {
downloadEntity = entryMap.get(entryMap.keyAt(0));
}
public static void updateItem(Context context, GameEntity gameEntity, GameViewHolder holder, boolean isShowPlatform) {
if (downloadEntity != null) {
// 更改进度条和提示文本的状态
changeStatus(context, holder, downloadEntity, isShowPlatform, false);
return;
}
}
// 控制是否显示下载按钮
if (!Config.isShow(context) || "光环助手".equals(gameEntity.getName())) {
holder.gameDownloadBtn.setVisibility(View.GONE);
} else {
holder.gameDownloadBtn.setVisibility(View.VISIBLE);
}
holder.gameDes.setVisibility(View.VISIBLE);
holder.gameProgressbar.setVisibility(View.GONE);
holder.gameInfo.setVisibility(View.GONE);
}
if (gameEntity.isLibaoExists()) {
holder.gameLibaoIcon.setVisibility(View.VISIBLE);
} else {
holder.gameLibaoIcon.setVisibility(View.GONE);
}
// 更改进度条和提示文本的状态
public static void changeStatus(Context context, GameViewHolder holder, DownloadEntity downloadEntity,
boolean isShowPlatform, boolean isNormal) {
holder.gameDes.setVisibility(View.GONE);
holder.gameProgressbar.setVisibility(View.VISIBLE);
holder.gameInfo.setVisibility(View.VISIBLE);
// LibaoDao libaoDao = new LibaoDao(context);
// if (libaoDao.isExist(gameEntity.getId())) {
// holder.gameLibaoIcon.setVisibility(View.VISIBLE);
// } else {
//
// }
String platform = PlatformUtils.getInstance(context).getPlatformName(downloadEntity.getPlatform());
if (gameEntity.getApk() == null || gameEntity.getApk().isEmpty()) {
holder.gameDes.setVisibility(View.VISIBLE);
holder.gameProgressbar.setVisibility(View.GONE);
holder.gameInfo.setVisibility(View.GONE);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_pause_style);
holder.gameDownloadBtn.setText("暂无");
holder.gameDownloadBtn.setClickable(false);
} else if (gameEntity.getApk().size() == 1) {
updateNormalItem(context, holder, gameEntity, isShowPlatform);
} else {
updatePluginItem(context, holder, gameEntity, isShowPlatform);
}
DownloadStatus status = downloadEntity.getStatus();
if (status.equals(DownloadStatus.downloading)) {
if (!"pause".equals(DownloadManager.getInstance(context).getStatus(downloadEntity.getUrl()))) {
holder.gameProgressbar.setProgress((int) (downloadEntity.getPercent() * 10));
if (isShowPlatform && platform != null) {
holder.gameDownloadSpeed.setText(String.format("%s - %s(剩%s)", platform,
SpeedUtils.getSpeed(downloadEntity.getSpeed()),
SpeedUtils.getRemainTime(downloadEntity.getSize(), downloadEntity.getProgress(), downloadEntity.getSpeed() * 1024)));
} else {
holder.gameDownloadSpeed.setText(String.format("%s(剩%s)", SpeedUtils.getSpeed(downloadEntity.getSpeed()),
SpeedUtils.getRemainTime(downloadEntity.getSize(), downloadEntity.getProgress(), downloadEntity.getSpeed() * 1024)));
}
holder.gameDownloadPercentage.setText(downloadEntity.getPercent() + "%");
}
}
if (isNormal) {
holder.gameDownloadBtn.setText(R.string.downloading);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
holder.gameDownloadBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.text_downloading_style));
}
} else if (status.equals(DownloadStatus.waiting)) {
holder.gameProgressbar.setProgress((int) (downloadEntity.getPercent() * 10));
if (isShowPlatform && platform != null) {
holder.gameDownloadSpeed.setText(String.format("%s - 等待", platform));
} else {
holder.gameDownloadSpeed.setText("等待");
}
holder.gameDownloadPercentage.setText(downloadEntity.getPercent() + "%");
private static void setNormalOnClickListener(final Context context,
final TextView downloadBtn,
final GameEntity gameEntity,
final int position,
final RecyclerView.Adapter<RecyclerView.ViewHolder> adapter,
final String entrance,
final String location, final boolean isCloseSoftInput) {
downloadBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isCloseSoftInput) {
InputMethodManager imm = (InputMethodManager) context
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
if (isNormal) {
holder.gameDownloadBtn.setText(R.string.downloading);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
holder.gameDownloadBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.text_downloading_style));
}
} else if (status.equals(DownloadStatus.pause)
|| status.equals(DownloadStatus.timeout)
|| status.equals(DownloadStatus.neterror)) {
holder.gameProgressbar.setProgress((int) (downloadEntity.getPercent() * 10));
if (isShowPlatform && platform != null) {
holder.gameDownloadSpeed.setText(String.format("%s - 暂停", platform));
} else {
holder.gameDownloadSpeed.setText("暂停");
}
holder.gameDownloadPercentage.setText(downloadEntity.getPercent() + "%");
String str = downloadBtn.getText().toString();
if ("下载".equals(str)) {
if (NetworkUtils.isWifiConnected(context)) {
download(context, gameEntity, downloadBtn, entrance, location);
} else {
DialogUtils.showDownloadDialog(context, new DialogUtils.ConfiremListener() {
@Override
public void onConfirem() {
download(context, gameEntity, downloadBtn, entrance, location);
}
});
}
} else if ("插件化".equals(str)) {
if (NetworkUtils.isWifiConnected(context)) {
plugin(context, gameEntity, downloadBtn, entrance, location);
} else {
DialogUtils.showDownloadDialog(context, new DialogUtils.ConfiremListener() {
@Override
public void onConfirem() {
plugin(context, gameEntity, downloadBtn, entrance, location);
}
});
}
} else if ("安装".equals(str)) {
install(context, gameEntity, position, adapter);
} else if ("启动".equals(str)) {
DataUtils.onGameLaunchEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), location);
if (isNormal) {
holder.gameDownloadBtn.setText(R.string.downloading);
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
holder.gameDownloadBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.text_downloading_style));
}
} else if (status.equals(DownloadStatus.done)) {
holder.gameProgressbar.setProgress(1000);
if (isShowPlatform && platform != null) {
holder.gameDownloadSpeed.setText(String.format("%s - 下载完成", platform));
} else {
holder.gameDownloadSpeed.setText("下载完成");
}
holder.gameDownloadPercentage.setText(R.string.hundred_percent);
PackageUtils.launchApplicationByPackageName(context, gameEntity.getApk().get(0).getPackageName());
} else if ("下载中".equals(str)) {
Intent intent = new Intent(context, DownloadManagerActivity.class);
intent.putExtra("currentItem", 1);
intent.putExtra("url", gameEntity.getApk().get(0).getUrl());
intent.putExtra("entrance", entrance + "+(" + location.split(":")[0] + ")");
context.startActivity(intent);
} else if ("更新".equals(str)) {
if (NetworkUtils.isWifiConnected(context)) {
update(context, gameEntity, entrance, location);
} else {
DialogUtils.showDownloadDialog(context, new DialogUtils.ConfiremListener() {
@Override
public void onConfirem() {
update(context, gameEntity, entrance, location);
}
});
}
}
}
});
}
if (isNormal) {
holder.gameDownloadBtn.setText("安装");
holder.gameDownloadBtn.setTextColor(Color.WHITE);
if (downloadEntity.isPluggable()
&& PackageManager.isInstalled(downloadEntity.getPackageName())) {
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
} else {
holder.gameDownloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
}
}
}
}
private static void setPluginOnClickListener(final Context context,
final TextView downloadBtn,
final GameEntity entity,
final String entrance,
final String location,
final boolean isCloseSoftInput) {
downloadBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View v) {
if (isCloseSoftInput) {
InputMethodManager imm = (InputMethodManager) context
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
DownloadDialog.getInstance(context).showPopupWindow(v, entity, entrance, location);
}
});
}
public static void setOnClickListener(final Context context,
final TextView downloadBtn,
final GameEntity gameEntity,
final int position,
final RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter,
final String entrance,
final String location) {
public static void setOnClickListener(Context context,
TextView downloadBtn,
GameEntity gameEntity,
int position,
RecyclerView.Adapter<RecyclerView.ViewHolder> adapter,
String entrance,
String location,
boolean isCloseSoftInput) {
if (gameEntity.getApk().size() == 1) {
downloadBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
onNormalClick(context, downloadBtn, gameEntity, position, adapter, entrance, location);
}
});
} else {
downloadBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
DownloadDialog.getInstance(context).showPopupWindow(v, gameEntity, entrance, location);
}
});
}
if (gameEntity.getApk().size() == 1) {
setNormalOnClickListener(context, downloadBtn, gameEntity, position, adapter, entrance, location, isCloseSoftInput);
} else {
setPluginOnClickListener(context, downloadBtn, gameEntity, entrance, location, isCloseSoftInput);
}
}
}
public static void onNormalClick(final Context context,
final TextView downloadBtn,
final GameEntity gameEntity,
final int position,
final RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter,
final String entrance,
final String location) {
//更新
private static void update(Context context,
GameEntity gameEntity,
String entrance,
String location) {
DataUtils.onGameUpdateEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), "下载开始");
DownloadManager.createDownload(context, gameEntity, "更新", entrance, location);
}
String str = downloadBtn.getText().toString();
switch (str) {
case "下载":
if (NetworkUtils.isWifiConnected(context)) {
download(context, gameEntity, downloadBtn, entrance, location);
} else {
DialogUtils.showDownloadDialog(context, new DialogUtils.ConfirmListener() {
@Override
public void onConfirm() {
download(context, gameEntity, downloadBtn, entrance, location);
}
});
}
break;
case "插件化":
if (NetworkUtils.isWifiConnected(context)) {
plugin(context, gameEntity, downloadBtn, entrance, location);
} else {
DialogUtils.showDownloadDialog(context, new DialogUtils.ConfirmListener() {
@Override
public void onConfirm() {
plugin(context, gameEntity, downloadBtn, entrance, location);
}
});
}
break;
case "安装":
install(context, gameEntity, position, adapter);
break;
case "启动":
DataUtils.onGameLaunchEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), location);
//下载
private static void download(Context context,
GameEntity gameEntity,
TextView downloadBtn,
String entrance,
String location) {
String msg = FileUtils.isCanDownload(context, gameEntity.getApk().get(0).getSize());
if (TextUtils.isEmpty(msg)) {
DataUtils.onGameDownloadEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), entrance, "下载开始");
PackageUtils.launchApplicationByPackageName(context, gameEntity.getApk().get(0).getPackageName());
break;
case "下载中":
context.startActivity(
DownloadManagerActivity.getDownloadMangerIntent(context, gameEntity.getApk().get(0).getUrl(), entrance + "+(" + location.split(":")[0] + ")"));
break;
case "更新":
if (NetworkUtils.isWifiConnected(context)) {
update(context, gameEntity, entrance, location);
} else {
DialogUtils.showDownloadDialog(context, new DialogUtils.ConfirmListener() {
@Override
public void onConfirm() {
update(context, gameEntity, entrance, location);
}
});
}
break;
}
}
DownloadManager.createDownload(context, gameEntity, "下载", entrance, location);
Toast.makeText(context, gameEntity.getName() + "已加入下载队列", Toast.LENGTH_SHORT).show();
//下载
private static void download(Context context,
GameEntity gameEntity,
TextView downloadBtn,
String entrance,
String location) {
String msg = FileUtils.isCanDownload(context, gameEntity.getApk().get(0).getSize());
if (TextUtils.isEmpty(msg)) {
DataUtils.onGameDownloadEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), entrance, "下载开始");
downloadBtn.setText("下载中");
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
downloadBtn.setTextColor(context.getResources().getColorStateList(R.color.text_downloading_style));
DownloadManager.createDownload(context, gameEntity, context.getString(R.string.download), entrance, location);
Utils.toast(context, gameEntity.getName() + "已加入下载队列");
DownloadManager.getInstance(context).putStatus(gameEntity.getApk().get(0).getUrl(), "downloading");
} else {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
downloadBtn.setText(R.string.downloading);
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
downloadBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.text_downloading_style));
//插件化
private static void plugin(Context context,
GameEntity gameEntity,
TextView downloadBtn,
String entrance,
String location) {
String msg = FileUtils.isCanDownload(context, gameEntity.getApk().get(0).getSize());
if (TextUtils.isEmpty(msg)) {
DataUtils.onGameDownloadEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), entrance, "下载开始");
// DownloadManager.getInstance(context).putStatus(gameEntity.getApk().get(0).getUrl(), "downloading");
} else {
Utils.toast(context, msg);
}
}
DownloadManager.createDownload(context, gameEntity, "插件化", entrance, location);
Toast.makeText(context, gameEntity.getName() + "已加入下载队列", Toast.LENGTH_SHORT).show();
//插件化
private static void plugin(Context context, GameEntity gameEntity, TextView downloadBtn, String entrance,
String location) {
String msg = FileUtils.isCanDownload(context, gameEntity.getApk().get(0).getSize());
if (TextUtils.isEmpty(msg)) {
DataUtils.onGameDownloadEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), entrance, "下载开始");
downloadBtn.setText("下载中");
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
downloadBtn.setTextColor(context.getResources().getColorStateList(R.color.text_downloading_style));
DownloadManager.createDownload(context, gameEntity, "插件化", entrance, location);
Utils.toast(context, gameEntity.getName() + "已加入下载队列");
DownloadManager.getInstance(context).putStatus(gameEntity.getApk().get(0).getUrl(), "downloading");
} else {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
downloadBtn.setText(R.string.downloading);
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_downloading_style);
downloadBtn.setTextColor(ContextCompat.getColorStateList(context, R.color.text_downloading_style));
//安装
private static void install(final Context context,
GameEntity gameEntity,
int position,
RecyclerView.Adapter<RecyclerView.ViewHolder> adapter) {
DownloadEntity downloadEntity = DownloadManager.getInstance(context).get(gameEntity.getApk().get(0).getUrl());
if (downloadEntity != null) {
final String path = downloadEntity.getPath();
if (FileUtils.isEmptyFile(path)) {
Toast.makeText(context, "解析包出错(可能被误删了),请重新下载", Toast.LENGTH_SHORT).show();
DownloadManager.getInstance(context).cancel(downloadEntity.getUrl());
if (gameEntity.getEntryMap() != null) {
gameEntity.getEntryMap().remove(gameEntity.getApk().get(0).getPlatform());
}
adapter.notifyItemChanged(position);
} else {
PackageUtils.launchSetup(context, path);
}
}
}
// DownloadManager.getInstance(context).putStatus(gameEntity.getApk().get(0).getUrl(), "downloading");
} else {
Utils.toast(context, msg);
}
}
//安装
private static void install(final Context context, GameEntity gameEntity, int position,
RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter) {
ApkEntity apkEntity = gameEntity.getApk().get(0);
DownloadEntity downloadEntity = DownloadManager.getInstance(context).getDownloadEntityByUrl(apkEntity.getUrl());
if (downloadEntity != null) {
final String path = downloadEntity.getPath();
if (FileUtils.isEmptyFile(path)) {
Utils.toast(context, context.getString(R.string.install_failure_hint));
DownloadManager.getInstance(context).cancel(downloadEntity.getUrl());
if (gameEntity.getEntryMap() != null) {
gameEntity.getEntryMap().remove(apkEntity.getPlatform());
}
adapter.notifyItemChanged(position);
} else {
PackageUtils.launchSetup(context, path);
}
}
}
//更新
private static void update(Context context, GameEntity gameEntity, String entrance, String location) {
DataUtils.onGameUpdateEvent(context, gameEntity.getName(), gameEntity.getApk().get(0).getPlatform(), "下载开始");
DownloadManager.createDownload(context, gameEntity, "更新", entrance, location);
}
}

View File

@ -0,0 +1,82 @@
package com.gh.common.util;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import com.gh.gamecenter.MainActivity;
import com.gh.gamecenter.SplashScreenActivity;
/**
* @author CsHeng
* @Date 2017/4/25
* @Time 16:39
*/
public class EntranceUtils {
public static final String KEY_TO = "to";
public static final String KEY_NEWSID = "newsId";
public static final String KEY_GAMEID = "gameId";
public static final String KEY_ID = "id";
public static final String KEY_URL = "url";
public static final String KEY_GAMENAME = "gameName";
public static final String HOST_ARTICLE = "article";
public static final String HOST_GAME = "game";
public static final String HOST_COLUMN = "column";
public static final String HOST_WEB = "web";
public static final String HOST_DOWNLOAD = "download";
public static final String HOST_SUGGESTION = "suggestion";
public static final String KEY_DATA = "data";
public static final String KEY_TYPE = "type";
public static final String KEY_NAME = "name";
public static final String KEY_ENTRANCE = "entrance";
public static final String KEY_TARGET = "target";
public static final String ENTRANCE_BROWSER = "(浏览器)";
public static final String ENTRANCE_UMENG = "(友盟推送)";
public static final String ENTRANCE_MIPUSH = "(小米推送)";
public static final String ENTRANCE_DOWNLOAD = "(下载跳转)";
public static final String KEY_SUGGEST_HINT_TYPE = "suggestHintType";
public static final String KEY_PACKAGENAME = "packageName";
public static final String KEY_PLATFORM = "platform";
public static final String KEY_GAME_NAME = "game_name";
public static final String KEY_VERSION = "version";
public static final String KEY_CONTENT = "content";
public static final String KEY_PLUGIN = "plugin";
public static final String KEY_CURRENTITEM = "currentItem";
public static final String KEY_COMMENTID = "commentId";
public static final String KEY_PATH = "path";
public static final String KEY_OLDERUSER = "isOldUser";
public static final String KEY_SEARCHKEY = "searchKey";
public static final String KEY_HINT = "hint";
public static final String KEY_GAME_ICON_URL = "gameIconUrl";
public static final String KEY_SHARECONTENT = "shareContent";
public static final String KEY_SUGGESTTYPE = "suggestType";
public static final String KEY_PROLIST = "provinceList";
public static final String KEY_ORDER = "order";
public static final String KEY_TAGTYPE = "tagType";
public static void jumpActivity(Context context, Bundle bundle) {
//TODO 把其他类似的跳转启动逻辑也处理掉
if (RunningUtils.isRunning(context)
&& MainActivity.class.getName().equals(RunningUtils.getBaseActivity(context))) {
// 应用正在运行,前台或后台
String to = bundle.getString(KEY_TO);
if (!TextUtils.isEmpty(to)) {
Class<?> clazz = ClassUtils.forName(to);
if (clazz != null) {
Intent intent1 = new Intent(context, clazz);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.putExtra(KEY_DATA, bundle);
context.startActivity(intent1);
}
}
} else {
// 应用未在运行
context.startActivity(SplashScreenActivity.getSplashScreenIntent(context, bundle));
}
}
}

View File

@ -1,384 +0,0 @@
package com.gh.common.util;
import android.content.Context;
import android.os.Environment;
import android.os.StatFs;
import android.os.StrictMode;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.UUID;
/**
*
* @author guanchao wen
* @email shuwoom.wgc@gmail.com
* @modify hzh 2016/03/16
* @update 2015-7-29下午2:26:02
*/
public class FileUtils {
private final static String TEST_FILE_NAME = System.currentTimeMillis() + ".log";
public static String getDownloadDir(Context context) {
String dir = null;
if (isMounted()) {
String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath();
dir = checkDir(baseDir + File.separator + "gh-download");
File file = new File(dir + File.separator + TEST_FILE_NAME);
if (!file.exists()) {
try {
if (!file.createNewFile()) {
// cannot create file
Utils.log("cannot create file");
dir = null;
}
} catch (IOException e) {
e.printStackTrace();
// cannot create file
Utils.log("cannot create file");
dir = null;
}
}
}
if (dir == null) {
String baseDir = context.getFilesDir().getAbsolutePath();
dir = checkDir(baseDir + File.separator + "gh-download");
try {
Runtime.getRuntime().exec("chmod 755 " + dir);
} catch (IOException e) {
e.printStackTrace();
}
}
return dir;
}
public static String getDownloadPath(Context context, String name) {
return getDownloadDir(context) + File.separator + name;
}
public static String getLogPath(Context context, String name) {
return checkDir(getDir(context, "log")) + File.separator + name;
}
public static String getPlatformPicDir(Context context) {
return checkDir(getDir(context, "PlatformPic"));
}
private static String checkDir(String dir) {
File directory = new File(dir);
if (directory.exists() && !directory.isDirectory()) {
directory.delete();
}
if (!directory.exists()) {
directory.mkdirs();
}
return dir;
}
public static void deleteFile(String savePath) {
File file = new File(savePath);
if (file.exists()) {
file.delete();
}
}
public static void deleteFolder(File folder) {
if (folder != null) {
if (folder.isDirectory()) {
for (File file : folder.listFiles()) {
if (file.isDirectory()) {
deleteFolder(file);
} else {
file.delete();
}
}
}
folder.delete();
}
}
public static boolean isEmptyFile(String path) {
File file = new File(path);
return !(file.exists() && file.length() != 0);
}
public static boolean isMounted() {
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
}
public static String getDir(Context context, String dir) {
if (isMounted()) {
// /storage/emulated/0/Android/data/包名/files
File file = context.getExternalFilesDir(null);
if (file != null) {
return file.getAbsolutePath() + File.separator + dir;
}
}
// /data/data/包名/files
return context.getFilesDir().getAbsolutePath() + File.separator + dir;
}
// 返回剩余空间 单位MB
@SuppressWarnings("deprecation")
public static float getFreeSpaceByPath(String path) {
StatFs statfs = new StatFs(path);
long blockSize = statfs.getBlockSize();
long availableBlocks = statfs.getAvailableBlocks();
return availableBlocks * blockSize / 1024f / 1024f;
}
public static String isCanDownload(Context context, String size) {
String msg = null;
String packageSizeStr = "";
for (int i = 0; i < size.length(); i++) {
if ((size.charAt(i) >= 48 && size.charAt(i) <= 57) || size.charAt(i) == 46) {
packageSizeStr += size.charAt(i);
}
}
float packageSize = 0;
if (packageSizeStr.length() != 0) {
packageSize = Float.valueOf(packageSizeStr);
}
float freeSpace = getFreeSpaceByPath(getDownloadDir(context));
if (freeSpace < packageSize) {
msg = "手机存储空间不足,无法进行下载!";
}
return msg;
}
// 下载文件
public static int downloadFile(String url, String savePath) {
DataInputStream dis = null;
FileOutputStream fos = null;
try {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5 * 1000);
connection.setReadTimeout(5 * 1000);
connection.connect();
int code = connection.getResponseCode();
if (code == 200) {
dis = new DataInputStream(connection.getInputStream());
File file = new File(savePath);
if (file.exists()) {
file.delete();
}
file.createNewFile();
fos = new FileOutputStream(file);
byte[] buffer = new byte[1024];
int len;
while ((len = dis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
}
return code;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (dis != null) {
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return -1;
}
// 上传文件
public static JSONObject uploadFile(String url, String filePath, String token) {
String end = "\r\n";
String twoHyphens = "--";
String boundary = UUID.randomUUID().toString().replaceAll("-", "").substring(0, 16);
try {
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
/*
* Output to the connection. Default is false, set to true because
* post method must write something to the connection
*/
connection.setDoOutput(true);
// Read from the connection. Default is true.
connection.setDoInput(true);
// Post cannot use caches
connection.setUseCaches(false);
// Set the post method. Default is GET
connection.setRequestMethod("POST");
connection.setConnectTimeout(5 * 1000);
connection.setReadTimeout(5 * 1000);
// 设置请求属性
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Charset", "UTF-8");
connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
if (token != null) {
connection.setRequestProperty("TOKEN", token);
}
// 设置StrictMode 否则HTTPURLConnection连接失败因为这是在主进程中进行网络连接
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads().detectDiskWrites().detectNetwork()
.penaltyLog().build());
File file = new File(filePath);
if (file.exists()) {
Utils.log("name = " + file.getName());
Utils.log("length = " + file.length());
}
// 设置DataOutputStreamgetOutputStream中默认调用connect()
DataOutputStream dos = new DataOutputStream(connection.getOutputStream()); // output
// to the connection
dos.writeBytes(twoHyphens + boundary + end);
dos.writeBytes("Content-Disposition: form-data; "
+ "name=\"Filedata\";filename=\"" + file.getName() + "\"" + end);
dos.writeBytes(end);
// 取得文件的FileInputStream
FileInputStream fStream = new FileInputStream(file);
// 设置每次写入8192bytes
int bufferSize = 8192;
byte[] buffer = new byte[bufferSize]; // 8k
int length;
// 从文件读取数据至缓冲区
while ((length = fStream.read(buffer)) != -1) {
// 将资料写入DataOutputStream中
dos.write(buffer, 0, length);
}
dos.writeBytes(end);
dos.writeBytes(twoHyphens + boundary + twoHyphens + end);
// 关闭流写入的东西自动生成Http正文
fStream.close();
// 关闭DataOutputStream
dos.flush();
dos.close();
InputStream inputStream;
try {
inputStream = connection.getInputStream();
} catch (IOException ioe) {
inputStream = connection.getErrorStream();
}
int ch;
StringBuffer b = new StringBuffer();
while ((ch = inputStream.read()) != -1) {
b.append((char) ch);
}
// 显示网页响应内容
Utils.log("content = " + b.toString().trim());
int statusCode = connection.getResponseCode();
Utils.log("statusCode = " + statusCode);
if (statusCode == 200) {
// {"icon":"http:\/\/gh-test-1.oss-cn-qingdao.aliyuncs.com\/pic\/57e4f4d58a3200042d29492f.jpg"}
JSONObject response = new JSONObject(b.toString().trim());
response.put("statusCode", 200);
return response;
} else if (statusCode == 403) {
JSONObject response = new JSONObject(b.toString().trim());
response.put("statusCode", 403);
return response;
} else if (statusCode == 401) {
JSONObject response = new JSONObject();
response.put("statusCode", 401);
return response;
}
} catch (Exception e) {
// 显示异常信息
e.printStackTrace();
Utils.log("Fail:" + e);
}
return null;
}
// 读取文件返回byte[]
public static byte[] readFile(File file) {
if (file == null) {
return null;
}
FileInputStream fis = null;
ByteArrayOutputStream bos = null;
try {
fis = new FileInputStream(file);
bos = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int len;
while ((len = fis.read(buffer)) != -1) {
bos.write(buffer, 0, len);
}
bos.flush();
return bos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
// 根据byte[],保存文件
public static void saveFile(File file, byte[] data) {
if (file == null || data == null) {
return;
}
ByteArrayInputStream bis = null;
FileOutputStream fos = null;
try {
bis = new ByteArrayInputStream(data);
fos = new FileOutputStream(file);
byte[] buffer = new byte[2048];
int len;
while ((len = bis.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

View File

@ -1,151 +1,129 @@
package com.gh.common.util;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.text.TextUtils;
import android.widget.TextView;
import com.gh.base.AppController;
import com.gh.download.DownloadEntity;
import com.gh.download.DownloadManager;
import com.gh.download.DownloadStatus;
import com.gh.gamecenter.GameDetailActivity;
import com.gh.gamecenter.R;
import com.gh.gamecenter.entity.ApkEntity;
import com.gh.gamecenter.entity.GameEntity;
import com.gh.gamecenter.entity.GameUpdateEntity;
import com.gh.gamecenter.manager.PackageManager;
import com.lightgame.download.DownloadEntity;
import com.lightgame.download.DownloadStatus;
import java.util.List;
public class GameUtils {
/**
* 去除与重复sourceList相同的数据
*/
public static List<GameEntity> removeDuplicateData(List<GameEntity> sourceList, List<GameEntity> rawList) {
if (sourceList == null || sourceList.isEmpty()
|| rawList == null || rawList.isEmpty()) {
return rawList;
}
String id;
for (int i = 0; i < rawList.size(); i++) {
id = rawList.get(i).getId();
for (GameEntity gameEntity : sourceList) {
if (id.equals(gameEntity.getId())) {
rawList.remove(i);
i--;
break;
}
}
}
return rawList;
}
/**
* 去除与重复sourceList相同的数据
*/
public static List<GameEntity> removeDuplicateData(List<GameEntity> sourceList, List<GameEntity> rawList) {
if (sourceList == null || sourceList.isEmpty()
|| rawList == null || rawList.isEmpty()) {
return rawList;
}
String id;
for (int i = 0; i < rawList.size(); i++) {
id = rawList.get(i).getId();
for (GameEntity gameEntity : sourceList) {
if (id.equals(gameEntity.getId())) {
rawList.remove(i);
i--;
break;
}
}
}
return rawList;
}
/**
* 启动游戏详情页面
*/
public static void startGameDetailActivity(Context context, GameEntity gameEntity, String entrance) {
AppController.put("GameEntity", gameEntity);
Intent intent = new Intent(context, GameDetailActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("entrance", entrance);
context.startActivity(intent);
}
/**
* 设置下载按钮状态
*/
public static void setDownloadBtnStatus(Context context, GameEntity gameEntity, TextView downloadBtn) {
String status = getDownloadBtnText(context, gameEntity);
downloadBtn.setTextColor(Color.WHITE);
downloadBtn.setText(status);
if ("插件化".equals(status)) {
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
} else if ("打开".equals(status)) {
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_launch_style);
} else {
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
}
}
/**
* 启动游戏详情页面
*/
public static void startGameDetailActivity(Context context, String gameId, String entrance) {
Intent intent = new Intent(context, GameDetailActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("gameId", gameId);
intent.putExtra("entrance", entrance);
context.startActivity(intent);
}
/**
* 获取下载按钮文案
*/
public static String getDownloadBtnText(Context context, GameEntity gameEntity) {
int doneCount = 0; // 下载完成数量
int pluginCount = 0; // 可插件化数量
int updateCount = 0; // 可更新数量
int installCount = 0; // 已安装数量
DownloadEntity downloadEntity;
Object gh_id;
for (ApkEntity apkEntity : gameEntity.getApk()) {
downloadEntity = DownloadManager.getInstance(context).getDownloadEntityByUrl(apkEntity.getUrl());
if (downloadEntity != null) {
if (downloadEntity.getStatus().equals(DownloadStatus.done)) {
doneCount++;
} else if (downloadEntity.isPluggable()) {
pluginCount++;
} else if (downloadEntity.isUpdate()) {
updateCount++;
}
}
if (PackageManager.isCanUpdate(gameEntity.getId(), apkEntity.getPackageName())) {
updateCount++;
}
if (PackageManager.isInstalled(apkEntity.getPackageName())) {
gh_id = PackageUtils.getMetaData(context, apkEntity.getPackageName(), "gh_id");
if (gameEntity.getTag() != null && gameEntity.getTag().size() != 0
&& !TextUtils.isEmpty(apkEntity.getGhVersion())
&& !PackageUtils.isSignature(context, apkEntity.getPackageName())) {
pluginCount++;
} else if (gh_id == null || gh_id.equals(gameEntity.getId())) {
installCount++;
}
}
}
if (doneCount != 0) {
return "安装";
} else if (pluginCount != 0) {
return "插件化";
} else if (updateCount != 0) {
return "更新";
} else if (installCount != 0) {
return "打开";
} else {
return "下载";
}
}
/**
* 设置下载按钮状态
*/
public static void setDownloadBtnStatus(Context context, GameEntity gameEntity, TextView downloadBtn) {
String status = getDownloadBtnText(context, gameEntity);
downloadBtn.setTextColor(Color.WHITE);
downloadBtn.setText(status);
if ("插件化".equals(status)) {
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_plugin_style);
} else if ("打开".equals(status)) {
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_launch_style);
} else {
downloadBtn.setBackgroundResource(R.drawable.game_item_btn_download_style);
}
}
/**
* 获取下载按钮文案
*/
public static String getDownloadBtnText(Context context, GameEntity gameEntity) {
int doneCount = 0; // 下载完成数量
int pluginCount = 0; // 可插件化数量
int updateCount = 0; // 可更新数量
int installCount = 0; // 已安装数量
DownloadEntity downloadEntity;
Object gh_id;
for (ApkEntity apkEntity : gameEntity.getApk()) {
downloadEntity = DownloadManager.getInstance(context).get(apkEntity.getUrl());
if (downloadEntity != null) {
if (downloadEntity.getStatus().equals(DownloadStatus.done)) {
doneCount++;
} else if (downloadEntity.isPluggable()) {
pluginCount++;
} else if (downloadEntity.isUpdate()) {
updateCount++;
}
}
if (PackageManager.isCanUpdate(gameEntity.getId(), apkEntity.getPackageName())) {
updateCount++;
}
if (PackageManager.isInstalled(apkEntity.getPackageName())) {
gh_id = PackageUtils.getMetaData(context, apkEntity.getPackageName(), "gh_id");
if (!PackageUtils.isSignature(context, apkEntity.getPackageName())) {
pluginCount++;
} else if (gh_id == null || gh_id.equals(gameEntity.getId())) {
installCount++;
}
}
}
if (doneCount != 0) {
return "安装";
} else if (pluginCount != 0) {
return "插件化";
} else if (updateCount != 0) {
return "更新";
} else if (installCount != 0) {
return "打开";
} else {
return "下载";
}
}
/**
* 获取GameUpdateEntity
*/
public static GameUpdateEntity getGameUpdateEntity(GameEntity gameEntity, ApkEntity apkEntity) {
GameUpdateEntity gameUpdateEntity = new GameUpdateEntity();
gameUpdateEntity.setId(gameEntity.getId());
gameUpdateEntity.setIcon(gameEntity.getIcon());
gameUpdateEntity.setName(gameEntity.getName());
gameUpdateEntity.setPackageName(apkEntity.getPackageName());
gameUpdateEntity.setSize(apkEntity.getSize());
gameUpdateEntity.setVersion(apkEntity.getVersion());
gameUpdateEntity.setGhVersion(apkEntity.getGhVersion());
gameUpdateEntity.setUrl(apkEntity.getUrl());
gameUpdateEntity.setPlatform(apkEntity.getPlatform());
gameUpdateEntity.setEtag(apkEntity.getEtag());
gameUpdateEntity.setPluggable(true);
gameUpdateEntity.setTag(gameEntity.getTag());
gameUpdateEntity.setBrief(gameEntity.getBrief());
return gameUpdateEntity;
}
/**
* 获取GameUpdateEntity
*/
public static GameUpdateEntity getGameUpdateEntity(GameEntity gameEntity, ApkEntity apkEntity) {
GameUpdateEntity gameUpdateEntity = new GameUpdateEntity();
gameUpdateEntity.setId(gameEntity.getId());
gameUpdateEntity.setIcon(gameEntity.getIcon());
gameUpdateEntity.setName(gameEntity.getName());
gameUpdateEntity.setPackageName(apkEntity.getPackageName());
gameUpdateEntity.setSize(apkEntity.getSize());
gameUpdateEntity.setVersion(apkEntity.getVersion());
gameUpdateEntity.setGhVersion(apkEntity.getGhVersion());
gameUpdateEntity.setUrl(apkEntity.getUrl());
gameUpdateEntity.setPlatform(apkEntity.getPlatform());
gameUpdateEntity.setEtag(apkEntity.getEtag());
gameUpdateEntity.setPluggable(true);
gameUpdateEntity.setTag(gameEntity.getTag());
gameUpdateEntity.setBrief(gameEntity.getBrief());
return gameUpdateEntity;
}
}

View File

@ -3,6 +3,8 @@ package com.gh.common.util;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.View;
import android.widget.LinearLayout;
@ -20,7 +22,6 @@ import java.util.Locale;
import java.util.TimeZone;
/**
*
* @author 温冠超
* @email 294299195@qq.com
* @date 2015-8-14
@ -29,100 +30,104 @@ import java.util.TimeZone;
*/
public class GameViewUtils {
public static void setLabelList(Context context, LinearLayout labelLayout, List<String> tag) {
labelLayout.removeAllViews();
if (tag == null || tag.isEmpty()) {
labelLayout.addView(getGameTagView(context, "官方版", 0));
} else {
for (int i = 0, size = tag.size(); i < size; i++) {
View view;
if (i == size - 1) {
view = getGameTagView(context, tag.get(i), 0);
} else {
view = getGameTagView(context, tag.get(i), DisplayUtils.dip2px(context, 5));
}
if (view != null) {
labelLayout.addView(view);
}
if (labelLayout.getChildCount() == 3) {
break;
}
}
}
}
public static void setLabelList(Context context, LinearLayout labelLayout, List<String> tag, String tagType) {
labelLayout.removeAllViews();
if (tag == null || tag.isEmpty()) {
labelLayout.addView(getGameTagView(context, "官方版", 0, tagType));
} else {
for (int i = 0, size = tag.size(); i < size; i++) {
View view;
if (i == size - 1) {
view = getGameTagView(context, tag.get(i), 0, tagType);
} else {
view = getGameTagView(context, tag.get(i), DisplayUtils.dip2px(context, 6), tagType);
}
if (view != null) {
labelLayout.addView(view);
}
if (labelLayout.getChildCount() == 3) {
break;
}
}
}
}
private static TextView getGameTagView(Context context, String tagStr, int rightMargin) {
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lparams.rightMargin = rightMargin;
TextView tag = new TextView(context);
tag.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
tag.setSingleLine(true);
tag.setText(tagStr);
if ("官方版".equals(tagStr) || "已关注".equals(tagStr)) {
tag.setBackgroundResource(R.drawable.border_green_bg);
tag.setTextColor(context.getResources().getColor(R.color.tag_green));
} else {
String colorStr = TagUtils.getInstance(context).getColor(tagStr);
if (colorStr == null) {
return null;
}
int color = Color.parseColor(colorStr);
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setColor(Color.TRANSPARENT);
gradientDrawable.setStroke(DisplayUtils.dip2px(context, 0.6f), color);
tag.setBackgroundDrawable(gradientDrawable);
private static TextView getGameTagView(Context context, String tagStr, int rightMargin, String tagType) {
LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(
LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lparams.rightMargin = rightMargin;
TextView tag = new TextView(context);
tag.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
tag.setSingleLine(true);
tag.setText(tagStr);
if ("官方版".equals(tagStr) || "已关注".equals(tagStr)) {
tag.setBackgroundResource(R.drawable.border_green_bg);
tag.setTextColor(ContextCompat.getColor(context, R.color.tag_green));
} else {
String colorStr;
if (!TextUtils.isEmpty(tagType) && "type".equals(tagType)) { // 游戏标签
colorStr = "#ff6a28";
} else {
colorStr = TagUtils.getInstance(context).getColor(tagStr);
}
if (colorStr == null) {
return null;
}
int color = Color.parseColor(colorStr);
GradientDrawable gradientDrawable = new GradientDrawable();
gradientDrawable.setColor(Color.TRANSPARENT);
gradientDrawable.setStroke(DisplayUtils.dip2px(context, 0.6f), color);
tag.setBackgroundDrawable(gradientDrawable);
// tag.setBackgroundResource(R.drawable.border_blue_bg);
tag.setTextColor(color);
}
tag.setLayoutParams(lparams);
tag.setPadding(DisplayUtils.dip2px(context, 3),
0,
DisplayUtils.dip2px(context, 3),
DisplayUtils.dip2px(context, 1));
return tag;
}
tag.setTextColor(color);
}
tag.setLayoutParams(lparams);
tag.setPadding(DisplayUtils.dip2px(context, 3),
0,
DisplayUtils.dip2px(context, 3),
DisplayUtils.dip2px(context, 1));
return tag;
}
public static String getGameTestDate(long testTime) {
public static String getGameTestDate(long testTime) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
format.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
format.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
String testDate;
try {
long today = format.parse(format.format(new Date())).getTime();
long day = Long.parseLong(testTime + "000");
Calendar calendar = Calendar.getInstance(TimeZone
.getTimeZone("Asia/Shanghai"));
calendar.setTimeInMillis(day);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
if (day >= today && day < today + 86400 * 1000) {
testDate = "今天" + hour + "";
} else if (day >= today + 86400 * 1000
&& day < today + 86400 * 1000 * 2) {
testDate = "明天" + hour + "";
} else if (day >= today + 86400 * 1000 * 2
&& day < today + 86400 * 1000 * 3) {
testDate = "后天" + hour + "";
} else if (day >= today - 86400 * 1000 && day < today) {
testDate = "昨天" + hour + "";
} else {
format = new SimpleDateFormat("MM-dd", Locale.CHINA);
testDate = format.format(day) + " " + hour + "";
}
return testDate;
} catch (ParseException e) {
e.printStackTrace();
long day = Long.parseLong(testTime + "000");
Calendar calendar = Calendar.getInstance(TimeZone
.getTimeZone("Asia/Shanghai"));
calendar.setTimeInMillis(day);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
format = new SimpleDateFormat("MM-dd", Locale.CHINA);
testDate = format.format(day) + " " + hour + "";
return testDate;
}
String testDate;
try {
long today = format.parse(format.format(new Date())).getTime();
long day = Long.parseLong(testTime + "000");
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm", Locale.CHINA);
String time = timeFormat.format(day);
}
if (day >= today && day < today + 86400 * 1000) {
testDate = "今天 " + time;
} else if (day >= today + 86400 * 1000
&& day < today + 86400 * 1000 * 2) {
testDate = "明天 " + time;
} else if (day >= today + 86400 * 1000 * 2
&& day < today + 86400 * 1000 * 3) {
testDate = "后天 " + time;
} else if (day >= today - 86400 * 1000 && day < today) {
testDate = "昨天 " + time;
} else {
testDate = new SimpleDateFormat("MM-dd HH:mm", Locale.CHINA).format(day);
}
return testDate;
} catch (ParseException e) {
e.printStackTrace();
long day = Long.parseLong(testTime + "000");
Calendar calendar = Calendar.getInstance(TimeZone
.getTimeZone("Asia/Shanghai"));
calendar.setTimeInMillis(day);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
format = new SimpleDateFormat("MM-dd", Locale.CHINA);
testDate = format.format(day) + " " + hour + "";
return testDate;
}
}
}

View File

@ -0,0 +1,299 @@
package com.gh.common.util;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
import com.gh.common.constant.Config;
import com.lightgame.utils.RuntimeUtils;
import com.lightgame.utils.Utils;
import com.sina.weibo.sdk.WbSdk;
import com.sina.weibo.sdk.auth.AuthInfo;
import com.sina.weibo.sdk.auth.Oauth2AccessToken;
import com.sina.weibo.sdk.auth.WbAuthListener;
import com.sina.weibo.sdk.auth.WbConnectErrorMessage;
import com.sina.weibo.sdk.auth.sso.SsoHandler;
import com.tencent.mm.sdk.openapi.IWXAPI;
import com.tencent.mm.sdk.openapi.SendAuth;
import com.tencent.mm.sdk.openapi.WXAPIFactory;
import com.tencent.tauth.IUiListener;
import com.tencent.tauth.Tencent;
import com.tencent.tauth.UiError;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
/**
* Created by khy on 14/06/17.
* <p>
* 获取第三方登录数据
*/
public class GetLoginDataUtils {
private static GetLoginDataUtils instance;
private Context mContext;
private OnLoginDataListener mLoginListener; //登录成功回调
private Tencent mTencent;
private IWXAPI mIWXAPI;
private SsoHandler mSsoHandler;
private Oauth2AccessToken mAccessToken; // weibo
public static final String SCOPE =
"email,direct_messages_read,direct_messages_write,"
+ "friendships_groups_read,friendships_groups_write,statuses_to_me_read,"
+ "follow_app_official_microblog," + "invitation_write"; // weiboCode
private GetLoginDataUtils(Context context) {
mContext = context.getApplicationContext();
mTencent = Tencent.createInstance(Config.TENCENT_APPID, mContext); //初始化QQ分享
mIWXAPI = WXAPIFactory.createWXAPI(mContext, Config.WECHAT_APPID, true); //初始化微信分享
WbSdk.install(context, new AuthInfo(mContext, Config.WEIBO_APPKEY, "http://www.sina.com", SCOPE));
Utils.log(GetLoginDataUtils.class.getSimpleName(), "initLogin");
}
public static GetLoginDataUtils getInstance(Context context) {
if (instance == null) {
instance = new GetLoginDataUtils(context);
}
return instance;
}
//QQ登录回调处理
public IUiListener QqLoginListener = new IUiListener() {
@Override
public void onComplete(Object o) {
Utils.log(GetLoginDataUtils.class.getSimpleName(), "QQ 登录成功");
if (o instanceof JSONObject) {
JSONObject jsonObject = (JSONObject) o;
String s = jsonObject.toString();
Utils.log(GetLoginDataUtils.class.getSimpleName(), "QQLoginComplete::" + s);
try {
mTencent.setOpenId(jsonObject.getString("openid"));
mTencent.setAccessToken(jsonObject.getString("access_token"), jsonObject.getString("expires_in"));
JSONObject content = new JSONObject();
content.put("openid", jsonObject.getString("openid"));
content.put("access_token_expire", Utils.getTime(mContext) + jsonObject.getLong("expires_in"));
content.put("access_token", jsonObject.getString("access_token"));
if (mLoginListener != null) {
mLoginListener.OnLoginData(content, LoginUtils.LoginTag.qq);// QQ 登录回调
}
} catch (JSONException e) {
Utils.log(GetLoginDataUtils.class.getSimpleName(), "QQ登录数据回调异常" + e.toString());
e.printStackTrace();
}
}
// QQToken qqToken = mTencent.getQQToken();
// UserInfo userInfo = new UserInfo(mContext, qqToken);
// userInfo.getUserInfo(new IUiListener() { // 获取QQ用户信息
// @Override
// public void onComplete(Object o) {
// Utils.log(GetLoginDataUtils.class.getSimpleName(), "QQUserInfo::" + o.toString());
// }
//
// @Override
// public void onError(UiError uiError) {
// Utils.log(GetLoginDataUtils.class.getSimpleName(), "QQUserInfoUiError::" + uiError.errorDetail + "==" + uiError.errorMessage + "==" + uiError.errorCode);
// }
//
// @Override
// public void onCancel() {
// Utils.log(GetLoginDataUtils.class.getSimpleName(), "QQUserInfoonCancel");
// }
// });
}
@Override
public void onError(UiError uiError) {
Utils.toast(mContext, "登录失败");
Utils.log(GetLoginDataUtils.class.getSimpleName(), "QQ 登录失败");
}
@Override
public void onCancel() {
Utils.toast(mContext, "登录取消");
Utils.log(GetLoginDataUtils.class.getSimpleName(), "QQ 登录取消");
}
};
public void onQQCallback(int requestCode, int resultCode, Intent data) {
Tencent.onActivityResultData(requestCode, resultCode, data, QqLoginListener);
}
// QQ登录
public void QQLogin(OnLoginDataListener listener, Activity activity) {
mLoginListener = listener;
if (mTencent != null && !mTencent.isSessionValid()) {
Utils.log(GetLoginDataUtils.class.getSimpleName(), "QQLogin");
mTencent.login(activity, "all", QqLoginListener);
}
}
public void QQLogout() {
if (mTencent != null && mTencent.isSessionValid()) {
mTencent.logout(mContext);
}
}
// 微信登录
public void WCLogin(OnLoginDataListener listener) {
mLoginListener = listener;
if (mIWXAPI != null) {
boolean register = mIWXAPI.registerApp(Config.WECHAT_APPID);
SendAuth.Req req = new SendAuth.Req();
req.scope = "snsapi_userinfo";
req.state = "光环助手";
boolean b = mIWXAPI.sendReq(req);
Utils.log(GetLoginDataUtils.class.getSimpleName(), "微信注册状态::" + register + "\n 发送状态::" + b);
if (!register || !b) {
Utils.toast(mContext, "请检查是否安装微信客户端");
}
}
}
public void WCLofinCallBack(JSONObject content) {
mLoginListener.OnLoginData(content, LoginUtils.LoginTag.wechat);
}
public void onWeiboCallback(int requestCode, int resultCode, Intent data) {
if (mSsoHandler != null) {
mSsoHandler.authorizeCallBack(requestCode, resultCode, data);
}
}
// 微博登录
public void WeiBoLogin(OnLoginDataListener listener, Activity context) {
mSsoHandler = new SsoHandler(context);
mLoginListener = listener;
mSsoHandler.authorizeClientSso(new SelfWbAuthListener());
// 第一次启动本应用AccessToken 不可用
mAccessToken = AccessTokenKeeper.readAccessToken(mContext);
// if (mAccessToken.isSessionValid()) {
// updateTokenView(true);
// }
}
// 微博登录回调处理
private class SelfWbAuthListener implements WbAuthListener {
@Override
public void onSuccess(final Oauth2AccessToken token) {
RuntimeUtils.getInstance().runOnUiThread(new Runnable() {
@Override
public void run() {
mAccessToken = token;
if (mAccessToken.isSessionValid()) {
// 显示 Token
// updateTokenView(false);
// 保存 Token 到 SharedPreferences
AccessTokenKeeper.writeAccessToken(mContext, mAccessToken);
Toast.makeText(mContext, "授权成功", Toast.LENGTH_SHORT).show();
}
}
});
JSONObject content = new JSONObject();
try {
content.put("uid", token.getUid());
content.put("access_token", token.getToken());
content.put("access_token_expire", Utils.getTime(mContext) + token.getExpiresTime());
content.put("refresh_token", token.getRefreshToken());
// content.put("refresh_token_expire", Utils.getTime(mContext) + 86400 * 30); // refresh_token 有效期30天
if (mLoginListener != null) {
mLoginListener.OnLoginData(content, LoginUtils.LoginTag.weibo);// 微博 登录回调
}
} catch (JSONException e) {
e.printStackTrace();
}
// AppController.MAIN_EXECUTOR.execute(new Runnable() {
// @Override
// public void run() {
// getWeiBoUserInfo(token.getToken(), token.getUid());
// }
// });
}
@Override
public void cancel() {
Utils.toast(mContext, "取消授权");
}
@Override
public void onFailure(WbConnectErrorMessage errorMessage) {
Utils.toast(mContext, "微博登录需要客户端支持,请先安装微博");
}
}
//微博 获取用户信息
private void getWeiBoUserInfo(String accessToken, String uid) {
String path = "https://api.weibo.com/2/users/show.json?access_token=" + accessToken + "&uid=" + uid;
Utils.log(GetLoginDataUtils.class.getSimpleName(), "getWeiBoUserInfo-url::" + path);
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
conn.setDoInput(true);
int code = conn.getResponseCode();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
Utils.log(GetLoginDataUtils.class.getSimpleName(), "getWeiBoUserInfo-RequestCode::" + code);
if (code == 200) {
InputStream is = conn.getInputStream();
while ((len = is.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
String str = new String(baos.toByteArray());
Utils.log(GetLoginDataUtils.class.getSimpleName(), "getWeiBoUserInfo-Body::" + str);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 微博显示当前 Token 信息。
*
* @param hasExisted 配置文件中是否已存在 token 信息并且合法
*/
private void updateTokenView(boolean hasExisted) {
String date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(
new java.util.Date(mAccessToken.getExpiresTime()));
String format = "Token%1$s \\n有效期%2$s";
String token = String.format(format, mAccessToken.getToken(), date);
Utils.log(GetLoginDataUtils.class.getSimpleName(), "::WB_TOKEN::" + token);
String message = String.format(format, mAccessToken.getToken(), date);
if (hasExisted) {
message = "Token 仍在有效期内,无需再次登录。" + "\n" + message;
}
Utils.log(GetLoginDataUtils.class.getSimpleName(), "::WB_MESSAGE::" + message);
}
// 登录成功回调
public interface OnLoginDataListener {
void OnLoginData(JSONObject content, LoginUtils.LoginTag loginTag);
}
}

View File

@ -0,0 +1,35 @@
package com.gh.common.util
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
/**
* Created by khy on 11/10/17.
*/
class GsonUtils private constructor() {
val mGson: Gson = Gson()
companion object {
fun getInstance(): GsonUtils {
return Inner.anotherSingle
}
private object Inner {
val anotherSingle = GsonUtils()
}
}
fun <T> fromJsonBean(json: String, t: Class<T>): T {
return mGson.fromJson(json, t)
}
fun <T> fromJsonList(json: String, t: Class<T>): T {
val type = object : TypeToken<List<T>>() {}.type
return mGson.fromJson(json, type)
}
fun toJson(any: Any): String {
return mGson.toJson(any)
}
}

View File

@ -10,105 +10,105 @@ import java.util.zip.GZIPOutputStream;
public class GzipUtils {
private final static int BUFFER = 1024;
private final static int BUFFER = 1024;
/*
* 数据压缩
*/
public static void compress(InputStream is, OutputStream os) throws IOException {
GZIPOutputStream gos = null;
try {
gos = new GZIPOutputStream(os);
int count;
byte data[] = new byte[BUFFER];
while ((count = is.read(data, 0, BUFFER)) != -1) {
gos.write(data, 0, count);
}
gos.flush();
gos.finish();
gos.close();
} finally {
if (gos != null) {
gos.close();
}
}
}
public static byte[] compressBytes(byte[] compressed) {
ByteArrayInputStream bais = null;
ByteArrayOutputStream baos = null;
try {
bais = new ByteArrayInputStream(compressed);
baos = new ByteArrayOutputStream();
compress(bais, baos);
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return compressed;
} finally {
if (bais != null) {
try {
bais.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
* 数据压缩
*/
public static void decompress(InputStream is, OutputStream os) throws IOException {
GZIPInputStream gis = null;
try {
gis = new GZIPInputStream(is);
int count;
byte data[] = new byte[BUFFER];
while ((count = gis.read(data, 0, BUFFER)) != -1) {
os.write(data, 0, count);
}
} finally {
if (gis != null) {
gis.close();
}
}
}
/*
* 数据压缩
*/
public static void compress(InputStream is, OutputStream os) throws IOException {
GZIPOutputStream gos = null;
try {
gos = new GZIPOutputStream(os);
int count;
byte data[] = new byte[BUFFER];
while ((count = is.read(data, 0, BUFFER)) != -1) {
gos.write(data, 0, count);
}
gos.flush();
gos.finish();
gos.close();
} finally {
if (gos != null) {
gos.close();
}
}
}
public static byte[] compressBytes(byte[] compressed) {
ByteArrayInputStream bais = null;
ByteArrayOutputStream baos = null;
try {
bais = new ByteArrayInputStream(compressed);
baos = new ByteArrayOutputStream();
compress(bais, baos);
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return compressed;
} finally {
if (bais != null) {
try {
bais.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static byte[] decompressBytes(byte[] compressed) {
ByteArrayInputStream bais = null;
ByteArrayOutputStream baos = null;
try {
bais = new ByteArrayInputStream(compressed);
baos = new ByteArrayOutputStream();
decompress(bais, baos);
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return compressed;
} finally {
if (bais != null) {
try {
bais.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static byte[] decompressBytes(byte[] compressed) {
ByteArrayInputStream bais = null;
ByteArrayOutputStream baos = null;
try {
bais = new ByteArrayInputStream(compressed);
baos = new ByteArrayOutputStream();
decompress(bais, baos);
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return compressed;
} finally {
if (bais != null) {
try {
bais.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (baos != null) {
try {
baos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
* 数据解压缩
*/
public static void decompress(InputStream is, OutputStream os) throws IOException {
GZIPInputStream gis = null;
try {
gis = new GZIPInputStream(is);
int count;
byte data[] = new byte[BUFFER];
while ((count = gis.read(data, 0, BUFFER)) != -1) {
os.write(data, 0, count);
}
} finally {
if (gis != null) {
gis.close();
}
}
}
}

View File

@ -0,0 +1,36 @@
package com.gh.common.util;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class HMACUtils {
public static String encrypt(String data, String key) {
try {
SecretKeySpec signingKey = new SecretKeySpec(key.getBytes(), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
return byte2hex(mac.doFinal(data.getBytes()));
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
e.printStackTrace();
}
return null;
}
private static String byte2hex(byte[] ciphertext) {
StringBuilder builder = new StringBuilder();
String stmp;
for (int i = 0; ciphertext != null && i < ciphertext.length; i++) {
stmp = Integer.toHexString(ciphertext[i] & 0XFF);
if (stmp.length() == 1) {
builder.append('0');
}
builder.append(stmp);
}
return builder.toString().toLowerCase();
}
}

View File

@ -41,6 +41,27 @@ public final class HotFix {
}
}
private static void injectInAliyunOs(Context context, String patchDexFile, String patchClassName)
throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException,
InstantiationException, NoSuchFieldException {
PathClassLoader obj = (PathClassLoader) context.getClassLoader();
String replaceAll = new File(patchDexFile).getName().replaceAll("\\.[a-zA-Z0-9]+", ".lex");
Class cls = Class.forName("dalvik.system.LexClassLoader");
Object newInstance =
cls.getConstructor(new Class[]{String.class, String.class, String.class, ClassLoader.class}).newInstance(
new Object[]{context.getDir("dex", 0).getAbsolutePath() + File.separator + replaceAll,
context.getDir("dex", 0).getAbsolutePath(), patchDexFile, obj});
cls.getMethod("loadClass", new Class[]{String.class}).invoke(newInstance, new Object[]{patchClassName});
setField(obj, PathClassLoader.class, "mPaths",
appendArray(getField(obj, PathClassLoader.class, "mPaths"), getField(newInstance, cls, "mRawDexPath")));
setField(obj, PathClassLoader.class, "mFiles",
combineArray(getField(obj, PathClassLoader.class, "mFiles"), getField(newInstance, cls, "mFiles")));
setField(obj, PathClassLoader.class, "mZips",
combineArray(getField(obj, PathClassLoader.class, "mZips"), getField(newInstance, cls, "mZips")));
setField(obj, PathClassLoader.class, "mLexs",
combineArray(getField(obj, PathClassLoader.class, "mLexs"), getField(newInstance, cls, "mDexs")));
}
private static boolean hasDexClassLoader() {
try {
Class.forName("dalvik.system.BaseDexClassLoader");
@ -50,85 +71,66 @@ public final class HotFix {
}
}
private static void injectInAliyunOs(Context context, String patchDexFile, String patchClassName)
throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException,
InstantiationException, NoSuchFieldException {
PathClassLoader obj = (PathClassLoader) context.getClassLoader();
String replaceAll = new File(patchDexFile).getName().replaceAll("\\.[a-zA-Z0-9]+", ".lex");
Class cls = Class.forName("dalvik.system.LexClassLoader");
Object newInstance =
cls.getConstructor(new Class[] {String.class, String.class, String.class, ClassLoader.class}).newInstance(
new Object[] {context.getDir("dex", 0).getAbsolutePath() + File.separator + replaceAll,
context.getDir("dex", 0).getAbsolutePath(), patchDexFile, obj});
cls.getMethod("loadClass", new Class[] {String.class}).invoke(newInstance, new Object[] {patchClassName});
setField(obj, PathClassLoader.class, "mPaths",
appendArray(getField(obj, PathClassLoader.class, "mPaths"), getField(newInstance, cls, "mRawDexPath")));
setField(obj, PathClassLoader.class, "mFiles",
combineArray(getField(obj, PathClassLoader.class, "mFiles"), getField(newInstance, cls, "mFiles")));
setField(obj, PathClassLoader.class, "mZips",
combineArray(getField(obj, PathClassLoader.class, "mZips"), getField(newInstance, cls, "mZips")));
setField(obj, PathClassLoader.class, "mLexs",
combineArray(getField(obj, PathClassLoader.class, "mLexs"), getField(newInstance, cls, "mDexs")));
}
@TargetApi(14)
private static void injectBelowApiLevel14(Context context, String str, String str2)
throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
PathClassLoader obj = (PathClassLoader) context.getClassLoader();
DexClassLoader dexClassLoader =
new DexClassLoader(str, context.getDir("dex", 0).getAbsolutePath(), str, context.getClassLoader());
dexClassLoader.loadClass(str2);
setField(obj, PathClassLoader.class, "mPaths",
appendArray(getField(obj, PathClassLoader.class, "mPaths"), getField(dexClassLoader, DexClassLoader.class,
"mRawDexPath")
));
setField(obj, PathClassLoader.class, "mFiles",
combineArray(getField(obj, PathClassLoader.class, "mFiles"), getField(dexClassLoader, DexClassLoader.class,
"mFiles")
));
setField(obj, PathClassLoader.class, "mZips",
combineArray(getField(obj, PathClassLoader.class, "mZips"), getField(dexClassLoader, DexClassLoader.class,
"mZips")));
setField(obj, PathClassLoader.class, "mDexs",
combineArray(getField(obj, PathClassLoader.class, "mDexs"), getField(dexClassLoader, DexClassLoader.class,
"mDexs")));
obj.loadClass(str2);
}
private static void injectAboveEqualApiLevel14(Context context, String str, String str2)
throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
PathClassLoader pathClassLoader = (PathClassLoader) context.getClassLoader();
Object a = combineArray(getDexElements(getPathList(pathClassLoader)),
getDexElements(getPathList(
new DexClassLoader(str, context.getDir("dex", 0).getAbsolutePath(), str, context.getClassLoader()))));
getDexElements(getPathList(
new DexClassLoader(str, context.getDir("dex", 0).getAbsolutePath(), str, context.getClassLoader()))));
Object a2 = getPathList(pathClassLoader);
setField(a2, a2.getClass(), "dexElements", a);
pathClassLoader.loadClass(str2);
}
private static Object getPathList(Object obj) throws ClassNotFoundException, NoSuchFieldException,
IllegalAccessException {
return getField(obj, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
}
private static Object getDexElements(Object obj) throws NoSuchFieldException, IllegalAccessException {
return getField(obj, obj.getClass(), "dexElements");
}
private static Object getField(Object obj, Class cls, String str)
throws NoSuchFieldException, IllegalAccessException {
Field declaredField = cls.getDeclaredField(str);
declaredField.setAccessible(true);
return declaredField.get(obj);
@TargetApi(14)
private static void injectBelowApiLevel14(Context context, String str, String str2)
throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException {
PathClassLoader obj = (PathClassLoader) context.getClassLoader();
DexClassLoader dexClassLoader =
new DexClassLoader(str, context.getDir("dex", 0).getAbsolutePath(), str, context.getClassLoader());
dexClassLoader.loadClass(str2);
setField(obj, PathClassLoader.class, "mPaths",
appendArray(getField(obj, PathClassLoader.class, "mPaths"), getField(dexClassLoader, DexClassLoader.class,
"mRawDexPath")
));
setField(obj, PathClassLoader.class, "mFiles",
combineArray(getField(obj, PathClassLoader.class, "mFiles"), getField(dexClassLoader, DexClassLoader.class,
"mFiles")
));
setField(obj, PathClassLoader.class, "mZips",
combineArray(getField(obj, PathClassLoader.class, "mZips"), getField(dexClassLoader, DexClassLoader.class,
"mZips")));
setField(obj, PathClassLoader.class, "mDexs",
combineArray(getField(obj, PathClassLoader.class, "mDexs"), getField(dexClassLoader, DexClassLoader.class,
"mDexs")));
obj.loadClass(str2);
}
private static void setField(Object obj, Class cls, String str, Object obj2)
throws NoSuchFieldException, IllegalAccessException {
throws NoSuchFieldException, IllegalAccessException {
Field declaredField = cls.getDeclaredField(str);
declaredField.setAccessible(true);
declaredField.set(obj, obj2);
}
private static Object appendArray(Object obj, Object obj2) {
Class componentType = obj.getClass().getComponentType();
int length = Array.getLength(obj);
Object newInstance = Array.newInstance(componentType, length + 1);
Array.set(newInstance, 0, obj2);
for (int i = 1; i < length + 1; i++) {
Array.set(newInstance, i, Array.get(obj, i - 1));
}
return newInstance;
}
private static Object getField(Object obj, Class cls, String str)
throws NoSuchFieldException, IllegalAccessException {
Field declaredField = cls.getDeclaredField(str);
declaredField.setAccessible(true);
return declaredField.get(obj);
}
private static Object combineArray(Object obj, Object obj2) {
Class componentType = obj2.getClass().getComponentType();
int length = Array.getLength(obj2);
@ -144,15 +146,13 @@ public final class HotFix {
return newInstance;
}
private static Object appendArray(Object obj, Object obj2) {
Class componentType = obj.getClass().getComponentType();
int length = Array.getLength(obj);
Object newInstance = Array.newInstance(componentType, length + 1);
Array.set(newInstance, 0, obj2);
for (int i = 1; i < length + 1; i++) {
Array.set(newInstance, i, Array.get(obj, i - 1));
}
return newInstance;
private static Object getDexElements(Object obj) throws NoSuchFieldException, IllegalAccessException {
return getField(obj, obj.getClass(), "dexElements");
}
private static Object getPathList(Object obj) throws ClassNotFoundException, NoSuchFieldException,
IllegalAccessException {
return getField(obj, Class.forName("dalvik.system.BaseDexClassLoader"), "pathList");
}
}

View File

@ -1,126 +0,0 @@
package com.gh.common.util;
import android.content.Context;
import com.gh.gamecenter.R;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.internal.tls.OkHostnameVerifier;
/**
* Created by khy on 2016/10/8.
*
*/
public class HttpsUtils {
private static final TrustManager[] TRUST_MANAGERS = new TrustManager[] {
new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
};
private static final HostnameVerifier HOSTNAME_VERIFIER = new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
private static SSLSocketFactory mSSLSocketFactory;
private static HostnameVerifier mHostnameVerifier;
public static void initHttpsUrlConnection(Context context) {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyStore keyStore = getHttpsKeyStore(context);
if (keyStore != null) {
String algorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(algorithm);
tmf.init(keyStore);
sslContext.init(null, tmf.getTrustManagers(), null);
mHostnameVerifier = OkHostnameVerifier.INSTANCE;
} else {
sslContext.init(null, TRUST_MANAGERS, null);
mHostnameVerifier = HOSTNAME_VERIFIER;
}
mSSLSocketFactory = sslContext.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
}
}
private static KeyStore getHttpsKeyStore(Context context) {
InputStream is = null;
try {
is = context.getResources().openRawResource(R.raw.cacert);
//读取证书
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
certificateFactory.generateCertificate(is);
Certificate certificate = certificateFactory.generateCertificate(is);
//创建一个证书库,并将证书导入证书库
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("trust", certificate);
return keyStore;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
public static HttpsURLConnection getHttpsURLConnection(URL url) throws Exception {
HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();
if (mSSLSocketFactory == null || mHostnameVerifier == null) {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, TRUST_MANAGERS, null);
mSSLSocketFactory = sslContext.getSocketFactory();
mHostnameVerifier = HOSTNAME_VERIFIER;
}
httpsURLConnection.setSSLSocketFactory(mSSLSocketFactory);
httpsURLConnection.setHostnameVerifier(mHostnameVerifier);
return httpsURLConnection;
}
}

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