diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..c274494b --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,13 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +custom: https://goweii.github.io/resource/payment_code/all_qrcode.jpg diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 00000000..80698663 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,26 @@ +name: Android CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: set up JDK 11 + uses: actions/setup-java@v2 + with: + java-version: '11' + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + - name: Build with Gradle + run: ./gradlew build diff --git a/.gitignore b/.gitignore index 5742df65..b3914263 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ ### Android template # Built application files app/release/ +app/com/ +app/per/ +app/googlePlay/ *.apk *.ap_ @@ -17,7 +20,6 @@ gen/ out/ # Gradle files -gradle/ .gradle/ build/ @@ -39,13 +41,6 @@ captures/ # IntelliJ *.iml .idea/ -.idea/workspace.xml -.idea/tasks.xml -.idea/gradle.xml -.idea/assetWizardSettings.xml -.idea/dictionaries -.idea/libraries -.idea/caches # Keystore files # Uncomment the following line if you do not want to check your keystore files in. @@ -69,3 +64,8 @@ fastlane/screenshots fastlane/test_output fastlane/readme.md +# WanCDKey +WanCDKey.java + +# bugly +app/bugly/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..de64d306 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,478 @@ +# 更新日志 + +## 2.3.0(83) + +- 【新增】新增英/法/俄/西班牙/阿拉伯语 + +## 2.2.9(82) + +- 【优化】优化使用体验 + +## 2.2.8(81) + +- 【修复】修复一些已知问题 + +## 2.2.7(80) + +- 【修复】修复一些已知问题 + +## 2.2.6(79) + +- 【修复】修复一些已知问题 + +## 2.2.5(78) + +- 【修复】修复部分主题文字颜色错误 + +## 2.2.4(77) + +- 【新增】修复WebInstance缓存内存泄露 +- 【遗留】其实还有内存泄露,留给有缘人发现 + +## 2.2.3(76) + +- 【优化】一些优化 + +## 2.2.2(75) + +- 【优化】一些优化 + +## 2.2.1(74) + +- 【新增】教程新增学习记录 + +## 2.2.0(73) + +- 【新增】二楼修改为书籍教程 +- 【修复】修复网页阅读模式失效 +- 【修复】修复关于我等接口失效 +- 【修复】修复首次下拉刷新未显示诗词问题 + +## 2.1.1(72) + +- 【修复】修复搜索偶发崩溃 + +## 2.1.0(71) + +- 【优化】移除评论系统 +- 【修复】修复一些BUG + +## 2.0.18(70) + +- 【修复】修复一大堆BUG + +## 2.0.17(69) + +- 【优化】优化文章分享卡片 +- 【优化】删除一些无用设置项 +- 【修复】修复已知BUG + +## 2.0.16(68) + +- 【优化】优化文章分享卡片 +- 【优化】优化体验 + +## 2.0.15(67) + +- 【修复】修复一些BUG + +## 2.0.14(66) + +- 【修复】修复底部弹窗被虚拟按键遮挡问题 + +## 2.0.13(65) + +- 【优化】替换消息相关接口 +- 【新增】桌面图标和启动图跟随主题更换 + +## 2.0.12(64) + +- 【修复】修复上版本写的新BUG + +## 2.0.11(63) + +- 【优化】移除首页导航栏高斯模糊效果 +- 【优化】优化启动弹窗大小 + +## 2.0.10(62) + +- 【修复】可能修复了一些BUG +- 【新增】生物认证登录,重新登录可开启 + +## 2.0.9(61) + +- 【修复】可能修复了一些BUG + +## 2.0.8(60) + +- 【优化】优化首页二楼 + +## 2.0.7(59) + +- 【新增】新增首页二楼,快捷查看书签 +- 【优化】优化文章抽屉,避免遮挡网页 + +## 2.0.6(58) + +- 【优化】优化文章加载速度 +- 【优化】优化阅读模式,适配知乎 + +## 2.0.5(57) + +- 【优化】优化文章加载速度 + +## 2.0.4(56) + +- 【修复】掘金文章阅读模式失效问题 + +## 2.0.3(55) + +- 【新增】动态主题(重要节日自动开启) +- 【优化】优化首页标题栏Icon自动根据背景变色 +- 【修复】修复一些BUG,可能也增加了一些BUG + +## 2.0.2(54) + +- 【修复】修复首页更新点击无反应BUG + +## 2.0.1(53) + +- 【修复】修复一些BUG + +## 2.0.0(52) + +- 【注意】因包名被恶意APP使用上架到小米商店,虽已经下架但无法申诉,安装会有高危提醒。因此本次更新将修改包名,且为强制更新 +- 【新增】网页视频支持全屏播放 +- 【新增】启动提醒最新加入的稍后阅读 +- 【新增】文章阅读完自动移出稍后阅读 +- 【新增】文章详情支持增删稍后阅读 +- 【优化】文章详情分享改为二维码分享 +- 【修复】修复一些BUG + +## 1.6.1(51) + +- 【修复】部分机型扫码崩溃BUG + +## 1.6.0(50) + +- 【优化】扫码 + +## 1.5.7(49) + +- 【优化】文章详情页显示 + +## 1.5.6(48) + +- 【新增】文章评论,需重新登录 +- 【新增】个人头像和封面修改 +- 【优化】优化暗色模式UI显示 +- 【感谢】感谢参与内测的小伙伴 + +## 1.5.5(47) + +- 【新增】消息提醒,不用登陆网页端也可查看最新消息 +- 【优化】每次下拉刷新显示不一样的古诗句 + +## 1.5.4(46) + +- 【修复】文章详情偶发崩溃BUG +- 【修复】文章大图预览滑动关闭偶发崩溃BUG + +## 1.5.3(45) + +- 【修复】修复上版本网页注入调试BUG + +## 1.5.2(44) + +- 【修复】修复上版本版本更新BUG + +## 1.5.1(43) + +- 【停更】APP功能已基本完善且趋于稳定,无重大BUG和需求将持续停更。 +- 【内测】接下来将转内测更新测试新功能,待稳定后再发布正式版,想尝鲜的小伙伴可加群(见关于作者)看公告 +- 【新增】简书/掘金/微信公众号/CSDN文章详情页精简显示内容 +- 【新增】内测更新通道 +- 【新增】网页支持启动第三方APP和下载 + +## 1.5.0(42) + +- 【停更】APP功能已基本完善且趋于稳定,无重大BUG和需求将持续停更 +- 【新增】主页Tab改版,新增问答删除公众号和项目 +- 【新增】列表分类标签支持跳转 +- 【优化】网页大图查看增加加载进度 +- 【修复】一些已知BUG + +## 1.4.6(41) + +- 【停更】APP功能已基本完善且趋于稳定,无重大BUG和需求将持续停更 +- 【新增】新增稍后阅读 +- 【新增】新增阅读历史 +- 【修复】一些已知BUG + +## 1.4.5(40) + +- 【感谢】感谢群里大佬们帮忙测试 +- 【优化】文章详情页增加悬浮返回按钮 +- 【优化】网页查看大图改为长按触发 +- 【优化】更精致的网页暗色模式 +- 【优化】扫码界面优化 +- 【优化】图片选择采用系统原生实现 +- 【优化】崩溃界面增加复制日志 +- 【修复】一些已知BUG +- 【其他】迁移至AndroidX + +## 1.4.4(39) + +- 【修复】打开偶发文章崩溃BUG +- 【修复】简书等部分网页图片无法大图预览BUG + +## 1.4.3(38) + +- 【新增】二维码/条码扫一扫 +- 【新增】全新文章详情页,优化文章显示 +- 【新增】网页图片支持大图预览 +- 【优化】文章列表显示和刷新 +- 【修复】一些已知BUG + +## 1.4.2(37) + +- 【新增】暗色模式跟随系统 +- 【修复】部分网页内链接点击无效问题 +- 【优化】移除内嵌广告 +- 【优化】文章分享界面发布按钮位置 +- 【优化】文章二维码分享卡片样式 +- 【优化】首页弹窗重叠问题 + +## 1.4.1(36) + +- 【新增】更多玩口令 +- 【修复】搜索闪退 +- 【修复】我的分享页无法取消收藏 +- 【修复】其他已知BUG + +## 1.4.0(35) + +- 【新增】网页支持暗色模式 +- 【新增】有趣的玩口令等你发现 +- 【新增】加入了一些广告,实属迫于无奈 +- 【修复】个人收藏缓存冲突问题 +- 【修复】其他已知BUG + +## 1.3.3(34) + +- 【修复】登录页软键盘遮挡 +- 【修复】首次启动闪屏 +- 【修复】已知BUG +- 【优化】体系页面文字颜色 + +## 1.3.2(33) + +- 【新增】依据最新的监管要求更新了《隐私政策》 +- 【优化】弹窗动效 +- 【优化】优化体验 + +## 1.3.1(32) + +- 【修复】修复部分机型1.3.0版本启动崩溃 + +## 1.3.0(31) + +- 【优化】暗黑模式配色 +- 【优化】更换腾讯X5内核 + +## 1.2.7(30) + +- 【优化】优化网页菜单,复制链接和浏览器打开移至地址栏快捷菜单 +- 【优化】优化文章二维码分享 +- 【修复】修复他人主页无分享文章时的显示问题 + +## 1.2.6(29) + +- 【新增】网页长图和二维码分享 +- 【新增】关于作者QQ群点击直接跳转 +- 【新增】关于作者二维码长按保存 +- 【优化】广场移至首页负一屏 +- 【优化】网页双击收藏动画仿抖音 +- 【修复】暗色模式切换偶发崩溃 +- 【修复】修复网页双击收藏崩溃 +- 【修复】修复其他已知BUG + +## 1.2.5(28) + +- 【优化】冷启动速度 +- 【修复】部分机型启动闪退 + +## 1.2.4(27) + +- 【新增】BUG统计 +- 【新增】崩溃拦截 + +## 1.2.3(26) + +- 【优化】网页返回键可关闭网页 +- 【优化】已复制链接检测弹窗交互 +- 【优化】他人主页头部可折叠 +- 【优化】文章描述文本显示 + +## 1.2.2(25) + +- 【新增】网页拦截域名自定义 +- 【新增】检测已复制链接提示打开 +- 【优化】列表文章有描述时标题单行显示 +- 【优化】用户主页避免重复打开 +- 【优化】网页菜单弹窗样式 +- 【优化】暗色模式切换体验 +- 【修复】收藏网址页面无法右滑 +- 【修复】小屏手机我的页面显示不全 + +## 1.2.1(24) + +- 【新增】我的分享 +- 【新增】分享文章 +- 【新增】支持第三方APP文章一键分享到广场 +- 【优化】文章列表布局 +- 【修复】部分颜色显示错误 +- 【修复】我的收藏红心状态错误 + +## 1.2.0(23) + +- 【新增】暗色模式,可前往设置开启 +- 【新增】广场及用户主页 +- 【新增】网页可编辑网址 +- 【新增】版本更新添加备用链接 +- 【优化】搜索历史列表布局 +- 【优化】登录页进出场动画 +- 【修复】列表分页BUG + +## 1.1.8(22) + +- 【备注】祖国生日快乐 +- 【新增】首页新增广场入口 +- 【优化】网页按钮移至底部 +- 【优化】弹窗弹出位置 +- 【优化】移除配置接口时间限制 + +## 1.1.7(21) + +- 【新增】首页标题栏动态适配国庆 +- 【优化】标题栏Loading颜色 +- 【优化】我的页面支持下拉刷新 +- 【修复】部分文章不显示作者 + +## 1.1.6(20) + +- 【优化】屏幕旋转后弹窗适配 +- 【修复】修复后台打开返回首页 +- 【修复】权限重复申请 + +## 1.1.5(19) + +- 【新增】支持被分享网页 +- 【新增】响应在浏览器打开 +- 【修复】稍后阅读无法点击 +- 【优化】完善开源列表 +- 【优化】收藏红心 + +## 1.1.4(18) + +- 【新增】自动登录WanAndroid网页 +- 【新增】关于作者新增QQ和QQ群 +- 【新增】关于我们页面新增项目仓库 +- 【新增】网页双击收藏动画 +- 【优化】概览页动效 + +## 1.1.3(17) + +- 【优化】恶意网址拦截,可前往设置开启 +- 【优化】概览页动效 +- 【修复】清除缓存BUG + +## 1.1.2(16) + +- 【新增】恶意网址拦截,可前往设置开启(比较仓促,后续完善) +- 【修复】一些BUG,可能包括8.0闪退 +- 【修复】转屏后弹窗布局错位 +- 【优化】关于作者页面文案 + +## 1.1.1(15) + +- 【新增】文章概览自动加载分页 +- 【新增】文章概览双击收藏 +- 【新增】我的页面显示积分排名 +- 【新增】我的积分可查看积分规则 +- 【优化】搜索历史为空时隐藏 +- 【优化】积分榜移至我的页面 +- 【修复】收藏列表刷新BUG + +## 1.1.0(14) + +- 新增我的积分和积分排行榜 +- 新增网页双击收藏 +- 新增首页文章长按快速预览 +- 优化我的页面布局 +- 优化部分图标颜色 +- 修复列表首次加载BUG + +## 1.0.12(13) + +- 优化懒加载后首次显示过慢问题 + +## 1.0.11(12) + +- 新增积分和等级显示 + +## 1.0.10(11) + +- 修复上版本造成的一些BUG + +## 1.0.9(10) + +- 修改应用启动图标 +- 修改主题色为Android10官方蓝 +- 修改首页仅在每天首次启动进行版本检查 +- 修改版本暂不更新时长为一周 +- 新增Banner显示开关 +- 优化所有Tab页面懒加载 + +## 1.0.8(9) + +- 优化网页滑动返回 +- 优化搜索体验 + +## 1.0.7(8) + +- 修复滑动返回的BUG + +## 1.0.6(7) + +- 搜索记录去重 +- 支持更换个人头像 +- 支持更换个人中心背景图 + +## 1.0.5(6) + +- 优化双击列表回顶体验 +- 新增设置网页仅边缘可滑动返回,避免内部滑动冲突 + +## 1.0.4(5) + +- 修复缓存分页导致数据错乱BUG + +## 1.0.3(4) + +- 压缩APK安装包大小 + +## 1.0.2(3) + +- 新增稍后阅读 + +## 1.0.1(2) + +- 添加滑动返回 +- 优化加载速度 +- 修复BUG + +## 1.0.0(1) + +- 第一版试水 + diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..18c91471 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..f3b72e2c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,24 @@ + +# 如何参与 + +## 发现一个bug + +你可以提交一个issues。格式可以像下面这样: + +> BUG描述:搜索的时候崩溃了 +> +> 应用版本:2.1.0 +> +> 报错信息: +> java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 17(offset:17).state:32 +> at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4641) +> at android.support.v7.widget. +> ... + +## 有一个优化点 + +你可以提交一个issues。 + +## Java或Kotlin分支语言翻译 + +fork并提交pr diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..30448207 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1 @@ +待补充 diff --git a/README.md b/README.md index 80e39557..e6262b33 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # WanAndroid -[WanAndroid](https://www.wanandroid.com/)官方内链APP,最美玩安卓客户端 +[WanAndroid](https://wanandroid.com/)|最美玩安卓客户端 -- [Wanandroid主页](https://www.wanandroid.com/blog/show/2577) +- [WanAndroid主页](https://wanandroid.com/blog/show/2577) - [GitHub主页](https://github.com/goweii/WanAndroid) -- [酷安主页](https://www.coolapk.com/apk/231591) +- [酷安主页](https://www.coolapk.com/apk/231591) @@ -18,36 +18,33 @@ # 功能及特色 -1. [玩Android开放API](https://www.wanandroid.com/blog/show/2)中除TODO的所有功能 -2. 简洁美观的UI设计 -3. 暗黑模式 -4. 滑动返回 -5. 稍后阅读 -6. 恶意网址拦截 -7. 版本更新 -8. 离线缓存 -9. 更换头像和个人中心背景 -10. 等级积分和排名显示 -11. 首页文章概览 -12. 用户主页 -13. 广场 -14. 文章分享(支持一键分享) +1. [玩Android开放API](https://wanandroid.com/blog/show/2)中除TODO的所有功能 +2. 简洁美观的UI设计,高品质暗黑模式 +3. 全局滑动返回 +4. 书签/阅读历史 +5. 恶意网址拦截 +6. 版本更新 +7. 离线缓存 +8. 更换头像和个人中心背景 +9. 等级/积分/排名 +10. 用户主页 +11. 广场/文章分享(支持由第三方APP一键分享) +12. 精简版文章详情页 +13. 消息提醒 # APP下载 -- [酷安下载](https://www.coolapk.com/apk/231591) - [GitHub下载](https://github.com/goweii/WanAndroid/releases) # 截图展示 -| ![1.jpg](https://upload-images.jianshu.io/upload_images/9231307-f8720505b7ceefb1.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) | ![4.jpg](https://upload-images.jianshu.io/upload_images/9231307-b4787c2cffda5587.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) | -| ------------------------------------------------------------ | ------------------------------------------------------------ | -| ![2.jpg](https://upload-images.jianshu.io/upload_images/9231307-173f647644377fbd.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) | ![5.jpg](https://upload-images.jianshu.io/upload_images/9231307-b15e62aa717585ad.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) | -| ![3.jpg](https://upload-images.jianshu.io/upload_images/9231307-4c99a7561fa76317.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) | ![6.jpg](https://upload-images.jianshu.io/upload_images/9231307-8a09ef4d98cea3ba.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) | +| ![1.jpg](https://gitee.com/goweii/pic/raw/master/default/20210320121732.jpeg) | ![2.jpg](https://gitee.com/goweii/pic/raw/master/default/20210320121719.jpeg) | ![3.jpg](https://gitee.com/goweii/pic/raw/master/default/20210320121749.jpeg) | +|-------------------------------------------------------------------------------|-------------------------------------------------------------------------------|-------------------------------------------------------------------------------| +| ![4.jpg](https://gitee.com/goweii/pic/raw/master/default/20210320121742.jpeg) | ![5.jpg](https://gitee.com/goweii/pic/raw/master/default/20210320121800.jpeg) | ![6.jpg](https://gitee.com/goweii/pic/raw/master/default/20210320121855.jpeg) | @@ -84,208 +81,4 @@ # 更新日志 -## 1.3.1(32) - -- 【修复】修复部分机型1.3.0版本启动崩溃 - -## 1.3.0(31) - -- 【优化】暗黑模式配色 -- 【优化】更换腾讯X5内核 - -## 1.2.7(30) - -- 【优化】优化网页菜单,复制链接和浏览器打开移至地址栏快捷菜单 -- 【优化】优化文章二维码分享 -- 【修复】修复他人主页无分享文章时的显示问题 - -## 1.2.6(29) - -- 【新增】网页长图和二维码分享 -- 【新增】关于作者QQ群点击直接跳转 -- 【新增】关于作者二维码长按保存 -- 【优化】广场移至首页负一屏 -- 【优化】网页双击收藏动画仿抖音 -- 【修复】暗色模式切换偶发崩溃 -- 【修复】修复网页双击收藏崩溃 -- 【修复】修复其他已知BUG - -## 1.2.5(28) - -- 【优化】冷启动速度 -- 【修复】部分机型启动闪退 - -## 1.2.4(27) - -- 【新增】BUG统计 -- 【新增】崩溃拦截 - -## 1.2.3(26) - -- 【优化】网页返回键可关闭网页 -- 【优化】已复制链接检测弹窗交互 -- 【优化】他人主页头部可折叠 -- 【优化】文章描述文本显示 - -## 1.2.2(25) - -- 【新增】网页拦截域名自定义 -- 【新增】检测已复制链接提示打开 -- 【优化】列表文章有描述时标题单行显示 -- 【优化】用户主页避免重复打开 -- 【优化】网页菜单弹窗样式 -- 【优化】暗色模式切换体验 -- 【修复】收藏网址页面无法右滑 -- 【修复】小屏手机我的页面显示不全 - -## 1.2.1(24) - -- 【新增】我的分享 -- 【新增】分享文章 -- 【新增】支持第三方APP文章一键分享到广场 -- 【优化】文章列表布局 -- 【修复】部分颜色显示错误 -- 【修复】我的收藏红心状态错误 - -## 1.2.0(23) - -- 【新增】暗色模式,可前往设置开启 -- 【新增】广场及用户主页 -- 【新增】网页可编辑网址 -- 【新增】版本更新添加备用链接 -- 【优化】搜索历史列表布局 -- 【优化】登录页进出场动画 -- 【修复】列表分页BUG - -## 1.1.8(22) - -- 【备注】祖国生日快乐 -- 【新增】首页新增广场入口 -- 【优化】网页按钮移至底部 -- 【优化】弹窗弹出位置 -- 【优化】移除配置接口时间限制 - -## 1.1.7(21) - -- 【新增】首页标题栏动态适配国庆 -- 【优化】标题栏Loading颜色 -- 【优化】我的页面支持下拉刷新 -- 【修复】部分文章不显示作者 - -## 1.1.6(20) - -- 【优化】屏幕旋转后弹窗适配 -- 【修复】修复后台打开返回首页 -- 【修复】权限重复申请 - -## 1.1.5(19) - -- 【新增】支持被分享网页 -- 【新增】响应在浏览器打开 -- 【修复】稍后阅读无法点击 -- 【优化】完善开源列表 -- 【优化】收藏红心 - -## 1.1.4(18) - -- 【新增】自动登录WanAndroid网页 -- 【新增】关于作者新增QQ和QQ群 -- 【新增】关于我们页面新增项目仓库 -- 【新增】网页双击收藏动画 -- 【优化】概览页动效 - -## 1.1.3(17) - -- 【优化】恶意网址拦截,可前往设置开启 -- 【优化】概览页动效 -- 【修复】清除缓存BUG - -## 1.1.2(16) - -- 【新增】恶意网址拦截,可前往设置开启(比较仓促,后续完善) -- 【修复】一些BUG,可能包括8.0闪退 -- 【修复】转屏后弹窗布局错位 -- 【优化】关于作者页面文案 - -## 1.1.1(15) - -- 【新增】文章概览自动加载分页 -- 【新增】文章概览双击收藏 -- 【新增】我的页面显示积分排名 -- 【新增】我的积分可查看积分规则 -- 【优化】搜索历史为空时隐藏 -- 【优化】积分榜移至我的页面 -- 【修复】收藏列表刷新BUG - -## 1.1.0(14) - -- 新增我的积分和积分排行榜 -- 新增网页双击收藏 -- 新增首页文章长按快速预览 -- 优化我的页面布局 -- 优化部分图标颜色 -- 修复列表首次加载BUG - -## 1.0.12(13) - -- 优化懒加载后首次显示过慢问题 - -## 1.0.11(12) - -- 新增积分和等级显示 - -## 1.0.10(11) - -- 修复上版本造成的一些BUG - -## 1.0.9(10) - -- 修改应用启动图标 -- 修改主题色为Android10官方蓝 -- 修改首页仅在每天首次启动进行版本检查 -- 修改版本暂不更新时长为一周 -- 新增Banner显示开关 -- 优化所有Tab页面懒加载 - -## 1.0.8(9) - -- 优化网页滑动返回 -- 优化搜索体验 - -## 1.0.7(8) - -- 修复滑动返回的BUG - -## 1.0.6(7) - -- 搜索记录去重 -- 支持更换个人头像 -- 支持更换个人中心背景图 - -## 1.0.5(6) - -- 优化双击列表回顶体验 -- 新增设置网页仅边缘可滑动返回,避免内部滑动冲突 - -## 1.0.4(5) - -- 修复缓存分页导致数据错乱BUG - -## 1.0.3(4) - -- 压缩APK安装包大小 - -## 1.0.2(3) - -- 新增稍后阅读 - -## 1.0.1(2) - -- 添加滑动返回 -- 优化加载速度 -- 修复BUG - -## 1.0.0(1) - -- 第一版试水 - +[CHANGELOG](https://github.com/goweii/WanAndroid/blob/master/CHANGELOG.md) diff --git a/app/build.gradle b/app/build.gradle index ec98da3e..696f5b12 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,34 +1,71 @@ apply plugin: 'com.android.application' -apply plugin: 'realm-android' +apply plugin: 'bugly' -apply from: "../module.gradle" +apply from: "${rootDir.path}/gradle/module.gradle" +apply from: "${rootDir.path}/gradle/flavor.gradle" android { + namespace "per.goweii.wanandroid" defaultConfig { - applicationId rootProject.ext.android.applicationId - renderscriptSupportModeEnabled true +// renderscriptTargetApi rootProject.ext.android.minSdkVersion +// renderscriptSupportModeEnabled true ndk { - abiFilters /*'armeabi' ,*/'armeabi-v7a'/*, 'arm64-v8a'*/ + abiFilters /*'armeabi',*/ 'armeabi-v7a', 'arm64-v8a' } - } - buildTypes { - release { - applicationVariants.all { variant -> - variant.outputs.each { output -> - output.outputFileName = "$applicationId-v${defaultConfig.versionName}.apk" - } + javaCompileOptions { + annotationProcessorOptions { + arguments = ["room.incremental": "true"] } } } + packagingOptions { + // flexmark-java 是 CommonMark(规范 0.28) 解析器的 Java 实现,采用 Markdown 解析架构后内联的块 + // https://github.com/vsch/flexmark-java + // 由于文件重复,新增设置: + exclude 'META-INF/LICENSE-LGPL-2.1.txt' + exclude 'META-INF/LICENSE-LGPL-3.txt' + exclude 'META-INF/LICENSE-W3C-TEST' + exclude 'META-INF/DEPENDENCIES' + + jniLibs { + useLegacyPackaging true + } + } + lint { + disable "NullSafeMutableLiveData" + } resourcePrefix "" + androidResources { + generateLocaleConfig true + } + applicationVariants.all { variant -> + variant.outputs.each { output -> + def versionName = variant.versionName + def versionCode = variant.versionCode + def buildType = variant.buildType.name + output.outputFileName = "${applicationId}-${versionName}-${versionCode}-${buildType}.apk" + } + } +} + +kapt { + generateStubs = true +} + +bugly { + debug = true +} + +repositories { + flatDir { dirs 'libs' } } dependencies { - annotationProcessor rootProject.ext.dependencies.butterknifeCompiler + kapt rootProject.ext.dependencies.butterknifeCompiler + implementation rootProject.ext.dependencies.startup implementation rootProject.ext.dependencies.cardview implementation rootProject.ext.dependencies.flexbox - implementation rootProject.ext.dependencies.realtimeBlurView - implementation rootProject.ext.dependencies.agentWeb +// implementation rootProject.ext.dependencies.realtimeBlurView implementation rootProject.ext.dependencies.banner implementation rootProject.ext.dependencies.persistentCookieJar implementation rootProject.ext.dependencies.revealLayout @@ -36,10 +73,30 @@ dependencies { implementation rootProject.ext.dependencies.swipelayout implementation rootProject.ext.dependencies.disklrucache implementation rootProject.ext.dependencies.multiStateView - implementation rootProject.ext.dependencies.pictureSelector - implementation rootProject.ext.dependencies.buglyCrashreport - implementation rootProject.ext.dependencies.crashActivity implementation rootProject.ext.dependencies.heartView - implementation rootProject.ext.dependencies.zxingLibrary - implementation rootProject.ext.dependencies.tbs +// implementation rootProject.ext.dependencies.tbs + implementation rootProject.ext.dependencies.webkit + implementation rootProject.ext.dependencies.photoView + implementation rootProject.ext.dependencies.roomRuntime + kapt rootProject.ext.dependencies.roomCompiler + implementation rootProject.ext.dependencies.roomKtx + implementation rootProject.ext.dependencies.jsoup + implementation rootProject.ext.dependencies.codex_core + implementation rootProject.ext.dependencies.codex_decorator_gesture + implementation rootProject.ext.dependencies.codex_decorator_beep + implementation rootProject.ext.dependencies.codex_decorator_finder_wechat + implementation rootProject.ext.dependencies.codex_decorator_frozen + implementation rootProject.ext.dependencies.codex_decorator_vibrate + implementation rootProject.ext.dependencies.codex_processor_zxing + implementation rootProject.ext.dependencies.lifecycle + kapt rootProject.ext.dependencies.lifecycle_compiler + implementation rootProject.ext.dependencies.palette + implementation rootProject.ext.dependencies.cropImageView + implementation rootProject.ext.dependencies.biometric + implementation rootProject.ext.dependencies.okhttp + implementation rootProject.ext.dependencies.okhttpSse + implementation 'com.github.donkingliang:ConsecutiveScroller:4.6.4' + implementation project(':component:ad') + implementation 'net.dankito.readability4j:readability4j:1.0.8' + implementation 'com.vladsch.flexmark:flexmark-all:0.64.8' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 99569c52..c72e3733 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,18 +1,34 @@ + xmlns:tools="http://schemas.android.com/tools"> - + + + + + + + + + + + + + + + + + + + + + + + + - + + + + @@ -44,78 +79,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + + + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" + android:launchMode="singleTask" /> + + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + + android:configChanges="keyboard|keyboardHidden|screenSize|fontScale|density|orientation" /> + + + + + + @@ -123,6 +259,19 @@ android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_path" /> + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/doc/privacy_policy.html b/app/src/main/assets/doc/privacy_policy.html new file mode 100644 index 00000000..1c839083 --- /dev/null +++ b/app/src/main/assets/doc/privacy_policy.html @@ -0,0 +1,45 @@ + + + + + 隐私政策 + + + + + +

+ 我们重视用户的隐私。您在使用我们的服务时,我们可能会收集和使用您的相关信息。我们希望通过本《隐私政策》向您说明,在使用我们的服务时,我们如何收集、使用、储存和分享这些信息,以及我们为您提供的访问、更新、控制和保护这些信息的方式。本《隐私政策》与您所使用的服务息息相关,希望您仔细阅读,在需要时,按照本《隐私政策》的指引,作出您认为适当的选择。本《隐私政策》中涉及的相关技术词汇,我们尽量以简明扼要的表述,并提供进一步说明的链接,以便您的理解。

+

您使用或继续使用我们的服务,即意味着同意我们按照本《隐私政策》收集、使用、储存和分享您的相关信息。

+

我们提供服务时,可能会收集、储存和使用下列与您有关的信息。如果您不提供相关信息,可能无法享受我们提供的某些服务,或者无法达到相关服务拟达到的效果。

+

+ 我们将尽一切可能采取适当的技术手段,保证您可以访问、更新和更正自己的注册信息或使用我们的服务时提供的其他个人信息。在访问、更新、更正和删除前述信息时,我们可能会要求您进行身份验证,以保障账户安全。

+

1. 适用范围

+

+ 除某些特定服务外,我们所有的服务均适用本《隐私政策》。这些特定服务将适用特定的隐私政策。针对某些特定服务的特定隐私政策,将更具体地说明我们在该等服务中如何使用您的信息。该特定服务的隐私政策构成本《隐私政策》的一部分。如相关特定服务的隐私政策与本《隐私政策》有不一致之处,适用该特定服务的隐私政策。

+

2. 信息的使用

+

a)在获得您的数据之后,本软件会将其上传至服务器,以生成您的排行榜数据,以便您能够更好地使用服务。

+

3. 信息披露

+

a)本软件不会将您的信息披露给不受信任的第三方。

+

b)根据法律的有关规定,或者行政或司法机构的要求,向第三方或者行政、司法机构披露;

+

c)如您出现违反中国有关法律、法规或者相关规则的情况,需要向第三方披露;

+

4. 信息存储和交换

+

本软件收集的有关您的信息和资料将保存在本软件及(或)其关联公司的服务器上,这些信息和资料可能传送至您所在国家、地区或本软件收集信息和资料所在地的境外并在境外被访问、存储和展示。

+

5. 信息安全

+

我们仅在本《隐私政策》所述目的所必需的期间和法律法规要求的时限内保留您的个人信息。 + 我们使用各种安全技术和程序,以防信息的丢失、不当使用、未经授权阅览或披露。例如,在某些服务中,我们将利用加密技术(例如SSL)来保护您提供的个人信息。但请您理解,由于技术的限制以及可能存在的各种恶意手段,在互联网行业,即便竭尽所能加强安全措施,也不可能始终保证信息百分之百的安全。您需要了解,您接入我们的服务所用的系统和通讯网络,有可能因我们可控范围外的因素而出现问题。

+

6. 隐私政策的适用例外

+

我们的服务可能包括或链接至第三方提供的社交媒体或其他服务(包括网站)。例如:

+

a)您利用 + “分享”键将某些内容分享到我们的服务,或您利用第三方连线服务登录我们的服务。这些功能可能会收集您的相关信息(包括您的日志信息),并可能在您的电脑装置cookies,从而正常运行上述功能;

+

b)我们通过广告或我们服务的其他方式向您提供链接,使您可以接入第三方的服务或网站。

+

+ 该等第三方社交媒体或其他服务可能由相关的第三方或我们运营。您使用该等第三方的社交媒体服务或其他服务(包括您向该等第三方提供的任何个人信息),须受该第三方的服务条款及隐私政策(而非《通用服务条款》或本《隐私政策》)约束,您需要仔细阅读其条款。本《隐私政策》仅适用于我们所收集的信息,并不适用于任何第三方提供的服务或第三方的信息使用规则,我们对任何第三方使用由您提供的信息不承担任何责任。

+

7. 变更

+

+ 我们可能适时修订本《隐私政策》的条款,该等修订构成本《隐私政策》的一部分。如该等修订造成您在本《隐私政策》下权利的实质减少,我们将在修订生效前通过在主页上显著位置提示或向您发送电子邮件或以其他方式通知您。在该种情况下,若您继续使用我们的服务,即表示同意受经修订的本《隐私政策》的约束。

+ + \ No newline at end of file diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 00000000..2d294aca Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png deleted file mode 100644 index 48805798..00000000 Binary files a/app/src/main/ic_launcher-web.png and /dev/null differ diff --git a/app/src/main/java/per/goweii/wanandroid/common/Config.java b/app/src/main/java/per/goweii/wanandroid/common/Config.java index 7ec12e95..ce835c00 100644 --- a/app/src/main/java/per/goweii/wanandroid/common/Config.java +++ b/app/src/main/java/per/goweii/wanandroid/common/Config.java @@ -3,10 +3,19 @@ /** * @author CuiZhen * @date 2019/5/7 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class Config { + /** + * 返回列表顶部双击超时 + */ public static final long SCROLL_TOP_DOUBLE_CLICK_DELAY = 500L; + /** + * 阅读历史最大保存个数 + */ + public static final int READ_RECORD_MAX_COUNT = 1000; + /** + * 消息通知最大显示个数,超过显示99+ + */ + public static final int NOTIFICATION_MAX_SHOW_COUNT = 99; } diff --git a/app/src/main/java/per/goweii/wanandroid/common/Constant.java b/app/src/main/java/per/goweii/wanandroid/common/Constant.java new file mode 100644 index 00000000..cd0e4980 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/common/Constant.java @@ -0,0 +1,12 @@ +package per.goweii.wanandroid.common; + +/** + * @author CuiZhen + * @date 2020/11/29 + */ +public class Constant { +// public static final String PRIVACY_POLICY_URL = "https://goweii.gitee.io/wanandroidserver/doc/privacy_policy.html"; + public static final String PRIVACY_POLICY_URL = "file:///android_asset/doc/privacy_policy.html"; + + public static final String BROWSER_UA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.84 Safari/537.36"; +} diff --git a/app/src/main/java/per/goweii/wanandroid/common/InitTasks.kt b/app/src/main/java/per/goweii/wanandroid/common/InitTasks.kt new file mode 100644 index 00000000..a374c1da --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/common/InitTasks.kt @@ -0,0 +1,231 @@ +package per.goweii.wanandroid.common + +import android.app.Activity +import android.app.Application +import android.os.Bundle +import com.scwang.smart.refresh.footer.ClassicsFooter +import com.scwang.smart.refresh.layout.SmartRefreshLayout +import per.goweii.basic.core.CoreInit +import per.goweii.basic.utils.AsyncInitTask +import per.goweii.basic.utils.SyncInitTask +import per.goweii.basic.utils.listener.SimpleCallback +import per.goweii.ponyo.crash.Crash +import per.goweii.rxhttp.core.RxHttp +import per.goweii.swipeback.SwipeBack +import per.goweii.swipeback.SwipeBackDirection +import per.goweii.wanandroid.db.WanDb +import per.goweii.wanandroid.http.RxHttpRequestSetting +import per.goweii.wanandroid.http.WanCache +import per.goweii.wanandroid.module.main.activity.CrashActivity +import per.goweii.wanandroid.utils.ConfigUtils +import per.goweii.wanandroid.utils.CookieUtils +import per.goweii.wanandroid.utils.DarkModeUtils +import per.goweii.wanandroid.utils.GrayFilterHelper +import per.goweii.wanandroid.utils.UserUtils +import per.goweii.wanandroid.utils.web.cache.ReadingModeManager +import per.goweii.wanandroid.widget.refresh.ShiciRefreshHeader + +/** + * @author CuiZhen + * @date 2020/2/20 + */ + +class SmartRefreshInitTask : SyncInitTask() { + override fun init(application: Application) { + SmartRefreshLayout.setDefaultRefreshHeaderCreator { context, _ -> ShiciRefreshHeader(context) } + SmartRefreshLayout.setDefaultRefreshFooterCreator { context, _ -> ClassicsFooter(context) } + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun level(): Int { + return 0 + } +} + +class CookieManagerInitTask : SyncInitTask() { + override fun init(application: Application) { + CookieUtils.init(application) + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun level(): Int { + return 0 + } +} + +class NightModeInitTask : SyncInitTask() { + override fun init(application: Application) { + DarkModeUtils.initDarkMode(application) + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun level(): Int { + return 0 + } +} + +class ThemeInitTask : SyncInitTask(), Application.ActivityLifecycleCallbacks { + override fun init(application: Application) { + application.registerActivityLifecycleCallbacks(this) + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun level(): Int { + return 0 + } + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + val themeId = ConfigUtils.getInstance().theme + if (themeId > 0) { + activity.setTheme(themeId) + } + } + + override fun onActivityStarted(activity: Activity) { + } + + override fun onActivityResumed(activity: Activity) { + } + + override fun onActivityPaused(activity: Activity) { + } + + override fun onActivityStopped(activity: Activity) { + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + } + + override fun onActivityDestroyed(activity: Activity) { + } +} + +class GrayFilterInitTask : SyncInitTask() { + override fun init(application: Application) { + GrayFilterHelper.attach(application) + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun level(): Int { + return 0 + } +} + +class SwipeBackInitTask : SyncInitTask() { + override fun init(application: Application) { + SwipeBack.getInstance().init(application) + SwipeBack.getInstance().swipeBackDirection = SwipeBackDirection.RIGHT + SwipeBack.getInstance().swipeBackTransformer = null + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun level(): Int { + return 0 + } +} + +class RxHttpInitTask : SyncInitTask() { + override fun init(application: Application) { + RxHttp.init(application) + RxHttp.initRequest(RxHttpRequestSetting(CookieUtils.cookieJar)) + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun level(): Int { + return 0 + } +} + +class WanDbInitTask : SyncInitTask() { + override fun init(application: Application) { + WanDb.init(application) + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun level(): Int { + return 0 + } +} + +class WanCacheInitTask : SyncInitTask() { + override fun init(application: Application) { + WanCache.init() + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun level(): Int { + return 0 + } +} + +class CoreInitTask : SyncInitTask() { + override fun init(application: Application) { + CoreInit.getInstance().onGoLoginCallback = SimpleCallback { data -> + UserUtils.getInstance().doIfLogin(data) + } + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun level(): Int { + return 0 + } +} + +class CrashInitTask : SyncInitTask() { + override fun init(application: Application) { + Crash.initialize(application) + Crash.setCrashActivity(CrashActivity::class.java) + } + + override fun onlyMainProcess(): Boolean { + return false + } + + override fun level(): Int { + return 0 + } +} + +class ReadingModeTask : AsyncInitTask() { + override fun init(application: Application) { + ReadingModeManager.setup() + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun level(): Int { + return 3 + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/common/ScrollTop.java b/app/src/main/java/per/goweii/wanandroid/common/ScrollTop.java deleted file mode 100644 index 3e8ad102..00000000 --- a/app/src/main/java/per/goweii/wanandroid/common/ScrollTop.java +++ /dev/null @@ -1,12 +0,0 @@ -package per.goweii.wanandroid.common; - -/** - * @author CuiZhen - * @date 2019/5/20 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public interface ScrollTop { - void scrollTop(); -} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/common/WanApp.java b/app/src/main/java/per/goweii/wanandroid/common/WanApp.java index 1c281f22..d3360eaa 100644 --- a/app/src/main/java/per/goweii/wanandroid/common/WanApp.java +++ b/app/src/main/java/per/goweii/wanandroid/common/WanApp.java @@ -1,177 +1,31 @@ package per.goweii.wanandroid.common; -import android.app.Activity; -import android.content.Intent; -import android.content.res.Configuration; -import android.os.Bundle; -import android.support.v7.app.AppCompatDelegate; - -import com.franmontiel.persistentcookiejar.PersistentCookieJar; -import com.franmontiel.persistentcookiejar.cache.SetCookieCache; -import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor; -import com.tencent.bugly.crashreport.CrashReport; -import com.tencent.smtt.sdk.QbSdk; - -import java.util.LinkedHashMap; -import java.util.Map; - -import cat.ereza.customactivityoncrash.config.CaocConfig; -import io.realm.Realm; -import per.goweii.basic.core.CoreInit; import per.goweii.basic.core.base.BaseApp; -import per.goweii.basic.utils.DebugUtils; -import per.goweii.basic.utils.LogUtils; -import per.goweii.basic.utils.listener.SimpleCallback; -import per.goweii.burred.Blurred; -import per.goweii.rxhttp.core.RxHttp; -import per.goweii.wanandroid.http.RxHttpRequestSetting; -import per.goweii.wanandroid.http.WanCache; -import per.goweii.wanandroid.module.main.activity.CrashActivity; -import per.goweii.wanandroid.module.main.activity.MainActivity; -import per.goweii.wanandroid.module.main.activity.WebActivity; -import per.goweii.wanandroid.utils.SettingUtils; -import per.goweii.wanandroid.utils.UserUtils; +import per.goweii.basic.utils.InitTaskRunner; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WanApp extends BaseApp { - - private static PersistentCookieJar mCookieJar = null; - - private static boolean mWebActivityStarted = false; - - public static boolean isWebActivityStarted() { - return mWebActivityStarted; - } - @Override public void onCreate() { super.onCreate(); - if (isMainProcess()) { - setDarkModeStatus(); - RxHttp.init(this); - RxHttp.initRequest(new RxHttpRequestSetting(getCookieJar())); - WanCache.init(); - Blurred.init(getAppContext()); - CoreInit.getInstance().setOnGoLoginCallback(new SimpleCallback() { - @Override - public void onResult(Activity data) { - UserUtils.getInstance().doIfLogin(data); - } - }); - Realm.init(this); - } - initX5(); - initBugly(); - initCrashActivity(); - } - - private void initX5() { - QbSdk.initX5Environment(this, new QbSdk.PreInitCallback() { - @Override - public void onCoreInitFinished() { - LogUtils.d("x5", "initX5Environment->onCoreInitFinished"); - } - - @Override - public void onViewInitFinished(boolean b) { - LogUtils.d("x5", "initX5Environment->onViewInitFinished=" + b); - } - }); - } - - private void initBugly() { - if (!DebugUtils.isDebug()) { - CrashReport.setIsDevelopmentDevice(this, DebugUtils.isDebug()); - CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(this); - strategy.setCrashHandleCallback(new CrashReport.CrashHandleCallback() { - @Override - public Map onCrashHandleStart(int crashType, String errorType, String errorMessage, String errorStack) { - LinkedHashMap map = new LinkedHashMap<>(); - String x5CrashInfo = com.tencent.smtt.sdk.WebView.getCrashExtraMessage(getAppContext()); - map.put("x5crashInfo", x5CrashInfo); - return map; - } - - @Override - public byte[] onCrashHandleStart2GetExtraDatas(int crashType, String errorType, String errorMessage, String errorStack) { - try { - return "Extra data.".getBytes("UTF-8"); - } catch (Exception e) { - return null; - } - } - }); - strategy.setUploadProcess(isMainProcess()); - CrashReport.initCrashReport(this, "0411151084", DebugUtils.isDebug(), strategy); - } - } - - private void initCrashActivity() { - CaocConfig.Builder.create() - .backgroundMode(CaocConfig.BACKGROUND_MODE_SILENT) - .enabled(true) - .showErrorDetails(true) - .showRestartButton(true) - .logErrorOnRestart(false) - .trackActivities(false) - .minTimeBetweenCrashesMs(2000) - .restartActivity(MainActivity.class) - .errorActivity(CrashActivity.class) - .apply(); - } - - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - super.onActivityCreated(activity, savedInstanceState); - if (activity instanceof MainActivity) { - if (!mWebActivityStarted) { - Intent intent = new Intent(activity, WebActivity.class); - intent.putExtra("destroyOnCreated", true); - activity.startActivity(intent); - } - } - } - - @Override - public void onActivityStarted(Activity activity) { - super.onActivityStarted(activity); - if (activity instanceof WebActivity) { - boolean destroyOnCreated = activity.getIntent().getBooleanExtra("destroyOnCreated", false); - if (destroyOnCreated) { - activity.finish(); - } - mWebActivityStarted = true; - } - } - - @Override - public void onActivityStopped(Activity activity) { - super.onActivityStopped(activity); - } - - public static boolean getDarkModeStatus() { - int mode = getAppContext().getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - return mode == Configuration.UI_MODE_NIGHT_YES; - } - - public static void setDarkModeStatus() { - if (SettingUtils.getInstance().isDarkTheme()) { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); - } else { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); - } - } + new InitTaskRunner(this) + .add(new SmartRefreshInitTask()) + .add(new CookieManagerInitTask()) + .add(new NightModeInitTask()) + .add(new ThemeInitTask()) + .add(new GrayFilterInitTask()) + .add(new SwipeBackInitTask()) + .add(new CoreInitTask()) + .add(new RxHttpInitTask()) + .add(new WanDbInitTask()) + .add(new WanCacheInitTask()) + .add(new CrashInitTask()) + .add(new ReadingModeTask()) + .run(); - public static PersistentCookieJar getCookieJar() { - if (mCookieJar == null) { - mCookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(getAppContext())); - } - return mCookieJar; } } diff --git a/app/src/main/java/per/goweii/wanandroid/db/WanDb.kt b/app/src/main/java/per/goweii/wanandroid/db/WanDb.kt new file mode 100644 index 00000000..0f02d68f --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/db/WanDb.kt @@ -0,0 +1,34 @@ +package per.goweii.wanandroid.db + +import android.annotation.SuppressLint +import android.content.Context +import androidx.room.Room +import per.goweii.wanandroid.db.migration.Migration_1_2 + +/** + * @author CuiZhen + * @date 2020/3/21 + */ +@SuppressLint("StaticFieldLeak") +object WanDb { + private lateinit var context: Context + private var database: WanRoom? = null + + @JvmStatic + fun init(context: Context) { + this.context = context.applicationContext + } + + @JvmStatic + fun db(): WanRoom { + if (database?.isOpen == true) { + return database!! + } + database = Room + .databaseBuilder(context, WanRoom::class.java, WanRoom.NAME) + .addMigrations(Migration_1_2()) + .build() + return database!! + } + +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/db/WanRoom.kt b/app/src/main/java/per/goweii/wanandroid/db/WanRoom.kt new file mode 100644 index 00000000..40cbfe36 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/db/WanRoom.kt @@ -0,0 +1,30 @@ +package per.goweii.wanandroid.db + +import androidx.room.Database +import androidx.room.RoomDatabase +import per.goweii.wanandroid.db.dao.ReadLaterDao +import per.goweii.wanandroid.db.dao.ReadRecordDao +import per.goweii.wanandroid.db.model.ReadLaterModel +import per.goweii.wanandroid.db.model.ReadRecordModel + +/** + * @author CuiZhen + * @date 2020/3/21 + */ +@Database( + entities = [ + ReadLaterModel::class, + ReadRecordModel::class + ], + version = WanRoom.VERSION, + exportSchema = false, +) +abstract class WanRoom : RoomDatabase() { + companion object { + const val NAME = "wan_db" + const val VERSION = 2 + } + + abstract fun readLaterDao(): ReadLaterDao + abstract fun readRecordDao(): ReadRecordDao +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/db/dao/ReadLaterDao.kt b/app/src/main/java/per/goweii/wanandroid/db/dao/ReadLaterDao.kt new file mode 100644 index 00000000..fb01b230 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/db/dao/ReadLaterDao.kt @@ -0,0 +1,32 @@ +package per.goweii.wanandroid.db.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import per.goweii.wanandroid.db.model.ReadLaterModel + +/** + * @author CuiZhen + * @date 2020/3/21 + */ +@Dao +interface ReadLaterDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insert(vararg mode: ReadLaterModel) + + @Query("DELETE FROM ReadLaterModel WHERE link = :link") + suspend fun delete(link: String) + + @Query("DELETE FROM ReadLaterModel") + suspend fun deleteAll() + + @Query("SELECT * FROM ReadLaterModel ORDER BY time DESC LIMIT (:offset), (:count)") + suspend fun findAll(offset: Int, count: Int): List + + @Query("SELECT * FROM ReadLaterModel ORDER BY time DESC LIMIT 0, (:count)") + suspend fun findLately(count: Int): List + + @Query("SELECT * FROM ReadLaterModel WHERE link = :link") + suspend fun findByLink(link: String): List +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/db/dao/ReadRecordDao.kt b/app/src/main/java/per/goweii/wanandroid/db/dao/ReadRecordDao.kt new file mode 100644 index 00000000..ff5b10b2 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/db/dao/ReadRecordDao.kt @@ -0,0 +1,42 @@ +package per.goweii.wanandroid.db.dao + +import androidx.room.* +import per.goweii.wanandroid.db.model.ReadRecordModel + +/** + * @author CuiZhen + * @date 2020/3/21 + */ +@Dao +interface ReadRecordDao { + @Insert(onConflict = OnConflictStrategy.ABORT) + suspend fun insert(vararg mode: ReadRecordModel) + + @Query("UPDATE ReadRecordModel SET lastTime = :lastTime WHERE (link = :link AND lastTime != :lastTime)") + suspend fun updateLastTime(link: String, lastTime: Long) + + @Query("UPDATE ReadRecordModel SET title = :title WHERE (link = :link AND title != :title)") + suspend fun updateTitle(link: String, title: String) + + @Query("UPDATE ReadRecordModel SET lastTime = :lastTime, percent = :percent WHERE (link = :link AND percent < :percent)") + suspend fun updatePercent(link: String, percent: Int, lastTime: Long) + + @Query("SELECT * FROM ReadRecordModel WHERE link = :link") + suspend fun findByLink(link: String): ReadRecordModel? + + @Query("DELETE FROM ReadRecordModel WHERE link = :link") + suspend fun delete(link: String) + + @Query("DELETE FROM ReadRecordModel") + suspend fun deleteAll() + + @Query("SELECT * FROM ReadRecordModel ORDER BY lastTime DESC LIMIT (:offset), (:count)") + suspend fun findAll(offset: Int, count: Int): List + + @Query("SELECT * FROM ReadRecordModel WHERE link in (:links) ORDER BY lastTime DESC") + suspend fun findByLinks(links: List): List + + @Query("""DELETE FROM ReadRecordModel WHERE link NOT IN + (SELECT link FROM ReadRecordModel ORDER BY lastTime DESC LIMIT 0, :maxCount)""") + suspend fun deleteIfMaxCount(maxCount: Int) +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/db/executor/DbExecutor.kt b/app/src/main/java/per/goweii/wanandroid/db/executor/DbExecutor.kt new file mode 100644 index 00000000..2c70929f --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/db/executor/DbExecutor.kt @@ -0,0 +1,27 @@ +package per.goweii.wanandroid.db.executor + +import kotlinx.coroutines.* + +/** + * @author CuiZhen + * @date 2020/3/21 + */ +open class DbExecutor : CoroutineScope by MainScope() { + + fun destroy() { + cancel() + } + + fun execute(runnable: suspend () -> T, success: ((t: T) -> Unit)? = null, error: ((e: Throwable) -> Unit)? = null) { + launch(CoroutineExceptionHandler { _, _ -> }) { + val result: T = withContext(Dispatchers.IO) { + runnable.invoke() + } + success?.invoke(result) + }.invokeOnCompletion { + it?.let { + error?.invoke(it) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/db/executor/ReadLaterExecutor.kt b/app/src/main/java/per/goweii/wanandroid/db/executor/ReadLaterExecutor.kt new file mode 100644 index 00000000..d1e21aee --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/db/executor/ReadLaterExecutor.kt @@ -0,0 +1,75 @@ +package per.goweii.wanandroid.db.executor + +import per.goweii.basic.utils.listener.SimpleCallback +import per.goweii.basic.utils.listener.SimpleListener +import per.goweii.wanandroid.db.WanDb.db +import per.goweii.wanandroid.db.model.ReadLaterModel + +/** + * @author CuiZhen + * @date 2020/3/21 + */ +class ReadLaterExecutor : DbExecutor() { + + fun findByLink(link: String, success: SimpleCallback>, error: SimpleListener) { + execute({ + db().readLaterDao().findByLink(link) + }, { + success.onResult(it) + }, { + error.onResult() + }) + } + + fun add(link: String, title: String, success: SimpleCallback, error: SimpleListener) { + execute({ + val model = ReadLaterModel(link, title, System.currentTimeMillis()) + db().readLaterDao().insert(model) + model + }, { + success.onResult(it) + }, { + error.onResult() + }) + } + + fun remove(link: String, success: SimpleListener, error: SimpleListener) { + execute({ + db().readLaterDao().delete(link) + }, { + success.onResult() + }, { + error.onResult() + }) + } + + fun removeAll(success: SimpleListener, error: SimpleListener) { + execute({ + db().readLaterDao().deleteAll() + }, { + success.onResult() + }, { + error.onResult() + }) + } + + fun getList(from: Int, count: Int, success: SimpleCallback>, error: SimpleListener) { + execute({ + db().readLaterDao().findAll(from, count) + }, { + success.onResult(it) + }, { + error.onResult() + }) + } + + fun findLately(count: Int, success: SimpleCallback>, error: SimpleListener) { + execute({ + db().readLaterDao().findLately(count) + }, { + success.onResult(it) + }, { + error.onResult() + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/db/executor/ReadRecordExecutor.kt b/app/src/main/java/per/goweii/wanandroid/db/executor/ReadRecordExecutor.kt new file mode 100644 index 00000000..db47cb1a --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/db/executor/ReadRecordExecutor.kt @@ -0,0 +1,149 @@ +package per.goweii.wanandroid.db.executor + +import androidx.room.withTransaction +import per.goweii.basic.utils.listener.SimpleCallback +import per.goweii.basic.utils.listener.SimpleListener +import per.goweii.wanandroid.common.Config +import per.goweii.wanandroid.db.WanDb.db +import per.goweii.wanandroid.db.model.ReadRecordModel + +/** + * @author CuiZhen + * @date 2020/3/21 + */ +class ReadRecordExecutor : DbExecutor() { + + fun findByLinks( + link: List, + success: SimpleCallback>, + error: SimpleCallback + ) { + execute({ + db().readRecordDao().findByLinks(link) + }, { + success.onResult(it) + }, { + error.onResult(it) + }) + } + + fun add( + link: String, + title: String, + percent: Float, + success: SimpleCallback, + error: SimpleListener + ) { + execute({ + val db = db() + db.withTransaction { + val dao = db.readRecordDao() + dao.updateTitle(link, title) + val old = dao.findByLink(link) + if (old != null) { + val time = System.currentTimeMillis() + dao.updateLastTime(link, time) + old.copy(lastTime = time) + } else { + val time = System.currentTimeMillis() + val model = ReadRecordModel( + link, title, time, time, + (percent * ReadRecordModel.MAX_PERCENT).toInt() + ) + dao.insert(model) + model + } + } + }, { + success.onResult(it) + removeIfMaxCount {} + }, { + error.onResult() + }) + } + + fun updateTitle( + link: String, + title: String, + success: SimpleCallback, + error: SimpleListener + ) { + execute({ + val db = db() + db.withTransaction { + val dao = db.readRecordDao() + dao.updateTitle(link, title) + dao.findByLink(link)!! + } + }, { + success.onResult(it) + }, { + error.onResult() + }) + } + + fun updatePercent( + link: String, + percent: Float, + lastTime: Long, + success: SimpleCallback, + error: SimpleListener + ) { + val p = (percent.coerceIn(0f, 1f) * ReadRecordModel.MAX_PERCENT).toInt() + execute({ + val db = db() + db.withTransaction { + val dao = db.readRecordDao() + dao.updatePercent(link, p, lastTime) + dao.findByLink(link)!! + } + }, { + success.onResult(it) + }, { + error.onResult() + }) + } + + fun remove(link: String, success: SimpleListener, error: SimpleListener) { + execute({ + db().readRecordDao().delete(link) + }, { + success.onResult() + }, { + error.onResult() + }) + } + + fun removeAll(success: SimpleListener, error: SimpleListener) { + execute({ + db().readRecordDao().deleteAll() + }, { + success.onResult() + }, { + error.onResult() + }) + } + + fun getList( + from: Int, + count: Int, + success: SimpleCallback>, + error: SimpleListener + ) { + execute({ + db().readRecordDao().findAll(from, count) + }, { + success.onResult(it) + }, { + error.onResult() + }) + } + + private fun removeIfMaxCount(finish: () -> Unit) { + execute({ db().readRecordDao().deleteIfMaxCount(Config.READ_RECORD_MAX_COUNT) }, { + finish.invoke() + }, { + finish.invoke() + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/db/migration/Migrations.kt b/app/src/main/java/per/goweii/wanandroid/db/migration/Migrations.kt new file mode 100644 index 00000000..236fd465 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/db/migration/Migrations.kt @@ -0,0 +1,21 @@ +@file:Suppress("ClassName") + +package per.goweii.wanandroid.db.migration + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase + +class Migration_1_2 : Migration(1, 2) { + override fun migrate(database: SupportSQLiteDatabase) { + database.beginTransaction() + try { + database.execSQL("ALTER TABLE ReadRecordModel ADD COLUMN percent INTEGER NOT NULL DEFAULT 0") + database.execSQL("ALTER TABLE ReadRecordModel ADD COLUMN lastTime INTEGER NOT NULL DEFAULT 0") + database.setTransactionSuccessful() + } catch (e: Throwable) { + e.printStackTrace() + } finally { + database.endTransaction() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/db/model/ReadLaterModel.kt b/app/src/main/java/per/goweii/wanandroid/db/model/ReadLaterModel.kt new file mode 100644 index 00000000..bda2638e --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/db/model/ReadLaterModel.kt @@ -0,0 +1,14 @@ +package per.goweii.wanandroid.db.model + +import androidx.room.Entity + +/** + * @author CuiZhen + * @date 2020/3/21 + */ +@Entity(primaryKeys = ["link"]) +data class ReadLaterModel( + val link: String, + val title: String, + val time: Long +) \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/db/model/ReadRecordModel.kt b/app/src/main/java/per/goweii/wanandroid/db/model/ReadRecordModel.kt new file mode 100644 index 00000000..38b3d880 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/db/model/ReadRecordModel.kt @@ -0,0 +1,27 @@ +package per.goweii.wanandroid.db.model + +import androidx.annotation.FloatRange +import androidx.annotation.IntRange +import androidx.room.Entity + +/** + * @author CuiZhen + * @date 2020/3/21 + */ +@Entity(primaryKeys = ["link"]) +data class ReadRecordModel( + val link: String, + val title: String, + val time: Long, + val lastTime: Long, + @IntRange(from = 0, to = 10000) val percent: Int, +) { + companion object { + const val MIN_PERCENT = 0 + const val MAX_PERCENT = 10000 + } + + val percentFloat: Float + @FloatRange(from = 0.0, to = 1.0) + get() = (percent.toFloat() / MAX_PERCENT.toFloat()).coerceIn(0f, 1f) +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/event/ArticleDeleteEvent.java b/app/src/main/java/per/goweii/wanandroid/event/ArticleDeleteEvent.java index c519f12c..fca856cb 100644 --- a/app/src/main/java/per/goweii/wanandroid/event/ArticleDeleteEvent.java +++ b/app/src/main/java/per/goweii/wanandroid/event/ArticleDeleteEvent.java @@ -3,8 +3,6 @@ /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ArticleDeleteEvent extends BaseEvent { diff --git a/app/src/main/java/per/goweii/wanandroid/event/ArticleShareEvent.java b/app/src/main/java/per/goweii/wanandroid/event/ArticleShareEvent.java index ecdaaf30..3a4375b1 100644 --- a/app/src/main/java/per/goweii/wanandroid/event/ArticleShareEvent.java +++ b/app/src/main/java/per/goweii/wanandroid/event/ArticleShareEvent.java @@ -3,8 +3,6 @@ /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ArticleShareEvent extends BaseEvent { diff --git a/app/src/main/java/per/goweii/wanandroid/event/BannerAutoSwitchEnableEvent.java b/app/src/main/java/per/goweii/wanandroid/event/BannerAutoSwitchEnableEvent.java new file mode 100644 index 00000000..93ee4645 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/event/BannerAutoSwitchEnableEvent.java @@ -0,0 +1,18 @@ +package per.goweii.wanandroid.event; + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +public class BannerAutoSwitchEnableEvent extends BaseEvent { + private final boolean enable; + + public BannerAutoSwitchEnableEvent(boolean enable) { + this.enable = enable; + } + + public boolean isEnable() { + return enable; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/event/BaseEvent.java b/app/src/main/java/per/goweii/wanandroid/event/BaseEvent.java index 6a51c5a3..abe5ed70 100644 --- a/app/src/main/java/per/goweii/wanandroid/event/BaseEvent.java +++ b/app/src/main/java/per/goweii/wanandroid/event/BaseEvent.java @@ -5,14 +5,20 @@ /** * @author CuiZhen * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class BaseEvent { - public void post(){ + public void post() { EventBus.getDefault().post(this); } + public void postSticky() { + EventBus.getDefault().postSticky(this); + } + + public void removeSticky() { + EventBus.getDefault().removeStickyEvent(this); + } + } diff --git a/app/src/main/java/per/goweii/wanandroid/event/CloseSecondFloorEvent.java b/app/src/main/java/per/goweii/wanandroid/event/CloseSecondFloorEvent.java new file mode 100644 index 00000000..511f87e0 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/event/CloseSecondFloorEvent.java @@ -0,0 +1,9 @@ +package per.goweii.wanandroid.event; + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +public class CloseSecondFloorEvent extends BaseEvent { +} diff --git a/app/src/main/java/per/goweii/wanandroid/event/CollectionEvent.java b/app/src/main/java/per/goweii/wanandroid/event/CollectionEvent.java index 1a743737..b70fdf22 100644 --- a/app/src/main/java/per/goweii/wanandroid/event/CollectionEvent.java +++ b/app/src/main/java/per/goweii/wanandroid/event/CollectionEvent.java @@ -3,8 +3,6 @@ /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CollectionEvent extends BaseEvent { @@ -13,23 +11,23 @@ public class CollectionEvent extends BaseEvent { private int articleId; private int collectId; - public static void postCollectWithCollectId(int collectId){ + public static void postCollectWithCollectId(int collectId) { new CollectionEvent(true, -1, collectId).post(); } - public static void postCollectWithArticleId(int articleId){ + public static void postCollectWithArticleId(int articleId) { new CollectionEvent(true, articleId, -1).post(); } - public static void postUnCollectWithArticleId(int articleId){ + public static void postUnCollectWithArticleId(int articleId) { new CollectionEvent(false, articleId, -1).post(); } - public static void postUncollectWithCollectId(int collectId){ + public static void postUncollectWithCollectId(int collectId) { new CollectionEvent(false, -1, collectId).post(); } - public static void postUncollect(int articleId, int collectId){ + public static void postUncollect(int articleId, int collectId) { new CollectionEvent(false, articleId, collectId).post(); } diff --git a/app/src/main/java/per/goweii/wanandroid/event/HomeActionBarEvent.java b/app/src/main/java/per/goweii/wanandroid/event/HomeActionBarEvent.java new file mode 100644 index 00000000..b472f2c6 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/event/HomeActionBarEvent.java @@ -0,0 +1,49 @@ +package per.goweii.wanandroid.event; + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +public class HomeActionBarEvent extends BaseEvent { + private String text; + private String color; + private String image; + private String secondFloorBg; + private float secondFloorBgBlurPercent; + + public HomeActionBarEvent(String text, + String color, + String image, + String secondFloorBg, + float secondFloorBgBlurPercent) { + this.text = text; + this.color = color; + this.image = image; + this.secondFloorBg = secondFloorBg; + this.secondFloorBgBlurPercent = secondFloorBgBlurPercent; + } + + public HomeActionBarEvent() { + } + + public String getHomeTitle() { + return text; + } + + public String getActionBarBgColor() { + return color; + } + + public String getActionBarBgImageUrl() { + return image; + } + + public String getSecondFloorBgImageUrl() { + return secondFloorBg; + } + + public float getSecondFloorBgImageBlurPercent() { + return secondFloorBgBlurPercent; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/event/LoginEvent.java b/app/src/main/java/per/goweii/wanandroid/event/LoginEvent.java index 05f60af5..8f108cc7 100644 --- a/app/src/main/java/per/goweii/wanandroid/event/LoginEvent.java +++ b/app/src/main/java/per/goweii/wanandroid/event/LoginEvent.java @@ -3,8 +3,6 @@ /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class LoginEvent extends BaseEvent { diff --git a/app/src/main/java/per/goweii/wanandroid/event/MessageCountEvent.java b/app/src/main/java/per/goweii/wanandroid/event/MessageCountEvent.java new file mode 100644 index 00000000..7d6cc0c2 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/event/MessageCountEvent.java @@ -0,0 +1,23 @@ +package per.goweii.wanandroid.event; + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +public class MessageCountEvent extends BaseEvent { + + private int count; + + public static void post(int count) { + new MessageCountEvent(count).post(); + } + + private MessageCountEvent(int count) { + this.count = count; + } + + public int getCount() { + return count; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/event/MessageDeleteEvent.java b/app/src/main/java/per/goweii/wanandroid/event/MessageDeleteEvent.java new file mode 100644 index 00000000..11f9f13a --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/event/MessageDeleteEvent.java @@ -0,0 +1,25 @@ +package per.goweii.wanandroid.event; + +import per.goweii.wanandroid.module.mine.model.MessageBean; + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +public class MessageDeleteEvent extends BaseEvent { + + private MessageBean bean; + + public static void post(MessageBean bean) { + new MessageDeleteEvent(bean).post(); + } + + private MessageDeleteEvent(MessageBean bean) { + this.bean = bean; + } + + public MessageBean getMessageBean() { + return bean; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/event/MessageUpdateEvent.java b/app/src/main/java/per/goweii/wanandroid/event/MessageUpdateEvent.java new file mode 100644 index 00000000..aff3bca0 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/event/MessageUpdateEvent.java @@ -0,0 +1,9 @@ +package per.goweii.wanandroid.event; + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +public class MessageUpdateEvent extends BaseEvent { +} diff --git a/app/src/main/java/per/goweii/wanandroid/event/ReadLaterEvent.java b/app/src/main/java/per/goweii/wanandroid/event/ReadLaterEvent.java new file mode 100644 index 00000000..0179ba64 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/event/ReadLaterEvent.java @@ -0,0 +1,9 @@ +package per.goweii.wanandroid.event; + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +public class ReadLaterEvent extends BaseEvent { +} diff --git a/app/src/main/java/per/goweii/wanandroid/event/ReadRecordAddedEvent.java b/app/src/main/java/per/goweii/wanandroid/event/ReadRecordAddedEvent.java new file mode 100644 index 00000000..66fa0962 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/event/ReadRecordAddedEvent.java @@ -0,0 +1,22 @@ +package per.goweii.wanandroid.event; + +import io.reactivex.annotations.NonNull; +import per.goweii.wanandroid.db.model.ReadRecordModel; + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +public class ReadRecordAddedEvent extends BaseEvent { + private final ReadRecordModel readRecordModel; + + public ReadRecordAddedEvent(@NonNull ReadRecordModel readRecordModel) { + this.readRecordModel = readRecordModel; + } + + @NonNull + public ReadRecordModel getReadRecordModel() { + return readRecordModel; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/event/ReadRecordUpdateEvent.java b/app/src/main/java/per/goweii/wanandroid/event/ReadRecordUpdateEvent.java new file mode 100644 index 00000000..756b9e9a --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/event/ReadRecordUpdateEvent.java @@ -0,0 +1,42 @@ +package per.goweii.wanandroid.event; + +import io.reactivex.annotations.NonNull; + +public class ReadRecordUpdateEvent extends BaseEvent { + private final String mLink; + private String mTitle; + private Long mTime; + private Integer mPercent; + + public ReadRecordUpdateEvent(@NonNull String link) { + this.mLink = link; + } + + public String getLink() { + return mLink; + } + + public String getTitle() { + return mTitle; + } + + public void setTitle(String title) { + this.mTitle = title; + } + + public Long getTime() { + return mTime; + } + + public void setTime(Long time) { + this.mTime = time; + } + + public Integer getPercent() { + return mPercent; + } + + public void setPercent(Integer percent) { + this.mPercent = percent; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/event/ScrollTopEvent.java b/app/src/main/java/per/goweii/wanandroid/event/ScrollTopEvent.java index e60e96dd..d03d98b5 100644 --- a/app/src/main/java/per/goweii/wanandroid/event/ScrollTopEvent.java +++ b/app/src/main/java/per/goweii/wanandroid/event/ScrollTopEvent.java @@ -3,8 +3,6 @@ /** * @author CuiZhen * @date 2019/5/20 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ScrollTopEvent extends BaseEvent { diff --git a/app/src/main/java/per/goweii/wanandroid/event/SettingChangeEvent.java b/app/src/main/java/per/goweii/wanandroid/event/SettingChangeEvent.java index 292a7c33..687a642e 100644 --- a/app/src/main/java/per/goweii/wanandroid/event/SettingChangeEvent.java +++ b/app/src/main/java/per/goweii/wanandroid/event/SettingChangeEvent.java @@ -3,36 +3,30 @@ /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class SettingChangeEvent extends BaseEvent { - private boolean showReadLaterChanged; + private boolean showQAChanged; private boolean showTopChanged; private boolean showBannerChanged; - private boolean hideAboutMeChanged; - private boolean hideOpenChanged; - private boolean rvAnimChanged; public SettingChangeEvent() { } @Override public void post() { - if (showReadLaterChanged || showTopChanged || showBannerChanged || - hideAboutMeChanged || hideOpenChanged || rvAnimChanged) { + if (showQAChanged || showTopChanged || showBannerChanged) { super.post(); } } - public boolean isShowReadLaterChanged() { - return showReadLaterChanged; + public boolean isShowQAChanged() { + return showQAChanged; } - public void setShowReadLaterChanged(boolean readLaterChanged) { - this.showReadLaterChanged = readLaterChanged; + public void setShowQAChanged(boolean showQAChanged) { + this.showQAChanged = showQAChanged; } public boolean isShowTopChanged() { @@ -50,28 +44,4 @@ public boolean isShowBannerChanged() { public void setShowBannerChanged(boolean showBannerChanged) { this.showBannerChanged = showBannerChanged; } - - public boolean isHideAboutMeChanged() { - return hideAboutMeChanged; - } - - public void setHideAboutMeChanged(boolean hideAboutMeChanged) { - this.hideAboutMeChanged = hideAboutMeChanged; - } - - public boolean isHideOpenChanged() { - return hideOpenChanged; - } - - public void setHideOpenChanged(boolean hideOpenChanged) { - this.hideOpenChanged = hideOpenChanged; - } - - public boolean isRvAnimChanged() { - return rvAnimChanged; - } - - public void setRvAnimChanged(boolean rvAnimChanged) { - this.rvAnimChanged = rvAnimChanged; - } } diff --git a/app/src/main/java/per/goweii/wanandroid/event/UserInfoUpdateEvent.java b/app/src/main/java/per/goweii/wanandroid/event/UserInfoUpdateEvent.java new file mode 100644 index 00000000..0e9eed4a --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/event/UserInfoUpdateEvent.java @@ -0,0 +1,9 @@ +package per.goweii.wanandroid.event; + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +public class UserInfoUpdateEvent extends BaseEvent { +} diff --git a/app/src/main/java/per/goweii/wanandroid/http/BaseRequest.java b/app/src/main/java/per/goweii/wanandroid/http/BaseRequest.java index af54f823..373730aa 100644 --- a/app/src/main/java/per/goweii/wanandroid/http/BaseRequest.java +++ b/app/src/main/java/per/goweii/wanandroid/http/BaseRequest.java @@ -1,12 +1,15 @@ package per.goweii.wanandroid.http; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.util.List; import io.reactivex.Observable; import io.reactivex.disposables.Disposable; import per.goweii.basic.core.receiver.LoginReceiver; +import per.goweii.basic.core.utils.JsonFormatUtils; +import per.goweii.basic.utils.LogUtils; import per.goweii.rxhttp.core.RxLife; import per.goweii.rxhttp.request.RxRequest; import per.goweii.rxhttp.request.exception.ExceptionHandle; @@ -15,49 +18,18 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class BaseRequest { - protected static Disposable request(@NonNull Observable> observable, @NonNull RequestListener listener) { - return RxRequest.create(observable) - .listener(new RxRequest.RequestListener() { - @Override - public void onStart() { - listener.onStart(); - } - - @Override - public void onError(ExceptionHandle handle) { - listener.onError(handle); - listener.onFailed(WanApi.ApiCode.ERROR, handle.getMsg()); - } - - @Override - public void onFinish() { - listener.onFinish(); - } - }) - .request(new RxRequest.ResultCallback() { - @Override - public void onSuccess(int code, T data) { - listener.onSuccess(code, data); - } - - @Override - public void onFailed(int code, String msg) { - if (code == WanApi.ApiCode.FAILED_NOT_LOGIN) { - UserUtils.getInstance().logout(); - LoginReceiver.sendNotLogin(); - } - listener.onFailed(code, msg); - } - }); + protected static Disposable request(@NonNull Observable> observable, + @NonNull RequestListener listener) { + return request(observable, listener, null); } - protected static Disposable request(@NonNull Observable> observable, @NonNull RequestListener listener, ResponseToCache responseToCache) { + protected static Disposable request(@NonNull Observable> observable, + @NonNull RequestListener listener, + @Nullable ResponseToCache responseToCache) { return RxRequest.create(observable) .listener(new RxRequest.RequestListener() { @Override @@ -67,6 +39,8 @@ public void onStart() { @Override public void onError(ExceptionHandle handle) { + handle.getException().printStackTrace(); + LogUtils.httpe(handle.getMsg()); listener.onError(handle); listener.onFailed(WanApi.ApiCode.ERROR, handle.getMsg()); } @@ -79,7 +53,12 @@ public void onFinish() { .request(new RxRequest.ResultCallback() { @Override public void onSuccess(int code, T data) { - if (responseToCache.onResponse(data)) { + LogUtils.httpi(JsonFormatUtils.INSTANCE.toJson(data)); + boolean diffCache = true; + if (responseToCache != null) { + diffCache = responseToCache.onResponse(data); + } + if (diffCache) { listener.onSuccess(code, data); } } @@ -180,6 +159,19 @@ public boolean onResponse(List resp) { })); } + protected static void netList(RxLife rxLife, + Observable>> observable, + String key, + RequestListener> listener) { + rxLife.add(request(observable, listener, new ResponseToCache>() { + @Override + public boolean onResponse(List resp) { + WanCache.getInstance().save(key, resp); + return true; + } + })); + } + protected static void cacheAndNetBean(RxLife rxLife, Observable> observable, String key, @@ -233,6 +225,50 @@ public boolean onResponse(T resp) { })); } + protected static void cacheOrNetBean(RxLife rxLife, + Observable> observable, + String key, + Class clazz, + RequestListener listener) { + cacheOrNetBean(rxLife, observable, false, key, clazz, listener); + } + + protected static void cacheOrNetBean(RxLife rxLife, + Observable> observable, + boolean refresh, + String key, + Class clazz, + RequestListener listener) { + if (refresh) { + netBean(rxLife, observable, key, listener); + return; + } + rxLife.add(WanCache.getInstance().getBean(key, clazz, new CacheListener() { + @Override + public void onSuccess(int code, T data) { + listener.onSuccess(code, data); + } + + @Override + public void onFailed() { + netBean(rxLife, observable, key, listener); + } + })); + } + + protected static void netBean(RxLife rxLife, + Observable> observable, + String key, + RequestListener listener) { + rxLife.add(request(observable, listener, new ResponseToCache() { + @Override + public boolean onResponse(T resp) { + WanCache.getInstance().save(key, resp); + return true; + } + })); + } + public interface ResponseToCache { boolean onResponse(T resp); } diff --git a/app/src/main/java/per/goweii/wanandroid/http/CacheListener.java b/app/src/main/java/per/goweii/wanandroid/http/CacheListener.java index aeb15ad5..efb2a9b4 100644 --- a/app/src/main/java/per/goweii/wanandroid/http/CacheListener.java +++ b/app/src/main/java/per/goweii/wanandroid/http/CacheListener.java @@ -3,11 +3,10 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface CacheListener { void onSuccess(int code, E data); + void onFailed(); } diff --git a/app/src/main/java/per/goweii/wanandroid/http/GoweiiHostInterceptor.kt b/app/src/main/java/per/goweii/wanandroid/http/GoweiiHostInterceptor.kt new file mode 100644 index 00000000..fa47b2c0 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/http/GoweiiHostInterceptor.kt @@ -0,0 +1,54 @@ +package per.goweii.wanandroid.http + +import android.net.Uri +import okhttp3.HttpUrl +import okhttp3.Interceptor +import okhttp3.Response +import per.goweii.basic.utils.LogUtils +import java.io.IOException + +class GoweiiHostInterceptor : Interceptor { + companion object { + private const val TAG = "GoweiiHostInterceptor" + + private const val GOWEII_HOST = "goweii" + + private const val GITEE_PAGE_SERVER_BASE_URL = "https://goweii.gitee.io/wanandroidserver" + private const val GITEE_RAW_SERVER_BASE_URL = "https://gitee.com/goweii/WanAndroidServer/raw/master" + } + + override fun intercept(chain: Interceptor.Chain): Response { + val request = chain.request() + + val httpUrl = request.url() + if (httpUrl.host() != GOWEII_HOST) { + return chain.proceed(request) + } + + HttpUrl.parse(GITEE_RAW_SERVER_BASE_URL) + ?.let { chain.tryWithBaseUrl(it) } + ?.takeIf { it.isSuccessful } + ?.let { return it } + + HttpUrl.parse(GITEE_PAGE_SERVER_BASE_URL) + ?.let { chain.tryWithBaseUrl(it) } + ?.takeIf { it.isSuccessful } + ?.let { return it } + + throw IOException("Unknown goweii host.") + } + + private fun Interceptor.Chain.tryWithBaseUrl(baseUrl: HttpUrl): Response { + val httpUrl = request().url().replaceWithBaseUrl(baseUrl) + LogUtils.d(TAG, "tryWithBaseUrl: $httpUrl") + return proceed(request().newBuilder().url(httpUrl).build()) + } + + private fun HttpUrl.replaceWithBaseUrl(baseUrl: HttpUrl): HttpUrl { + val uriBuilder = Uri.parse(baseUrl.toString()).buildUpon() + encodedPathSegments().forEach { uriBuilder.appendEncodedPath(it) } + uriBuilder.encodedQuery(encodedQuery()) + val uri = uriBuilder.build() + return HttpUrl.parse(uri.toString())!! + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/http/NullCacheException.java b/app/src/main/java/per/goweii/wanandroid/http/NullCacheException.java index e9a86d94..955cfe65 100644 --- a/app/src/main/java/per/goweii/wanandroid/http/NullCacheException.java +++ b/app/src/main/java/per/goweii/wanandroid/http/NullCacheException.java @@ -3,8 +3,6 @@ /** * @author CuiZhen * @date 2019/5/24 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class NullCacheException extends Exception { diff --git a/app/src/main/java/per/goweii/wanandroid/http/RequestCallback.java b/app/src/main/java/per/goweii/wanandroid/http/RequestCallback.java index 3eb5ed01..091ff0d5 100644 --- a/app/src/main/java/per/goweii/wanandroid/http/RequestCallback.java +++ b/app/src/main/java/per/goweii/wanandroid/http/RequestCallback.java @@ -5,8 +5,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public abstract class RequestCallback implements RequestListener { diff --git a/app/src/main/java/per/goweii/wanandroid/http/RequestListener.java b/app/src/main/java/per/goweii/wanandroid/http/RequestListener.java index 2d6765d6..81ea46cc 100644 --- a/app/src/main/java/per/goweii/wanandroid/http/RequestListener.java +++ b/app/src/main/java/per/goweii/wanandroid/http/RequestListener.java @@ -5,14 +5,16 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface RequestListener { void onStart(); + void onSuccess(int code, E data); + void onFailed(int code, String msg); + void onError(ExceptionHandle handle); + void onFinish(); } diff --git a/app/src/main/java/per/goweii/wanandroid/http/RxHttpRequestSetting.java b/app/src/main/java/per/goweii/wanandroid/http/RxHttpRequestSetting.java index 7e92b489..4341c609 100644 --- a/app/src/main/java/per/goweii/wanandroid/http/RxHttpRequestSetting.java +++ b/app/src/main/java/per/goweii/wanandroid/http/RxHttpRequestSetting.java @@ -1,11 +1,18 @@ package per.goweii.wanandroid.http; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.franmontiel.persistentcookiejar.PersistentCookieJar; +import java.util.HashMap; +import java.util.Map; + +import okhttp3.CacheControl; +import okhttp3.Interceptor; import okhttp3.OkHttpClient; import per.goweii.basic.core.common.Config; +import per.goweii.basic.utils.DebugUtils; import per.goweii.rxhttp.request.setting.DefaultRequestSetting; import per.goweii.rxhttp.request.utils.HttpsCompat; @@ -25,7 +32,7 @@ public RxHttpRequestSetting(PersistentCookieJar cookieJar) { @Override public boolean isDebug() { - return Config.DEBUG; + return DebugUtils.isDebug(); } @Override @@ -36,12 +43,25 @@ public long getTimeout() { @NonNull @Override public String getBaseUrl() { - return Config.BASE_URL; + return WanApi.ApiConfig.BASE_URL; + } + + @Nullable + @Override + public Map getStaticHeaderParameter() { + Map map = new HashMap<>(1); + map.put("Cache-Control", CacheControl.FORCE_NETWORK.toString()); + return map; } @Override public int getSuccessCode() { - return 0; + return WanApi.ApiCode.SUCCESS; + } + + @Override + public int[] getMultiSuccessCode() { + return new int[] {WanApi.ApiCode.DAILY_NEWS_SUCCESS}; } @Override @@ -51,4 +71,12 @@ public void setOkHttpClient(OkHttpClient.Builder builder) { HttpsCompat.enableTls12ForHttpsURLConnection(); builder.cookieJar(mCookieJar); } + + @Nullable + @Override + public Interceptor[] getInterceptors() { + return new Interceptor[]{ + new GoweiiHostInterceptor() + }; + } } diff --git a/app/src/main/java/per/goweii/wanandroid/http/WanApi.java b/app/src/main/java/per/goweii/wanandroid/http/WanApi.java index 69ecdeda..f1c8e748 100644 --- a/app/src/main/java/per/goweii/wanandroid/http/WanApi.java +++ b/app/src/main/java/per/goweii/wanandroid/http/WanApi.java @@ -5,21 +5,30 @@ import io.reactivex.Observable; import per.goweii.rxhttp.request.Api; import per.goweii.rxhttp.request.base.BaseBean; +import per.goweii.wanandroid.common.Constant; +import per.goweii.wanandroid.module.book.model.BookBean; +import per.goweii.wanandroid.module.explore.DailyNewsResponse; import per.goweii.wanandroid.module.home.model.BannerBean; import per.goweii.wanandroid.module.home.model.HotKeyBean; import per.goweii.wanandroid.module.login.model.LoginBean; +import per.goweii.wanandroid.module.main.model.AdvertBean; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.ArticleListBean; +import per.goweii.wanandroid.module.main.model.BetaUserBean; import per.goweii.wanandroid.module.main.model.ChapterBean; import per.goweii.wanandroid.module.main.model.CollectionLinkBean; import per.goweii.wanandroid.module.main.model.ConfigBean; import per.goweii.wanandroid.module.main.model.JinrishiciBean; +import per.goweii.wanandroid.module.main.model.ListBean; +import per.goweii.wanandroid.module.main.model.RecommendBean; import per.goweii.wanandroid.module.main.model.UpdateBean; import per.goweii.wanandroid.module.main.model.UsefulWebBean; import per.goweii.wanandroid.module.main.model.UserPageBean; +import per.goweii.wanandroid.module.main.model.WebArticleUrlRegexBean; import per.goweii.wanandroid.module.mine.model.AboutMeBean; import per.goweii.wanandroid.module.mine.model.CoinRankBean; import per.goweii.wanandroid.module.mine.model.CoinRecordBean; +import per.goweii.wanandroid.module.mine.model.MessageBean; import per.goweii.wanandroid.module.mine.model.UserInfoBean; import per.goweii.wanandroid.module.navigation.model.NaviBean; import retrofit2.http.Field; @@ -32,20 +41,25 @@ /** * @author CuiZhen * @date 2019/5/7 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WanApi extends Api { + private static final String HEADER_USER_AGENT = + "User-Agent: " + Constant.BROWSER_UA; public static ApiService api() { return api(ApiService.class); } + public static class ApiConfig { + public static final String BASE_URL = "https://wanandroid.com/"; + } + public static class ApiCode { public static final int ERROR = 1000; public static final int SUCCESS = 0; + public static final int DAILY_NEWS_SUCCESS = 200; public static final int FAILED_NO_CACHE = -9000; //没有缓存 @@ -54,21 +68,47 @@ public static class ApiCode { public interface ApiService { + @GET("https://orz.ai/api/v1/dailynews") + Observable getDailyNews(@Query("platform") String platform); + @GET("https://v2.jinrishici.com/token") Observable> getJinrishiciToken(); @GET("https://v2.jinrishici.com/sentence") - Observable> getJinrishici(@retrofit2.http.Header("Token") String token); + Observable> getJinrishici(@retrofit2.http.Header("X-User-Token") String token); + + @GET("https://goweii/web/article.json") + @retrofit2.http.Headers({HEADER_USER_AGENT}) + Observable>> getWebArticleUrlRegex(); - @GET("https://gitee.com/goweii/WanAndroidServer/raw/master/update/update.json") + @GET("https://goweii/update/update.json") + @retrofit2.http.Headers({HEADER_USER_AGENT}) Observable> update(); - @GET("https://gitee.com/goweii/WanAndroidServer/raw/master/about/about_me.json") + @GET("https://goweii/about/about_me.json") + @retrofit2.http.Headers({HEADER_USER_AGENT}) Observable> getAboutMe(); - @GET("https://gitee.com/goweii/WanAndroidServer/raw/master/config/config.json") + @GET("https://goweii/config/config.json") + @retrofit2.http.Headers({HEADER_USER_AGENT}) Observable> getConfig(); + @GET("https://goweii/advert/advert.json") + @retrofit2.http.Headers({HEADER_USER_AGENT}) + Observable> getAdvert(); + + @GET("https://goweii/advert/recommend.json") + @retrofit2.http.Headers({HEADER_USER_AGENT}) + Observable> getRecommend(); + + @GET("https://goweii/update/beta/update.json") + @retrofit2.http.Headers({HEADER_USER_AGENT}) + Observable> betaUpdate(); + + @GET("https://goweii/update/beta/users.json") + @retrofit2.http.Headers({HEADER_USER_AGENT}) + Observable>> betaUsers(); + /** * 登录 * 方法: POST @@ -217,8 +257,9 @@ Observable> search(@Path("page") int page, * 页码:拼接在链接上,从0开始。 */ @GET("article/list/{page}/json") - Observable> getKnowledgeArticleList(@Path("page") int page, - @Query("cid") int id); + Observable> getChapterArticleList(@Path("page") int page, + @Query("cid") int id, + @Query("order_type") int orderType); /** * 收藏文章列表 @@ -241,7 +282,7 @@ Observable> getKnowledgeArticleList(@Path("page") i * 参数: 文章id,拼接在链接中。 */ @POST("lg/collect/{id}/json") - Observable> collect(@Path("id") int id); + Observable> collectArticle(@Path("id") int id); /** * 收藏站外文章 @@ -251,9 +292,9 @@ Observable> getKnowledgeArticleList(@Path("page") i */ @FormUrlEncoded @POST("lg/collect/add/json") - Observable> collect(@Field("title") String title, - @Field("author") String author, - @Field("link") String link); + Observable> collectArticle(@Field("title") String title, + @Field("author") String author, + @Field("link") String link); /** * 收藏网址 @@ -263,8 +304,8 @@ Observable> collect(@Field("title") String title, */ @FormUrlEncoded @POST("lg/collect/addtool/json") - Observable> collect(@Field("name") String name, - @Field("link") String link); + Observable> collectLink(@Field("name") String name, + @Field("link") String link); /** * 取消收藏 文章列表 @@ -273,7 +314,7 @@ Observable> collect(@Field("name") String name, * id:拼接在链接上 id传入的是列表中文章的id。 */ @POST("lg/uncollect_originId/{id}/json") - Observable> uncollect(@Path("id") int id); + Observable> uncollectArticle(@Path("id") int id); /** * 删除收藏网站 @@ -286,29 +327,29 @@ Observable> collect(@Field("name") String name, Observable> uncollectLink(@Field("id") int id); /** - * 编辑收藏网站 + * 取消收藏 我的收藏页面(该页面包含自己录入的内容) * 方法:POST * 参数: - * id,name,link + * id:拼接在链接上 + * originId:列表页下发,无则为-1 + * originId 代表的是你收藏之前的那篇文章本身的id; 但是收藏支持主动添加,这种情况下,没有originId则为-1 */ @FormUrlEncoded - @POST("lg/collect/updatetool/json") - Observable> updateCollectLink(@Field("id") int id, - @Field("name") String name, - @Field("link") String link); + @POST("lg/uncollect/{id}/json") + Observable> uncollectArticle(@Path("id") int id, + @Field("originId") int originId); /** - * 取消收藏 我的收藏页面(该页面包含自己录入的内容) + * 编辑收藏网站 * 方法:POST * 参数: - * id:拼接在链接上 - * originId:列表页下发,无则为-1 - * originId 代表的是你收藏之前的那篇文章本身的id; 但是收藏支持主动添加,这种情况下,没有originId则为-1 + * id,name,link */ @FormUrlEncoded - @POST("lg/uncollect/{id}/json") - Observable> uncollect(@Path("id") int id, - @Field("originId") int originId); + @POST("lg/collect/updatetool/json") + Observable> updateCollectLink(@Field("id") int id, + @Field("name") String name, + @Field("link") String link); /** * 获取个人积分 @@ -322,6 +363,30 @@ Observable> uncollect(@Path("id") int id, @GET("lg/coin/userinfo/json") Observable> getUserInfo(); + /** + * 未读消息数量 + */ + @GET("message/lg/count_unread/json") + Observable> getMessageUnreadCount(); + + /** + * 未读消息列表 + */ + @GET("message/lg/unread_list/{page}/json") + Observable>> getMessageUnreadList(@Path("page") int page); + + /** + * 已读消息列表 + */ + @GET("message/lg/readed_list/{page}/json") + Observable>> getMessageReadList(@Path("page") int page); + + /** + * 删除消息 + */ + @GET("/message/lg/delete/{id}") + Observable> deleteMessage(@Path("id") int id); + /** * 获取个人积分获取列表 * page 1开始 @@ -376,6 +441,19 @@ Observable> getUserPage(@Path("userId") int userId, @POST("lg/user_article/add/json") Observable> shareArticle(@Field("title") String title, @Field("link") String link); + + /** + * 问答 + * pageId,拼接在链接上,例如上面的1 + */ + @GET("wenda/list/{page}/json") + Observable> getQuestionList(@Path("page") int page); + + /** + * 教程列表 + */ + @GET("chapter/547/sublist/json") + Observable>> getBooks(); } } diff --git a/app/src/main/java/per/goweii/wanandroid/http/WanCache.java b/app/src/main/java/per/goweii/wanandroid/http/WanCache.java index 5fd5e7df..d0e01878 100644 --- a/app/src/main/java/per/goweii/wanandroid/http/WanCache.java +++ b/app/src/main/java/per/goweii/wanandroid/http/WanCache.java @@ -24,12 +24,11 @@ import per.goweii.basic.utils.coder.MD5Coder; import per.goweii.basic.utils.file.CacheUtils; import per.goweii.basic.utils.listener.SimpleListener; +import per.goweii.wanandroid.utils.UserUtils; /** * @author CuiZhen * @date 2019/5/24 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WanCache { @@ -50,7 +49,7 @@ public static void init() { public static WanCache getInstance() { if (INSTANCE == null) { - throw new RuntimeException("WanCache未初始化"); + throw new RuntimeException("WanCache is not initialized"); } return INSTANCE; } @@ -61,7 +60,7 @@ private WanCache() { private DiskLruCache getDiskLruCache() { if (mDiskLruCache == null) { - throw new RuntimeException("WanCache未初始化或初始化失败"); + throw new RuntimeException("WanCache is not initialized"); } return mDiskLruCache; } @@ -209,9 +208,10 @@ private List jsonToBeanList(String json, Class t) { @SuppressLint("DefaultLocale") public static class CacheKey { - public static final String JINRISHICI_TOKEN = "https://v2.jinrishici.com/token"; - public static final String UPDATE = "https://raw.githubusercontent.com/goweii/WanAndroidServer/master/update/update.json"; - public static final String ABOUT_ME = "https://raw.githubusercontent.com/goweii/WanAndroidServer/master/about/about_me.json"; + private static final String DAILY_NEWS = "dailynews/%s"; + public static final String JINRISHICI_TOKEN = "jinrishici/token"; + public static final String UPDATE = "update/update.json"; + public static final String ABOUT_ME = "about/about_me.json"; public static final String WXARTICLE_CHAPTERS = "wxarticle/chapters/json"; private static final String WXARTICLE_LIST = "wxarticle/list/%d/%d/json";//id+page private static final String WXARTICLE_LIST_SEARCH = "wxarticle/list/%d/%d/json?key=%s";//id+page+key @@ -219,18 +219,25 @@ public static class CacheKey { private static final String PROJECT_ARTICLE_LIST = "project/list/%d/json?cid=%d";//page+id public static final String TOP_ARTICLE_LIST = "article/top/json"; private static final String ARTICLE_LIST = "article/list/%d/json";//page + private static final String QUESTION_LIST = "wenda/list/%d/json";//page public static final String BANNER = "banner/json"; public static final String USEFUL_WEB_LIST = "friend/json"; public static final String HOT_KEY_LIST = "hotkey/json"; private static final String SEARCH = "article/query/%d/json?key=%s";//page+key public static final String NAVI_LIST = "navi/json"; public static final String KNOWLEDGE_LIST = "tree/json"; - private static final String KNOWLEDGE_ARTICLE_LIST = "article/list/%d/json?cid=%d";//page+id + private static final String CHAPTER_ARTICLE_LIST = "article/list/%d/json?cid=%d&order_type=%d";//page+id+orderType private static final String COLLECT_ARTICLE_LIST = "lg/collect/list/%d/json";//page - public static final String COLLECT_LINK_LIST = "lg/collect/usertools/json"; + private static final String COLLECT_LINK_LIST = "lg/collect/usertools/json"; private static final String USER_ARTICLE_LIST = "user_article/list/%d/json";//page private static final String USER_PAGE = "user/%d/share_articles/%d/json";//userId+page - private static final String MINE_SHARE_ARTICLE_LIST = "user/lg/private_articles/{page}/json";//page + private static final String MINE_SHARE_ARTICLE_LIST = "user/lg/private_articles/%d/json";//page + public static final String BOOK_LIST = "chapter/547/sublist/json"; + + private static String addUserId(String key) { + int userId = UserUtils.getInstance().getWanId(); + return userId + "@" + key; + } public static String WXARTICLE_LIST(int id, int page) { return String.format(WXARTICLE_LIST, id, page); @@ -252,24 +259,36 @@ public static String ARTICLE_LIST(int page) { return String.format(ARTICLE_LIST, page); } - public static String KNOWLEDGE_ARTICLE_LIST(int id, int page) { - return String.format(KNOWLEDGE_ARTICLE_LIST, page, id); + public static String CHAPTER_ARTICLE_LIST(int id, int page, int orderType) { + return String.format(CHAPTER_ARTICLE_LIST, page, id, orderType); } public static String COLLECT_ARTICLE_LIST(int page) { - return String.format(COLLECT_ARTICLE_LIST, page); + return addUserId(String.format(COLLECT_ARTICLE_LIST, page)); + } + + public static String COLLECT_LINK_LIST() { + return addUserId(COLLECT_LINK_LIST); } public static String USER_ARTICLE_LIST(int page) { return String.format(USER_ARTICLE_LIST, page); } + public static String QUESTION_LIST(int page) { + return String.format(QUESTION_LIST, page); + } + public static String USER_PAGE(int userId, int page) { return String.format(USER_PAGE, userId, page); } public static String MINE_SHARE_ARTICLE_LIST(int page) { - return String.format(MINE_SHARE_ARTICLE_LIST, page); + return addUserId(String.format(MINE_SHARE_ARTICLE_LIST, page)); + } + + public static String DAILY_NEWS(String platformCode) { + return String.format(DAILY_NEWS, platformCode); } } diff --git a/app/src/main/java/per/goweii/wanandroid/http/WanResponse.java b/app/src/main/java/per/goweii/wanandroid/http/WanResponse.java index c1c658e2..a59fcbb0 100644 --- a/app/src/main/java/per/goweii/wanandroid/http/WanResponse.java +++ b/app/src/main/java/per/goweii/wanandroid/http/WanResponse.java @@ -5,8 +5,6 @@ /** * @author CuiZhen * @date 2019/5/8 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WanResponse implements BaseResponse { diff --git a/app/src/main/java/per/goweii/wanandroid/module/book/activity/BookDetailsActivity.kt b/app/src/main/java/per/goweii/wanandroid/module/book/activity/BookDetailsActivity.kt new file mode 100644 index 00000000..4f84fa9c --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/book/activity/BookDetailsActivity.kt @@ -0,0 +1,169 @@ +package per.goweii.wanandroid.module.book.activity + +import android.content.Context +import android.content.Intent +import android.os.Parcelable +import android.view.LayoutInflater +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.appbar.AppBarLayout.OnOffsetChangedListener +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import per.goweii.basic.core.base.BaseActivity +import per.goweii.basic.utils.ResUtils +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.ActivityBookDetailsBinding +import per.goweii.wanandroid.db.model.ReadRecordModel +import per.goweii.wanandroid.event.ReadRecordAddedEvent +import per.goweii.wanandroid.event.ReadRecordUpdateEvent +import per.goweii.wanandroid.module.book.adapter.BookChapterAdapter +import per.goweii.wanandroid.module.book.contract.BookDetailsPresenter +import per.goweii.wanandroid.module.book.contract.BookDetailsView +import per.goweii.wanandroid.module.book.model.BookBean +import per.goweii.wanandroid.module.book.model.BookChapterBean +import per.goweii.wanandroid.utils.ImageLoader +import per.goweii.wanandroid.utils.MultiStateUtils +import per.goweii.wanandroid.utils.MultiStateUtils.Companion.toContent +import per.goweii.wanandroid.utils.MultiStateUtils.Companion.toEmpty +import per.goweii.wanandroid.utils.MultiStateUtils.Companion.toError +import per.goweii.wanandroid.utils.UrlOpenUtils +import kotlin.math.abs + +class BookDetailsActivity : BaseActivity(), BookDetailsView { + companion object { + private const val PAGE_START = 0 + private const val PARAM_BOOK = "book" + + fun start(context: Context, bookBean: BookBean) { + context.startActivity(Intent(context, BookDetailsActivity::class.java).apply { + putExtra(PARAM_BOOK, bookBean as Parcelable) + }) + } + } + + private lateinit var bookBean: BookBean + private lateinit var adapter: BookChapterAdapter + + private var currPage = PAGE_START + + override fun initViewBinding(inflater: LayoutInflater) = ActivityBookDetailsBinding.inflate(inflater) + + override fun initPresenter(): BookDetailsPresenter = BookDetailsPresenter() + + override fun initView() { + bookBean = intent.getParcelableExtra(PARAM_BOOK)!! + + ImageLoader.userIcon(binding.rivBookImg, bookBean.cover) + binding.abc.titleTextView.text = bookBean.name + binding.tvBookName.text = bookBean.name + binding.tvBookAuthor.text = bookBean.author + binding.tvBookDesc.text = bookBean.desc + binding.tvBookCopyright.text = bookBean.lisense + binding.tvBookCopyright.setOnClickListener { + UrlOpenUtils.with(bookBean.lisenseLink).open(this) + } + + binding.rv.layoutManager = LinearLayoutManager(context) + adapter = BookChapterAdapter() + adapter.setEnableLoadMore(false) + adapter.setOnLoadMoreListener({ + presenter.getChapters(bookBean.id, currPage) + }, binding.rv) + adapter.setOnItemClickListener { _, _, position -> + val item: BookChapterBean = adapter.getItem(position) ?: return@setOnItemClickListener + UrlOpenUtils.with(item.articleBean).open(context) + } + binding.rv.adapter = adapter + MultiStateUtils.setEmptyAndErrorClick(binding.msv) { + MultiStateUtils.toLoading(binding.msv) + presenter.getChapters(bookBean.id, currPage) + } + binding.abl.addOnOffsetChangedListener(OnOffsetChangedListener { abl, offset -> + if (abs(offset) == abl.totalScrollRange) { + binding.abc.titleTextView.alpha = 1f + val color = ResUtils.getThemeColor(binding.abc, R.attr.colorMainOrSurface) + binding.abc.setBackgroundColor(color) + binding.llTop.alpha = 1F + } else { + binding.abc.titleTextView.alpha = 0f + val color = ResUtils.getThemeColor(binding.abc, R.attr.colorTransparent) + binding.abc.setBackgroundColor(color) + binding.llTop.alpha = 1f - (abs(offset).toFloat() / abl.totalScrollRange.toFloat()) + } + }) + binding.ctbl.post { + binding.ctbl.minimumHeight = binding.abc.actionBar.height + binding.ctbl.scrimVisibleHeightTrigger = binding.abc.actionBar.height + } + } + + override fun loadData() { + MultiStateUtils.toLoading(binding.msv) + presenter.getChapters(bookBean.id, currPage) + } + + override fun isRegisterEventBus(): Boolean = true + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onReadRecordAddedEvent(event: ReadRecordAddedEvent) { + kotlin.run { + adapter.data.forEachIndexed { index, bookChapterBean -> + if (bookChapterBean.articleBean.link == event.readRecordModel.link) { + bookChapterBean.time = event.readRecordModel.time + bookChapterBean.percent = event.readRecordModel.percentFloat + adapter.notifyItemChanged(index) + return@run + } + } + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onReadRecordUpdateEvent(event: ReadRecordUpdateEvent) { + kotlin.run { + adapter.data.forEachIndexed { index, bookChapterBean -> + if (bookChapterBean.articleBean.link == event.link) { + if (event.time != null) { + bookChapterBean.time = event.time + } + if (event.percent != null) { + bookChapterBean.percent = + (event.percent.toFloat() / ReadRecordModel.MAX_PERCENT) + .coerceIn(0f, 1f) + } + adapter.notifyItemChanged(index) + return@run + } + } + } + } + + override fun getBookChaptersSuccess( + list: List, + curPage: Int, + isOver: Boolean + ) { + currPage = curPage + PAGE_START + if (curPage == 1) { + adapter.setNewData(list) + adapter.setEnableLoadMore(true) + if (list.isEmpty()) { + toEmpty(binding.msv) + } else { + toContent(binding.msv) + } + } else { + adapter.addData(list) + adapter.loadMoreComplete() + } + if (isOver) { + adapter.loadMoreEnd() + } + } + + override fun getBookChaptersFailed() { + adapter.loadMoreFail() + if (currPage == PAGE_START) { + toError(binding.msv) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/book/adapter/BookAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/book/adapter/BookAdapter.java new file mode 100644 index 00000000..6a7c81a6 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/book/adapter/BookAdapter.java @@ -0,0 +1,21 @@ +package per.goweii.wanandroid.module.book.adapter; + +import androidx.annotation.NonNull; + +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; + +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.module.book.model.BookBean; +import per.goweii.wanandroid.utils.ImageLoader; + +public class BookAdapter extends BaseQuickAdapter { + public BookAdapter() { + super(R.layout.rv_item_book); + } + + @Override + protected void convert(@NonNull BaseViewHolder helper, @NonNull BookBean item) { + ImageLoader.image(helper.getView(R.id.piv_img), item.getCover()); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/book/adapter/BookChapterAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/book/adapter/BookChapterAdapter.java new file mode 100644 index 00000000..6d3b8f12 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/book/adapter/BookChapterAdapter.java @@ -0,0 +1,66 @@ +package per.goweii.wanandroid.module.book.adapter; + +import android.annotation.SuppressLint; +import android.text.Html; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import me.zhanghai.android.materialprogressbar.MaterialProgressBar; +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.module.book.model.BookChapterBean; +import per.goweii.wanandroid.module.main.model.ArticleBean; + +public class BookChapterAdapter extends BaseQuickAdapter { + @SuppressLint("SimpleDateFormat") + private final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + public BookChapterAdapter() { + super(R.layout.rv_item_book_chapter); + } + + @SuppressLint("SetTextI18n") + @Override + protected void convert(@NonNull BaseViewHolder helper, @NonNull BookChapterBean item) { + TextView tv_title = helper.getView(R.id.tv_title); + TextView tv_state = helper.getView(R.id.tv_state); + TextView tv_time = helper.getView(R.id.tv_time); + LinearLayout ll_state = helper.getView(R.id.ll_state); + MaterialProgressBar pb_percent = helper.getView(R.id.pb_percent); + + tv_title.setText((helper.getAdapterPosition() + 1) + ". " + Html.fromHtml(item.getArticleBean().getTitle())); + if (item.getTime() == 0) { + ll_state.setVisibility(View.VISIBLE); + tv_state.setText(R.string.not_learned); + tv_state.setTextColor(ResUtils.getThemeColor(tv_state, R.attr.colorTextThird)); + pb_percent.setVisibility(View.INVISIBLE); + tv_time.setVisibility(View.INVISIBLE); + } else { + ll_state.setVisibility(View.VISIBLE); + tv_time.setVisibility(View.VISIBLE); + tv_time.setText(sdf.format(new Date(item.getTime()))); + if (item.getPercent() >= 0.98) { + tv_state.setText(R.string.already_learned); + tv_state.setTextColor(ResUtils.getThemeColor(tv_state, R.attr.colorTextAccent)); + pb_percent.setVisibility(View.INVISIBLE); + } else { + ll_state.setVisibility(View.VISIBLE); + int p = (int) (item.getPercent() * 100); + tv_state.setText(mContext.getString(R.string.learned_n_percent, p)); + tv_state.setTextColor(ResUtils.getThemeColor(tv_state, R.attr.colorTextMain)); + pb_percent.setVisibility(View.VISIBLE); + } + } + pb_percent.setMax(10000); + pb_percent.setProgress((int) (item.getPercent() * pb_percent.getMax())); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/book/contract/BookContract.kt b/app/src/main/java/per/goweii/wanandroid/module/book/contract/BookContract.kt new file mode 100644 index 00000000..e0484ddd --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/book/contract/BookContract.kt @@ -0,0 +1,40 @@ +package per.goweii.wanandroid.module.book.contract + +import per.goweii.basic.core.base.BasePresenter +import per.goweii.basic.core.base.BaseView +import per.goweii.rxhttp.request.exception.ExceptionHandle +import per.goweii.wanandroid.http.RequestListener +import per.goweii.wanandroid.module.book.model.BookBean +import per.goweii.wanandroid.module.book.model.BookRequest + +interface BookView : BaseView { + fun getBookListSuccess(list: List) + fun getBookListFailed() +} + +class BookPresenter : BasePresenter() { + fun getList() { + BookRequest.getBookList(rxLife, object : RequestListener> { + override fun onStart() { + } + + override fun onSuccess(code: Int, data: List) { + if (isAttach) { + baseView.getBookListSuccess(data) + } + } + + override fun onFailed(code: Int, msg: String?) { + if (isAttach) { + baseView.getBookListFailed() + } + } + + override fun onError(handle: ExceptionHandle?) { + } + + override fun onFinish() { + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/book/contract/BookDetailsContract.kt b/app/src/main/java/per/goweii/wanandroid/module/book/contract/BookDetailsContract.kt new file mode 100644 index 00000000..af99add3 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/book/contract/BookDetailsContract.kt @@ -0,0 +1,84 @@ +package per.goweii.wanandroid.module.book.contract + +import kotlinx.coroutines.* +import per.goweii.basic.core.base.BasePresenter +import per.goweii.basic.core.base.BaseView +import per.goweii.rxhttp.request.exception.ExceptionHandle +import per.goweii.wanandroid.db.executor.ReadRecordExecutor +import per.goweii.wanandroid.db.model.ReadRecordModel +import per.goweii.wanandroid.http.RequestListener +import per.goweii.wanandroid.module.book.model.BookChapterBean +import per.goweii.wanandroid.module.book.model.BookRequest +import per.goweii.wanandroid.module.main.model.ArticleListBean +import kotlin.coroutines.resumeWithException + +interface BookDetailsView : BaseView { + fun getBookChaptersSuccess(list: List, curPage: Int, isOver: Boolean) + fun getBookChaptersFailed() +} + +class BookDetailsPresenter : BasePresenter() { + private lateinit var mainScope: CoroutineScope + private lateinit var readRecordExecutor: ReadRecordExecutor + + override fun attach(baseView: BookDetailsView) { + super.attach(baseView) + mainScope = MainScope() + readRecordExecutor = ReadRecordExecutor() + } + + override fun detach() { + super.detach() + mainScope.cancel() + readRecordExecutor.destroy() + } + + fun getChapters(id: Int, page: Int) { + BookRequest.getBookChapterList(rxLife, id, page, object : RequestListener { + override fun onStart() { + } + + override fun onSuccess(code: Int, data: ArticleListBean) { + fillReadRecord(data) + } + + override fun onFailed(code: Int, msg: String?) { + if (isAttach) { + baseView.getBookChaptersFailed() + } + } + + override fun onError(handle: ExceptionHandle?) { + } + + override fun onFinish() { + } + }) + } + + private fun fillReadRecord(data: ArticleListBean) = mainScope.launch { + val list = withContext(Dispatchers.Default) { + data.datas.map { BookChapterBean(it) } + } + val records = getReadRecords(list) + list.forEach { bookChapterBean -> + records.find { it.link == bookChapterBean.articleBean.link }?.let { readRecordModel -> + bookChapterBean.time = readRecordModel.lastTime + bookChapterBean.percent = readRecordModel.percentFloat + } + } + if (isAttach) { + baseView.getBookChaptersSuccess(list, data.curPage, data.isOver) + } + } + + private suspend fun getReadRecords(articles: List) = + suspendCancellableCoroutine> { continuation -> + val links = articles.map { it.articleBean.link } + readRecordExecutor.findByLinks(links, { + continuation.resumeWith(Result.success(it)) + }, { + continuation.resumeWithException(it) + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/book/fragment/BookFragment.kt b/app/src/main/java/per/goweii/wanandroid/module/book/fragment/BookFragment.kt new file mode 100644 index 00000000..de256c41 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/book/fragment/BookFragment.kt @@ -0,0 +1,101 @@ +package per.goweii.wanandroid.module.book.fragment + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.GridLayoutManager +import com.scwang.smart.refresh.layout.api.RefreshFooter +import com.scwang.smart.refresh.layout.constant.RefreshState +import per.goweii.basic.core.base.BaseFragment +import per.goweii.basic.core.utils.SmartRefreshUtils +import per.goweii.wanandroid.databinding.FragmentBookBinding +import per.goweii.wanandroid.event.CloseSecondFloorEvent +import per.goweii.wanandroid.module.book.activity.BookDetailsActivity +import per.goweii.wanandroid.module.book.adapter.BookAdapter +import per.goweii.wanandroid.module.book.model.BookBean +import per.goweii.wanandroid.module.book.contract.BookPresenter +import per.goweii.wanandroid.module.book.contract.BookView +import per.goweii.wanandroid.utils.MultiStateUtils +import per.goweii.wanandroid.utils.RvConfigUtils +import per.goweii.wanandroid.widget.refresh.SimpleOnMultiListener + +class BookFragment : BaseFragment(), BookView { + private lateinit var mAdapter: BookAdapter + + override fun initViewBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentBookBinding { + return FragmentBookBinding.inflate(inflater, container, false) + } + + override fun initPresenter(): BookPresenter = BookPresenter() + + override fun initView() { + SmartRefreshUtils.with(binding.srl).pureScrollMode() + binding.srl.setOnMultiListener(object : SimpleOnMultiListener() { + override fun onFooterMoving( + footer: RefreshFooter?, + isDragging: Boolean, + percent: Float, + offset: Int, + footerHeight: Int, + maxDragHeight: Int + ) { + super.onFooterMoving( + footer, + isDragging, + percent, + offset, + footerHeight, + maxDragHeight + ) + if (binding.srl.state != RefreshState.PullUpCanceled && isDragging && percent > 1.2F) { + binding.srl.closeHeaderOrFooter() + CloseSecondFloorEvent().post() + } + } + }) + binding.rv.layoutManager = GridLayoutManager(context, 3) + mAdapter = BookAdapter() + RvConfigUtils.init(mAdapter) + mAdapter.setEnableLoadMore(false) + mAdapter.setOnItemClickListener { _, _, position -> + mAdapter.getItem(position)?.let { item -> + BookDetailsActivity.start(requireContext(), item) + } + } + binding.rv.adapter = mAdapter + MultiStateUtils.setEmptyAndErrorClick(binding.msv) { + MultiStateUtils.toLoading(binding.msv) + presenter.getList() + } + } + + override fun loadData() { + MultiStateUtils.toLoading(binding.msv) + } + + override fun onVisible(isFirstVisible: Boolean) { + super.onVisible(isFirstVisible) + presenter.getList() + } + + override fun onInvisible() { + super.onInvisible() + } + + override fun isRegisterEventBus(): Boolean = false + + override fun getBookListSuccess(list: List) { + mAdapter.setNewData(list) + if (list.isEmpty()) { + MultiStateUtils.toEmpty(binding.msv, true) + } else { + MultiStateUtils.toContent(binding.msv) + } + } + + override fun getBookListFailed() { + MultiStateUtils.toError(binding.msv) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/book/model/BookBean.kt b/app/src/main/java/per/goweii/wanandroid/module/book/model/BookBean.kt new file mode 100644 index 00000000..0874687d --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/book/model/BookBean.kt @@ -0,0 +1,21 @@ +package per.goweii.wanandroid.module.book.model + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize +import per.goweii.rxhttp.request.base.BaseBean + +@Parcelize +data class BookBean( + val id: Int, + val courseId: Int, + val cover: String, + val author: String, + val name: String, + val desc: String, + val lisense: String, + val lisenseLink: String, + val order: Int, + val parentChapterId: Int, + val userControlSetTop: Boolean, + val visible: Int, +): BaseBean(), Parcelable diff --git a/app/src/main/java/per/goweii/wanandroid/module/book/model/BookChapterBean.kt b/app/src/main/java/per/goweii/wanandroid/module/book/model/BookChapterBean.kt new file mode 100644 index 00000000..279c010d --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/book/model/BookChapterBean.kt @@ -0,0 +1,9 @@ +package per.goweii.wanandroid.module.book.model + +import per.goweii.wanandroid.module.main.model.ArticleBean + +data class BookChapterBean( + val articleBean: ArticleBean, + var time: Long = 0L, + var percent: Float = 0F, +) \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/book/model/BookRequest.java b/app/src/main/java/per/goweii/wanandroid/module/book/model/BookRequest.java new file mode 100644 index 00000000..3e369a8f --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/book/model/BookRequest.java @@ -0,0 +1,40 @@ +package per.goweii.wanandroid.module.book.model; + +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; + +import java.util.List; + +import per.goweii.rxhttp.core.RxLife; +import per.goweii.wanandroid.http.BaseRequest; +import per.goweii.wanandroid.http.RequestListener; +import per.goweii.wanandroid.http.WanApi; +import per.goweii.wanandroid.http.WanCache; +import per.goweii.wanandroid.module.knowledge.model.KnowledgeRequest; +import per.goweii.wanandroid.module.main.model.ArticleListBean; +import per.goweii.wanandroid.module.main.model.ChapterBean; + +public class BookRequest extends BaseRequest { + public static void getBookList(RxLife rxLife, @NonNull RequestListener> listener) { + cacheAndNetList(rxLife, + WanApi.api().getBooks(), + false, + WanCache.CacheKey.BOOK_LIST, + BookBean.class, + listener); + } + + public static void getBookChapterList(RxLife rxLife, int id, @IntRange(from = 0) int page, @NonNull RequestListener listener) { + if (page == 0) { + cacheAndNetBean(rxLife, + WanApi.api().getChapterArticleList(page, id, 1), + false, + WanCache.CacheKey.CHAPTER_ARTICLE_LIST(id, page, 1), + ArticleListBean.class, + listener); + } else { + rxLife.add(request(WanApi.api().getChapterArticleList(page, id, 1), listener)); + } + } + +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsAdapter.java new file mode 100644 index 00000000..b3aaaf14 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsAdapter.java @@ -0,0 +1,73 @@ +package per.goweii.wanandroid.module.explore; + +import android.text.Html; +import android.text.TextUtils; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; + +import java.util.List; + +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.web.cache.HtmlCacheManager; +import per.goweii.wanandroid.widget.BravhLoadMoreView; +import per.goweii.wanandroid.widget.CollectView; + +public class DailyNewsAdapter extends BaseQuickAdapter { + public DailyNewsAdapter() { + super(R.layout.rv_item_article, null); + setLoadMoreView(new BravhLoadMoreView()); + } + + @Override + public void setNewData(@Nullable List data) { + setNewDiffData(new DailyNewsDiffCallback(data)); + } + + @Override + protected void convert(@NonNull BaseViewHolder helper, DailyNewsBean item) { + final View view = helper.itemView; + HtmlCacheManager.INSTANCE.submit(item.getUrl()); + LinearLayout ll_top = view.findViewById(R.id.ll_top); + LinearLayout ll_chapter = view.findViewById(R.id.ll_chapter); + TextView tv_top = view.findViewById(R.id.tv_top); + TextView tv_new = view.findViewById(R.id.tv_new); + TextView tv_author = view.findViewById(R.id.tv_author); + TextView tv_tag = view.findViewById(R.id.tv_tag); + TextView tv_time = view.findViewById(R.id.tv_time); + ImageView iv_img = view.findViewById(R.id.iv_img); + TextView tv_title = view.findViewById(R.id.tv_title); + TextView tv_desc = view.findViewById(R.id.tv_desc); + TextView tv_chapter_name = view.findViewById(R.id.tv_chapter_name); + CollectView cv_collect = view.findViewById(R.id.cv_collect); + ll_chapter.setVisibility(View.GONE); + ll_top.setVisibility(View.GONE); + tv_chapter_name.setVisibility(View.GONE); + cv_collect.setVisibility(View.GONE); + tv_top.setVisibility(View.VISIBLE); + tv_new.setVisibility(View.VISIBLE); + DailyNewsPlatform platform = DailyNewsPlatform.fromCodeOrNull(item.getSource()); + if (platform != null) { + tv_author.setText(platform.getPlatformName()); + } else if (!TextUtils.isEmpty(item.getSource())){ + tv_author.setText(item.getSource()); + } else { + tv_author.setText(SettingUtils.getInstance().getDailyNewsPlatform().getPlatformName()); + } + tv_tag.setVisibility(View.GONE); + tv_chapter_name.setVisibility(View.GONE); + tv_time.setVisibility(View.VISIBLE); + tv_time.setText(item.getPublish_time()); + iv_img.setVisibility(View.GONE); + tv_title.setText(Html.fromHtml(item.getTitle())); + tv_desc.setVisibility(View.GONE); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsBean.java b/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsBean.java new file mode 100644 index 00000000..2d6fd8f5 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsBean.java @@ -0,0 +1,63 @@ +package per.goweii.wanandroid.module.explore; + +import java.util.Objects; + +public class DailyNewsBean { + private String title; + private String url; + private String content; + private String source; + private String publish_time; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getSource() { + return source; + } + + public void setSource(String source) { + this.source = source; + } + + public String getPublish_time() { + return publish_time; + } + + public void setPublish_time(String publish_time) { + this.publish_time = publish_time; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + DailyNewsBean that = (DailyNewsBean) o; + return Objects.equals(title, that.title) && Objects.equals(url, that.url) && Objects.equals(content, that.content) && Objects.equals(source, that.source) && Objects.equals(publish_time, that.publish_time); + } + + @Override + public int hashCode() { + return Objects.hash(title, url, content, source, publish_time); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsDiffCallback.kt b/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsDiffCallback.kt new file mode 100644 index 00000000..40cc9b33 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsDiffCallback.kt @@ -0,0 +1,24 @@ +package per.goweii.wanandroid.module.explore + +import com.chad.library.adapter.base.diff.BaseQuickDiffCallback +import com.chad.library.adapter.base.entity.MultiItemEntity +import com.google.android.gms.ads.nativead.NativeAd +import per.goweii.wanandroid.module.main.adapter.ArticleAdapter +import per.goweii.wanandroid.module.main.model.NativeAdBean +import per.goweii.wanandroid.module.main.model.ArticleBean + +/** + * @author CuiZhen + * @date 2020/3/7 + */ +class DailyNewsDiffCallback( + newList: List? +) : BaseQuickDiffCallback(newList) { + override fun areItemsTheSame(oldItem: DailyNewsBean, newItem: DailyNewsBean): Boolean { + return oldItem.url == newItem.url + } + + override fun areContentsTheSame(oldItem: DailyNewsBean, newItem: DailyNewsBean): Boolean { + return oldItem == newItem + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsPlatform.kt b/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsPlatform.kt new file mode 100644 index 00000000..8cd3362d --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsPlatform.kt @@ -0,0 +1,47 @@ +package per.goweii.wanandroid.module.explore + +enum class DailyNewsPlatform( + val platformName: String, + val platformCode: String, + val contentCategory: String, +) { + BAIDU("百度热搜", "baidu", "社会热点、娱乐、事件"), + SSPAI("少数派", "shaoshupai", "科技、数码、生活方式"), + WEIBO("微博热搜", "weibo", "社交媒体热点、娱乐、事件"), + ZHIHU("知乎热榜", "zhihu", "问答、深度内容、社会热点"), + TSKR("36氪", "36kr", "科技创业、商业资讯"), + FTPOJIE("吾爱破解", "52pojie", "技术、软件、安全"), + BILIBILI("哔哩哔哩", "bilibili", "视频、动漫、游戏、生活"), + DOUBAN("豆瓣", "douban", "书影音、文化、讨论"), + HUPU("虎扑", "hupu", "体育、游戏、数码"), + TIEBA("百度贴吧", "tieba", "兴趣社区、话题讨论"), + JUEJIN("掘金", "juejin", "编程、技术文章"), + DOUYIN("抖音", "douyin", "短视频热点、娱乐"), + VTEX("V2EX", "v2ex", "技术、编程、创意"), + JINRITOUTIAO("今日头条", "jinritoutiao", "新闻、热点事件"), + STACKOVERFLOW("Stack Overflow", "stackoverflow", "编程问答、技术讨论"), + GITHUB("GitHub Trending", "github", "开源项目、编程语言"), + HACKERNEWS("Hacker News", "hackernews", "科技新闻、创业、编程"), + SINA_FINANCE("新浪财经", "sina_finance", "财经新闻、股市资讯"), + EASTMONEY("东方财富", "eastmoney", "财经资讯、投资理财"), + XUEQIU("雪球", "xueqiu", "股票投资、财经社区"), + CLS("财联社", "cls", "财经快讯、市场动态"), + TENXUNWANG("腾讯网", "tenxunwang", "综合新闻、娱乐、科技"), + ; + + companion object { + val _code2Platform: Map by lazy { + DailyNewsPlatform.entries.associateBy { it.platformCode } + } + + @JvmStatic + fun fromCode(value: String): DailyNewsPlatform { + return _code2Platform[value] ?: JUEJIN + } + + @JvmStatic + fun fromCodeOrNull(value: String): DailyNewsPlatform? { + return _code2Platform[value] + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsRequest.java b/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsRequest.java new file mode 100644 index 00000000..d84fa2f8 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsRequest.java @@ -0,0 +1,109 @@ +package per.goweii.wanandroid.module.explore; + +import android.annotation.SuppressLint; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; + +import io.reactivex.disposables.Disposable; +import per.goweii.basic.core.utils.JsonFormatUtils; +import per.goweii.basic.utils.LogUtils; +import per.goweii.rxhttp.core.RxLife; +import per.goweii.rxhttp.request.RxRequest; +import per.goweii.rxhttp.request.exception.ExceptionHandle; +import per.goweii.wanandroid.http.BaseRequest; +import per.goweii.wanandroid.http.CacheListener; +import per.goweii.wanandroid.http.RequestListener; +import per.goweii.wanandroid.http.WanApi; +import per.goweii.wanandroid.http.WanCache; + +@SuppressLint("SimpleDateFormat") +public class DailyNewsRequest extends BaseRequest { + public static void getDailyNews(@NonNull DailyNewsPlatform platform, + @NonNull RxLife rxLife, + @NonNull RequestListener> listener) { + WanCache.getInstance().getBean( + WanCache.CacheKey.DAILY_NEWS(platform.getPlatformCode()), + DailyNewsResponse.class, + new CacheListener() { + @Override + public void onSuccess(int code, final DailyNewsResponse data) { + final String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); + if (TextUtils.equals(data.getDate(), date)) { + listener.onSuccess(code, data.getData()); + } else { + getDailyNewsFromNet(platform, rxLife, listener); + } + } + + @Override + public void onFailed() { + getDailyNewsFromNet(platform, rxLife, listener); + } + }); + } + + public static void getDailyNewsFromCache(@NonNull DailyNewsPlatform platform, + @NonNull RequestListener> listener) { + WanCache.getInstance().getBean( + WanCache.CacheKey.DAILY_NEWS(platform.getPlatformCode()), + DailyNewsResponse.class, + new CacheListener() { + @Override + public void onSuccess(int code, final DailyNewsResponse data) { + listener.onSuccess(code, data.getData()); + } + + @Override + public void onFailed() { + } + }); + } + + public static void getDailyNewsFromNet(@NonNull DailyNewsPlatform platform, + @NonNull RxLife rxLife, + @NonNull RequestListener> listener) { + final Disposable disposable = RxRequest.create(WanApi.api().getDailyNews(platform.getPlatformCode())) + .listener(new RxRequest.RequestListener() { + @Override + public void onStart() { + listener.onStart(); + } + + @Override + public void onError(ExceptionHandle handle) { + handle.getException().printStackTrace(); + LogUtils.httpe(handle.getMsg()); + listener.onError(handle); + listener.onFailed(WanApi.ApiCode.ERROR, handle.getMsg()); + } + + @Override + public void onFinish() { + listener.onFinish(); + } + }) + .request(new RxRequest.ResultCallback>() { + @Override + public void onSuccess(int code, List data) { + LogUtils.httpi(JsonFormatUtils.INSTANCE.toJson(data)); + final String date = new SimpleDateFormat("yyyy-MM-dd").format(new Date()); + final DailyNewsResponse response = new DailyNewsResponse(); + response.setData(data); + response.setDate(date); + WanCache.getInstance().save(WanCache.CacheKey.DAILY_NEWS(platform.getPlatformCode()), response); + listener.onSuccess(code, data); + } + + @Override + public void onFailed(int code, String msg) { + listener.onFailed(code, msg); + } + }); + rxLife.add(disposable); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsResponse.java b/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsResponse.java new file mode 100644 index 00000000..5b9af1d1 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/explore/DailyNewsResponse.java @@ -0,0 +1,50 @@ +package per.goweii.wanandroid.module.explore; + +import java.util.List; + +import per.goweii.rxhttp.request.base.BaseResponse; + +public class DailyNewsResponse implements BaseResponse> { + private String status; + private List data; + private String msg; + private String date; + + @Override + public int getCode() { + return Integer.parseInt(status); + } + + @Override + public void setCode(int code) { + status = String.valueOf(code); + } + + @Override + public List getData() { + return data; + } + + @Override + public void setData(List data) { + this.data = data; + } + + @Override + public String getMsg() { + return msg; + } + + @Override + public void setMsg(String msg) { + this.msg = msg; + } + + public String getDate() { + return date; + } + + public void setDate(String date) { + this.date = date; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/explore/ExploreFragment.kt b/app/src/main/java/per/goweii/wanandroid/module/explore/ExploreFragment.kt new file mode 100644 index 00000000..e8722377 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/explore/ExploreFragment.kt @@ -0,0 +1,156 @@ +package per.goweii.wanandroid.module.explore + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.content.ContextCompat +import androidx.core.view.doOnLayout +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.chad.library.adapter.base.BaseQuickAdapter +import com.chad.library.adapter.base.BaseViewHolder +import per.goweii.anylayer.AnyLayer +import per.goweii.anylayer.ktx.backgroundDimDefault +import per.goweii.anylayer.ktx.contentView +import per.goweii.anylayer.ktx.direction +import per.goweii.anylayer.ktx.horizontal +import per.goweii.anylayer.ktx.onBindData +import per.goweii.anylayer.ktx.vertical +import per.goweii.anylayer.popup.PopupLayer +import per.goweii.basic.core.base.BaseFragment +import per.goweii.basic.core.utils.SmartRefreshUtils +import per.goweii.basic.utils.ResUtils +import per.goweii.basic.utils.ext.gone +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.FragmentExploreBinding +import per.goweii.wanandroid.module.main.activity.ArticleActivity +import per.goweii.wanandroid.utils.MultiStateUtils +import per.goweii.wanandroid.utils.SettingUtils + +class ExploreFragment : BaseFragment(), ExploreView { + private var mSmartRefreshUtils: SmartRefreshUtils? = null + private var mAdapter: DailyNewsAdapter? = null + + override fun initPresenter(): ExplorePresenter { + return ExplorePresenter() + } + + override fun initViewBinding( + inflater: LayoutInflater, + container: ViewGroup?, + ): FragmentExploreBinding { + return FragmentExploreBinding.inflate(inflater, container, false); + } + + override fun initView() { + val arrowDrawable = ContextCompat.getDrawable(requireContext(), R.drawable.ic_down)!! + arrowDrawable.setTint(ResUtils.getThemeColor(requireContext(), R.attr.colorTextOnMain)) + arrowDrawable.setBounds(0, 0, arrowDrawable.intrinsicWidth, arrowDrawable.intrinsicHeight) + binding.abc.titleTextView.compoundDrawablePadding = + ResUtils.getDimens(R.dimen.margin_very_thin).toInt() + binding.abc.titleTextView.setCompoundDrawablesRelative(null, null, arrowDrawable, null) + updateTitle() + binding.abc.titleTextView.setOnClickListener { + showChoicePlatformDialog() + } + mSmartRefreshUtils = SmartRefreshUtils.with(binding.srl) + mSmartRefreshUtils!!.pureScrollMode() + mSmartRefreshUtils!!.setRefreshListener { + presenter.getDailyNewsFromNet() + } + binding.rv.setLayoutManager(LinearLayoutManager(context)) + mAdapter = DailyNewsAdapter() + mAdapter!!.setEnableLoadMore(false) + mAdapter!!.setOnItemClickListener { adapter, view, position -> + val bean = mAdapter?.getItem(position) + if (bean != null) { + ArticleActivity.start( + requireContext(), + bean.url, bean.title, + -1, false, "", -1 + ) +// UrlOpenUtils.with(bean.url).open(requireContext()) + } + } + binding.rv.setAdapter(mAdapter) + MultiStateUtils.setEmptyAndErrorClick(binding.msv) { + MultiStateUtils.toLoading(binding.msv) + presenter.getDailyNewsFromNet() + } + } + + private fun showChoicePlatformDialog() { + AnyLayer.popup(binding.abc) + .backgroundDimDefault() + .direction(PopupLayer.Align.Direction.VERTICAL) + .horizontal(PopupLayer.Align.Horizontal.CENTER) + .vertical(PopupLayer.Align.Vertical.BELOW) + .contentView(R.layout.dialog_choice_daily_news_platform) + .onBindData layer@{ + val rv = requireView(R.id.dialog_choice_daily_news_platform_rv) + rv.layoutManager = LinearLayoutManager(requireContext()) + rv.adapter = object : BaseQuickAdapter( + R.layout.rv_item_choice_daily_news_platform, + DailyNewsPlatform.entries.toList().sortedBy { it.platformName } + ) { + override fun convert(holder: BaseViewHolder, item: DailyNewsPlatform) { + holder.setText( + R.id.rv_item_choice_daily_news_platform_tv_title, + item.platformName + ) + holder.setText( + R.id.rv_item_choice_daily_news_platform_tv_subtitle, + item.contentCategory + ) + holder.setVisible( + R.id.rv_item_choice_daily_news_platform_ic_check, + item == SettingUtils.getInstance().dailyNewsPlatform + ) + holder.itemView.setOnClickListener { + SettingUtils.getInstance().dailyNewsPlatform = item + updateTitle() + this@layer.dismiss() + MultiStateUtils.toLoading(binding.msv) + presenter.getDailyNewsFromCache() + presenter.getDailyNewsFromNet() + } + } + } + } + .show() + } + + @SuppressLint("SetTextI18n") + private fun updateTitle() { + val titleName = requireContext().getString(R.string.today_trending) + val platformName = SettingUtils.getInstance().dailyNewsPlatform.platformName + binding.abc.titleTextView.text = "$titleName·$platformName" + } + + override fun loadData() { + MultiStateUtils.toLoading(binding.msv) + presenter.getDailyNewsFromCache() + } + + override fun onVisible(isFirstVisible: Boolean) { + super.onVisible(isFirstVisible) + if (isFirstVisible) { + presenter.getDailyNewsFromNet() + } + } + + override fun getDailyNewsSuccess(platform: DailyNewsPlatform, data: List) { + mAdapter?.setNewData(data) + if (data.isEmpty()) { + MultiStateUtils.toEmpty(binding.msv) + } else { + MultiStateUtils.toContent(binding.msv) + } + mSmartRefreshUtils?.success() + } + + override fun getDailyFailed(platform: DailyNewsPlatform) { + mSmartRefreshUtils!!.fail() + MultiStateUtils.toError(binding.msv) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/explore/ExplorePresenter.kt b/app/src/main/java/per/goweii/wanandroid/module/explore/ExplorePresenter.kt new file mode 100644 index 00000000..c1b8d0a6 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/explore/ExplorePresenter.kt @@ -0,0 +1,65 @@ +package per.goweii.wanandroid.module.explore + +import per.goweii.basic.core.base.BasePresenter +import per.goweii.rxhttp.request.exception.ExceptionHandle +import per.goweii.wanandroid.http.RequestListener +import per.goweii.wanandroid.utils.SettingUtils + +class ExplorePresenter : BasePresenter() { + fun getDailyNewsFromCache() { + val platform = SettingUtils.getInstance().dailyNewsPlatform + DailyNewsRequest.getDailyNewsFromCache( + platform, + object : RequestListener> { + override fun onStart() { + } + + override fun onSuccess(code: Int, data: List) { + if (isAttach && SettingUtils.getInstance().dailyNewsPlatform == platform) { + baseView.getDailyNewsSuccess(platform, data) + } + } + + override fun onFailed(code: Int, msg: String?) { + if (isAttach && SettingUtils.getInstance().dailyNewsPlatform == platform) { + baseView.getDailyFailed(platform) + } + } + + override fun onError(handle: ExceptionHandle?) { + } + + override fun onFinish() { + } + }) + } + + fun getDailyNewsFromNet() { + val platform = SettingUtils.getInstance().dailyNewsPlatform + DailyNewsRequest.getDailyNewsFromNet( + platform, + rxLife, + object : RequestListener> { + override fun onStart() { + } + + override fun onSuccess(code: Int, data: List) { + if (isAttach && SettingUtils.getInstance().dailyNewsPlatform == platform) { + baseView.getDailyNewsSuccess(platform, data) + } + } + + override fun onFailed(code: Int, msg: String?) { + if (isAttach && SettingUtils.getInstance().dailyNewsPlatform == platform) { + baseView.getDailyFailed(platform) + } + } + + override fun onError(handle: ExceptionHandle?) { + } + + override fun onFinish() { + } + }) + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/explore/ExploreView.kt b/app/src/main/java/per/goweii/wanandroid/module/explore/ExploreView.kt new file mode 100644 index 00000000..6f1ae71f --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/explore/ExploreView.kt @@ -0,0 +1,8 @@ +package per.goweii.wanandroid.module.explore + +import per.goweii.basic.core.base.BaseView + +interface ExploreView : BaseView { + fun getDailyNewsSuccess(platform: DailyNewsPlatform, data: List) + fun getDailyFailed(platform: DailyNewsPlatform) +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/activity/SearchActivity.java b/app/src/main/java/per/goweii/wanandroid/module/home/activity/SearchActivity.java index 70975c38..4b2edd3b 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/activity/SearchActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/activity/SearchActivity.java @@ -2,39 +2,50 @@ import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; +import android.text.Editable; import android.text.TextUtils; +import android.text.TextWatcher; import android.view.KeyEvent; +import android.view.LayoutInflater; import android.view.View; import android.view.inputmethod.EditorInfo; +import android.widget.EditText; import android.widget.FrameLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; + import butterknife.BindView; -import per.goweii.actionbarex.common.ActionBarSearch; -import per.goweii.actionbarex.common.OnActionBarChildClickListener; +import per.goweii.actionbarex.common.ActionIconView; import per.goweii.basic.core.base.BaseActivity; +import per.goweii.basic.core.base.BasePresenter; import per.goweii.basic.core.mvp.MvpPresenter; import per.goweii.basic.utils.EditTextUtils; import per.goweii.basic.utils.InputMethodUtils; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.ActivitySearchBinding; import per.goweii.wanandroid.module.home.fragment.SearchHistoryFragment; import per.goweii.wanandroid.module.home.fragment.SearchResultFragment; /** * @author CuiZhen * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class SearchActivity extends BaseActivity { +public class SearchActivity extends BaseActivity { - @BindView(R.id.abs) - ActionBarSearch abs; + @BindView(R.id.aiv_back) + ActionIconView aiv_back; + @BindView(R.id.aiv_clear) + ActionIconView aiv_clear; + @BindView(R.id.aiv_search) + ActionIconView aiv_search; + @BindView(R.id.et_search) + EditText et_search; @BindView(R.id.fl) FrameLayout fl; @@ -49,20 +60,21 @@ public static void start(Context context) { context.startActivity(intent); } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_search; + protected ActivitySearchBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivitySearchBinding.inflate(inflater); } @Nullable @Override - protected MvpPresenter initPresenter() { + protected BasePresenter initPresenter() { return null; } @Override protected void initView() { - abs.setOnLeftIconClickListener(new OnActionBarChildClickListener() { + aiv_back.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mIsResultPage) { @@ -72,20 +84,47 @@ public void onClick(View v) { } } }); - abs.setOnRightTextClickListener(new OnActionBarChildClickListener() { + aiv_search.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - String key = abs.getEditTextView().getText().toString(); + String key = et_search.getText().toString(); search(key); } }); - abs.getEditTextView().setSingleLine(); - abs.getEditTextView().setImeOptions(EditorInfo.IME_ACTION_SEARCH); - abs.getEditTextView().setOnEditorActionListener(new TextView.OnEditorActionListener() { + aiv_clear.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + et_search.setText(""); + InputMethodUtils.hide(et_search); + et_search.clearFocus(); + } + }); + et_search.addTextChangedListener(new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + String key = et_search.getText().toString(); + if (TextUtils.isEmpty(key)) { + aiv_clear.setVisibility(View.INVISIBLE); + showHistoryFragment(); + mSearchResultFragment.clear(); + } else { + aiv_clear.setVisibility(View.VISIBLE); + } + } + }); + et_search.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((actionId == EditorInfo.IME_ACTION_SEARCH)) { - String key = abs.getEditTextView().getText().toString(); + String key = et_search.getText().toString(); search(key); return true; } @@ -127,23 +166,21 @@ public void onBackPressed() { } public void search(String key) { - InputMethodUtils.hide(abs.getEditTextView()); - abs.getEditTextView().clearFocus(); + InputMethodUtils.hide(et_search); + et_search.clearFocus(); if (TextUtils.isEmpty(key)) { - if (mIsResultPage) { - showHistoryFragment(); - } + showHistoryFragment(); + mSearchResultFragment.clear(); } else { - EditTextUtils.setTextWithSelection(abs.getEditTextView(), key); - if (!mIsResultPage) { - showResultFragment(); - } + EditTextUtils.setTextWithSelection(et_search, key); + showResultFragment(); mSearchHistoryFragment.addHistory(key); mSearchResultFragment.search(key); } } private void showHistoryFragment() { + if (!mIsResultPage) return; mIsResultPage = false; FragmentTransaction t = mFragmentManager.beginTransaction(); t.hide(mSearchResultFragment); @@ -152,6 +189,7 @@ private void showHistoryFragment() { } private void showResultFragment() { + if (mIsResultPage) return; mIsResultPage = true; FragmentTransaction t = mFragmentManager.beginTransaction(); t.hide(mSearchHistoryFragment); diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/activity/UserPageActivity.java b/app/src/main/java/per/goweii/wanandroid/module/home/activity/UserPageActivity.java index fe8b4567..0aa6976f 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/activity/UserPageActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/activity/UserPageActivity.java @@ -1,95 +1,74 @@ package per.goweii.wanandroid.module.home.activity; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.design.widget.AppBarLayout; -import android.support.design.widget.CollapsingToolbarLayout; -import android.support.design.widget.CoordinatorLayout; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import android.net.Uri; +import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.coordinatorlayout.widget.CoordinatorLayout; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; +import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.appbar.CollapsingToolbarLayout; import com.kennyc.view.MultiStateView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; -import com.scwang.smartrefresh.layout.api.RefreshFooter; -import com.scwang.smartrefresh.layout.api.RefreshHeader; -import com.scwang.smartrefresh.layout.api.RefreshLayout; -import com.scwang.smartrefresh.layout.constant.RefreshState; -import com.scwang.smartrefresh.layout.listener.OnMultiPurposeListener; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.api.RefreshFooter; +import com.scwang.smart.refresh.layout.api.RefreshHeader; +import com.scwang.smart.refresh.layout.api.RefreshLayout; +import com.scwang.smart.refresh.layout.constant.RefreshState; +import com.scwang.smart.refresh.layout.listener.OnMultiListener; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import java.util.List; +import java.util.Random; import butterknife.BindView; import per.goweii.actionbarex.common.ActionBarCommon; +import per.goweii.actionbarex.common.OnActionBarChildClickListener; import per.goweii.basic.core.base.BaseActivity; import per.goweii.basic.core.utils.SmartRefreshUtils; import per.goweii.basic.ui.toast.ToastMaker; +import per.goweii.basic.utils.CopyUtils; +import per.goweii.basic.utils.LogUtils; +import per.goweii.basic.utils.RandomUtils; +import per.goweii.basic.utils.ResUtils; import per.goweii.basic.utils.listener.SimpleListener; +import per.goweii.wanandroid.BuildConfig; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.ActivityUserPageBinding; import per.goweii.wanandroid.event.CollectionEvent; import per.goweii.wanandroid.event.LoginEvent; -import per.goweii.wanandroid.event.SettingChangeEvent; import per.goweii.wanandroid.module.home.presenter.UserPagePresenter; import per.goweii.wanandroid.module.home.view.UserPageView; -import per.goweii.wanandroid.module.main.activity.WebActivity; import per.goweii.wanandroid.module.main.adapter.ArticleAdapter; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.UserPageBean; +import per.goweii.wanandroid.utils.ImageLoader; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; -import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; +import per.goweii.wanandroid.utils.router.Router; import per.goweii.wanandroid.widget.CollectView; /** * @author CuiZhen * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class UserPageActivity extends BaseActivity implements UserPageView { +public class UserPageActivity extends BaseActivity implements UserPageView { private static final int PAGE_START = 1; - @BindView(R.id.msv) - MultiStateView msv; - @BindView(R.id.msv_list) - MultiStateView msv_list; - @BindView(R.id.cl) - CoordinatorLayout cl; - @BindView(R.id.ctbl) - CollapsingToolbarLayout ctbl; - @BindView(R.id.abl) - AppBarLayout abl; - @BindView(R.id.abc) - ActionBarCommon abc; - @BindView(R.id.srl) - SmartRefreshLayout srl; - @BindView(R.id.iv_blur) - ImageView iv_blur; - @BindView(R.id.rl_user_info) - RelativeLayout rl_user_info; - @BindView(R.id.rv) - RecyclerView rv; - @BindView(R.id.tv_user_name) - TextView tv_user_name; - @BindView(R.id.tv_user_id) - TextView tv_user_id; - @BindView(R.id.tv_user_coin) - TextView tv_user_coin; - @BindView(R.id.tv_user_ranking) - TextView tv_user_ranking; - private SmartRefreshUtils mSmartRefreshUtils; private ArticleAdapter mAdapter; @@ -98,7 +77,7 @@ public class UserPageActivity extends BaseActivity implements public static void start(Context context, int userId) { Intent intent = new Intent(context, UserPageActivity.class); - intent.putExtra("userId", userId); + intent.putExtra("id", userId); context.startActivity(intent); } @@ -107,17 +86,7 @@ public void onCollectionEvent(CollectionEvent event) { if (event.getArticleId() == -1) { return; } - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.getId() == event.getArticleId()) { - if (item.isCollect() != event.isCollect()) { - item.setCollect(event.isCollect()); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - break; - } - } + mAdapter.notifyCollectionEvent(event); } @Subscribe(threadMode = ThreadMode.MAIN) @@ -126,21 +95,7 @@ public void onLoginEvent(LoginEvent event) { currPage = PAGE_START; getUserPage(true); } else { - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.isCollect()) { - item.setCollect(false); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - } - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); + mAdapter.notifyAllUnCollect(); } } @@ -149,9 +104,10 @@ protected boolean isRegisterEventBus() { return true; } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_user_page; + protected ActivityUserPageBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityUserPageBinding.inflate(inflater); } @Nullable @@ -162,8 +118,34 @@ protected UserPagePresenter initPresenter() { @Override protected void initView() { - mUserId = getIntent().getIntExtra("userId", mUserId); - mSmartRefreshUtils = SmartRefreshUtils.with(srl); + mUserId = getUserIdFromIntent(getIntent()); + binding.abc.setOnRightTextClickListener(new OnActionBarChildClickListener() { + @Override + public void onClick(View v) { + String userId = String.valueOf(mUserId); + String salt = RandomUtils.randomLetter(10 - userId.length()); + StringBuilder id = new StringBuilder(); + Random random = new Random(); + for (int i = 0; i < userId.length(); i++) { + int l = userId.length() - i; + int maxi = salt.length() - l - 1; + maxi = maxi < 2 ? 2 : maxi; + int ii = random.nextInt(maxi); + id.append(salt.substring(0, ii)); + id.append(userId.charAt(i)); + salt = salt.substring(ii); + } + id.append(salt); + StringBuilder s = new StringBuilder(); + String content = ResUtils.getString(R.string.your_friend_has_shared_a_mystery_user_with_you); + String key = String.format(BuildConfig.WANPWD_FORMAT, BuildConfig.WANPWD_TYPE_USERPAGE, id.toString()); + s.append(String.format(content, key)); + LogUtils.d("UserPageActivity", s); + CopyUtils.copyText(s.toString()); + ToastMaker.showShort(getString(R.string.copied)); + } + }); + mSmartRefreshUtils = SmartRefreshUtils.with(binding.srl); mSmartRefreshUtils.pureScrollMode(); mSmartRefreshUtils.setRefreshListener(new SmartRefreshUtils.RefreshListener() { @Override @@ -172,31 +154,30 @@ public void onRefresh() { getUserPage(true); } }); - rv.setLayoutManager(new LinearLayoutManager(getContext())); + binding.rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new ArticleAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); mAdapter.setEnableLoadMore(false); mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override public void onLoadMoreRequested() { getUserPage(true); } - }, rv); + }, binding.rv); mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - WebActivity.start(getContext(), item); + UrlOpenUtils.Companion.with(item).open(getContext()); } } }); mAdapter.setOnItemChildViewClickListener(new ArticleAdapter.OnItemChildViewClickListener() { @Override public void onCollectClick(BaseViewHolder helper, CollectView v, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - if (!v.isChecked()) { + if (v.isChecked()) { presenter.collect(item, v); } else { presenter.uncollect(item, v); @@ -204,20 +185,20 @@ public void onCollectClick(BaseViewHolder helper, CollectView v, int position) { } } }); - rv.setAdapter(mAdapter); - MultiStateUtils.setEmptyAndErrorClick(msv, new SimpleListener() { + binding.rv.setAdapter(mAdapter); + MultiStateUtils.setEmptyAndErrorClick(binding.msv, new SimpleListener() { @Override public void onResult() { - MultiStateUtils.toLoading(msv); + MultiStateUtils.toLoading(binding.msv); currPage = PAGE_START; getUserPage(true); } }); - srl.setOnMultiPurposeListener(new OnMultiPurposeListener() { + binding.srl.setOnMultiListener(new OnMultiListener() { @Override public void onHeaderMoving(RefreshHeader header, boolean isDragging, float percent, int offset, int headerHeight, int maxDragHeight) { - iv_blur.getLayoutParams().height = rl_user_info.getMeasuredHeight() + offset; - iv_blur.requestLayout(); + binding.ivBlur.getLayoutParams().height = binding.rlUserInfo.getMeasuredHeight() + offset; + binding.ivBlur.requestLayout(); } @Override @@ -234,8 +215,8 @@ public void onHeaderFinish(RefreshHeader header, boolean success) { @Override public void onFooterMoving(RefreshFooter footer, boolean isDragging, float percent, int offset, int footerHeight, int maxDragHeight) { - iv_blur.getLayoutParams().height = rl_user_info.getMeasuredHeight() - offset; - iv_blur.requestLayout(); + binding.ivBlur.getLayoutParams().height = binding.rlUserInfo.getMeasuredHeight() - offset; + binding.ivBlur.requestLayout(); } @Override @@ -258,35 +239,40 @@ public void onLoadMore(@NonNull RefreshLayout refreshLayout) { public void onRefresh(@NonNull RefreshLayout refreshLayout) { } + @SuppressLint("RestrictedApi") @Override public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) { } }); - abl.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() { + binding.abl.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() { @Override public void onOffsetChanged(AppBarLayout abl, int offset) { if (Math.abs(offset) == abl.getTotalScrollRange()) { - abc.getTitleTextView().setAlpha(1F); - abc.setBackgroundResource(R.color.basic_ui_action_bar_bg); + binding.abc.getTitleTextView().setAlpha(1F); + int color = ResUtils.getThemeColor(binding.abc, R.attr.colorMainOrSurface); + binding.abc.setBackgroundColor(color); + binding.rlUserInfo.setAlpha(1f); } else { - abc.getTitleTextView().setAlpha(0F); - abc.setBackgroundResource(R.color.transparent); + binding.abc.getTitleTextView().setAlpha(0F); + int color = ResUtils.getThemeColor(binding.abc, R.attr.colorTransparent); + binding.abc.setBackgroundColor(color); + binding.rlUserInfo.setAlpha(1f - ((float) Math.abs(offset) / (float) abl.getTotalScrollRange())); } } }); - ctbl.post(new Runnable() { + binding.ctbl.post(new Runnable() { @Override public void run() { - ctbl.setMinimumHeight(abc.getActionBarHeight()); - ctbl.setScrimVisibleHeightTrigger(abc.getActionBarHeight()); + binding.ctbl.setMinimumHeight(binding.abc.getActionBar().getHeight()); + binding.ctbl.setScrimVisibleHeightTrigger(binding.abc.getActionBar().getHeight()); } }); } @Override protected void loadData() { - MultiStateUtils.toLoading(msv); - msv_list.setVisibility(View.GONE); + MultiStateUtils.toLoading(binding.msv); + binding.msvList.setVisibility(View.GONE); currPage = PAGE_START; getUserPage(false); } @@ -294,33 +280,57 @@ protected void loadData() { @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); - int newUserId = getIntent().getIntExtra("userId", mUserId); + int newUserId = getUserIdFromIntent(intent); if (mUserId != newUserId) { loadData(); } } + private int getUserIdFromIntent(Intent intent) { + int id = mUserId; + Uri uri = Router.getUriFrom(intent); + if (uri != null) { + String userId = uri.getQueryParameter("id"); + if (userId != null) { + try { + id = Integer.parseInt(userId); + } catch (Exception ignore) { + } + } + } else { + id = intent.getIntExtra("id", id); + } + if (id < 0) { + binding.abc.getRightTextView().setVisibility(View.GONE); + } else { + binding.abc.getRightTextView().setVisibility(View.VISIBLE); + } + return id; + } + public void getUserPage(boolean refresh) { presenter.getUserPage(mUserId, currPage, refresh); } @Override public void getUserPageSuccess(int code, UserPageBean data) { - MultiStateUtils.toContent(msv); + MultiStateUtils.toContent(binding.msv); currPage = data.getShareArticles().getCurPage() + PAGE_START; if (data.getShareArticles().getCurPage() == 1) { - abc.getTitleTextView().setText(data.getCoinInfo().getUsername()); - tv_user_name.setText(data.getCoinInfo().getUsername()); - tv_user_id.setText("" + data.getCoinInfo().getUserId()); - tv_user_coin.setText("" + data.getCoinInfo().getCoinCount()); - tv_user_ranking.setText("" + data.getCoinInfo().getRank()); - mAdapter.setNewData(data.getShareArticles().getDatas()); + binding.abc.getTitleTextView().setText(data.getCoinInfo().getUsername()); + ImageLoader.userIcon(binding.civUserIcon, ""); + ImageLoader.userBlur(binding.ivBlur, ""); + binding.tvUserName.setText(data.getCoinInfo().getUsername()); + binding.tvUserId.setText("" + data.getCoinInfo().getUserId()); + binding.tvUserCoin.setText("" + data.getCoinInfo().getCoinCount()); + binding.tvUserRanking.setText("" + data.getCoinInfo().getRank()); + mAdapter.setArticleData(data.getShareArticles().getDatas()); mAdapter.setEnableLoadMore(true); - msv_list.setVisibility(View.VISIBLE); + binding.msvList.setVisibility(View.VISIBLE); if (data.getShareArticles().getDatas() == null || data.getShareArticles().getDatas().isEmpty()) { - MultiStateUtils.toEmpty(msv_list); + MultiStateUtils.toEmpty(binding.msvList); } else { - MultiStateUtils.toContent(msv_list); + MultiStateUtils.toContent(binding.msvList); } } else { mAdapter.addData(data.getShareArticles().getDatas()); @@ -338,7 +348,7 @@ public void getUserPageFailed(int code, String msg) { mSmartRefreshUtils.fail(); mAdapter.loadMoreFail(); if (currPage == PAGE_START) { - MultiStateUtils.toError(msv); + MultiStateUtils.toError(binding.msv); } } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/fragment/HomeFragment.java b/app/src/main/java/per/goweii/wanandroid/module/home/fragment/HomeFragment.java index b40c32b2..a9894dd5 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/fragment/HomeFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/fragment/HomeFragment.java @@ -1,27 +1,43 @@ package per.goweii.wanandroid.module.home.fragment; -import android.app.Activity; +import android.animation.Animator; +import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; import android.text.TextUtils; +import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.FrameLayout; import android.widget.ImageView; +import androidx.activity.OnBackPressedCallback; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentTransaction; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; +import com.chad.library.adapter.base.entity.MultiItemEntity; +import com.google.android.gms.ads.nativead.NativeAd; import com.kennyc.view.MultiStateView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.api.RefreshHeader; +import com.scwang.smart.refresh.layout.api.RefreshLayout; +import com.scwang.smart.refresh.layout.constant.RefreshState; import com.youth.banner.Banner; import com.youth.banner.BannerConfig; import com.youth.banner.Transformer; import com.youth.banner.listener.OnBannerListener; +import com.youth.banner.loader.ImageLoaderInterface; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -30,48 +46,70 @@ import java.util.List; import butterknife.BindView; +import kotlin.Unit; +import kotlin.jvm.functions.Function0; +import kotlin.jvm.functions.Function1; import per.goweii.actionbarex.common.ActionBarCommon; import per.goweii.actionbarex.common.OnActionBarChildClickListener; import per.goweii.anylayer.Layer; import per.goweii.basic.core.base.BaseFragment; import per.goweii.basic.core.glide.GlideHelper; +import per.goweii.basic.core.glide.transformation.BlurTransformation; import per.goweii.basic.core.utils.SmartRefreshUtils; import per.goweii.basic.ui.toast.ToastMaker; +import per.goweii.basic.utils.ColorUtils; +import per.goweii.basic.utils.ResUtils; import per.goweii.basic.utils.display.DisplayInfoUtils; +import per.goweii.basic.utils.listener.OnClickListener2; import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.basic.utils.listener.SimpleListener; +import per.goweii.wanandroid.utils.GoogleAdUnitIds; +import per.goweii.component.ad.NativeAdProvider; +import per.goweii.cropimageview.CropImageView; +import per.goweii.statusbarcompat.utils.LuminanceUtils; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.common.ScrollTop; -import per.goweii.wanandroid.common.WanApp; +import per.goweii.wanandroid.databinding.FragmentHomeBinding; +import per.goweii.wanandroid.event.BannerAutoSwitchEnableEvent; +import per.goweii.wanandroid.event.CloseSecondFloorEvent; import per.goweii.wanandroid.event.CollectionEvent; +import per.goweii.wanandroid.event.HomeActionBarEvent; import per.goweii.wanandroid.event.LoginEvent; import per.goweii.wanandroid.event.SettingChangeEvent; +import per.goweii.wanandroid.module.book.fragment.BookFragment; import per.goweii.wanandroid.module.home.activity.SearchActivity; import per.goweii.wanandroid.module.home.model.BannerBean; import per.goweii.wanandroid.module.home.presenter.HomePresenter; import per.goweii.wanandroid.module.home.view.HomeView; -import per.goweii.wanandroid.module.main.activity.MainActivity; -import per.goweii.wanandroid.module.main.activity.WebActivity; +import per.goweii.wanandroid.module.main.activity.ScanActivity; import per.goweii.wanandroid.module.main.adapter.ArticleAdapter; import per.goweii.wanandroid.module.main.dialog.WebDialog; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.ArticleListBean; import per.goweii.wanandroid.module.main.model.ConfigBean; +import per.goweii.wanandroid.module.main.model.NativeAdBean; +import per.goweii.wanandroid.module.main.model.RecommendBean; +import per.goweii.wanandroid.module.main.utils.BottomDrawerViewOutlineProvider; +import per.goweii.wanandroid.utils.ConfigUtils; +import per.goweii.wanandroid.utils.DarkModeUtils; import per.goweii.wanandroid.utils.ImageLoader; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; +import per.goweii.wanandroid.utils.RecommendManager; import per.goweii.wanandroid.utils.RvScrollTopUtils; import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; +import per.goweii.wanandroid.utils.cdkey.CDKeyUtils; +import per.goweii.wanandroid.utils.router.Router; import per.goweii.wanandroid.widget.CollectView; +import per.goweii.wanandroid.widget.bottomdrawer.BottomDrawerLayout; +import per.goweii.wanandroid.widget.refresh.ShiciRefreshHeader; +import per.goweii.wanandroid.widget.refresh.SimpleOnMultiListener; /** * @author CuiZhen * @date 2019/5/11 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class HomeFragment extends BaseFragment implements ScrollTop, HomeView { +public class HomeFragment extends BaseFragment implements RvScrollTopUtils.ScrollTop, HomeView { private static final int PAGE_START = 0; @@ -79,19 +117,29 @@ public class HomeFragment extends BaseFragment implements ScrollT MultiStateView msv; @BindView(R.id.abc) ActionBarCommon abc; + @BindView(R.id.dl) + BottomDrawerLayout dl; @BindView(R.id.srl) SmartRefreshLayout srl; @BindView(R.id.rv) RecyclerView rv; + @BindView(R.id.fl_dl_second_floor) + FrameLayout fl_dl_second_floor; + @BindView(R.id.fl_dl_content) + FrameLayout fl_dl_content; + @BindView(R.id.v_dl_content_handle) + FrameLayout v_dl_content_handle; + @BindView(R.id.v_dl_content_mask) + View v_dl_content_mask; + @BindView(R.id.iv_second_floor_background) + CropImageView iv_second_floor_background; private Banner mBanner; private ArticleAdapter mAdapter; private int currPage = PAGE_START; private SmartRefreshUtils mSmartRefreshUtils; - private List mBannerBeans; - private List mHeaderTopItemViews; - private List mHeaderTopItemBeans; + private List mBannerDatas; private WebDialog mWebDialog; public static HomeFragment create() { @@ -106,33 +154,22 @@ public void onCollectionEvent(CollectionEvent event) { if (event.getArticleId() == -1) { return; } - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.getId() == event.getArticleId()) { - if (item.isCollect() != event.isCollect()) { - item.setCollect(event.isCollect()); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - if (mWebDialog != null) { - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - break; - } - } - if (mHeaderTopItemViews != null && mHeaderTopItemViews.size() > 0) { - for (int i = 0; i < mHeaderTopItemViews.size(); i++) { - ArticleBean item = mHeaderTopItemBeans.get(i); - if (item.getId() == event.getArticleId()) { - if (item.isCollect() != event.isCollect()) { - item.setCollect(event.isCollect()); - View view = mHeaderTopItemViews.get(i); - bindHeaderTopItem(view, item); + mAdapter.forEach(new ArticleAdapter.ArticleForEach() { + @Override + public boolean forEach(int dataPos, int adapterPos, ArticleBean bean) { + if (bean.getId() == event.getArticleId()) { + if (bean.isCollect() != event.isCollect()) { + bean.setCollect(event.isCollect()); + mAdapter.notifyItemChanged(adapterPos); } - break; + if (mWebDialog != null) { + mAdapter.notifyItemChanged(adapterPos); + } + return true; } + return false; } - } + }); } @Subscribe(threadMode = ThreadMode.MAIN) @@ -142,11 +179,9 @@ public void onSettingChangeEvent(SettingChangeEvent event) { } if (event.isShowTopChanged()) { if (SettingUtils.getInstance().isShowTop()) { - if (mHeaderTopItemViews == null || mHeaderTopItemViews.size() == 0) { - presenter.getTopArticleList(true); - } + presenter.getTopArticleList(true); } else { - removeHeaderTopItems(); + removeTopItems(); } } if (event.isShowBannerChanged()) { @@ -155,9 +190,6 @@ public void onSettingChangeEvent(SettingChangeEvent event) { presenter.getBanner(); } } - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); - } } @Subscribe(threadMode = ThreadMode.MAIN) @@ -172,25 +204,32 @@ public void onLoginEvent(LoginEvent event) { currPage = PAGE_START; presenter.getArticleList(currPage, true); } else { - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.isCollect()) { - item.setCollect(false); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - } - if (mHeaderTopItemViews != null && mHeaderTopItemViews.size() > 0) { - for (int i = 0; i < mHeaderTopItemViews.size(); i++) { - ArticleBean item = mHeaderTopItemBeans.get(i); - if (item.isCollect()) { - item.setCollect(false); - View view = mHeaderTopItemViews.get(i); - bindHeaderTopItem(view, item); - break; - } - } + mAdapter.notifyAllUnCollect(); + } + } + + @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) + public void onHomeActionBarEvent(HomeActionBarEvent event) { + event.removeSticky(); + updateActionBarByConfig(event); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onCloseSecondFloorEvent(CloseSecondFloorEvent event) { + dl.open(300); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onBannerAutoSwitchEnableEvent(BannerAutoSwitchEnableEvent event) { + if (mBanner == null) return; + if (event.isEnable()) { + if (dl.isOpened()) { + mBanner.isAutoPlay(true); + mBanner.startAutoPlay(); } + } else { + mBanner.isAutoPlay(false); + mBanner.stopAutoPlay(); } } @@ -199,9 +238,10 @@ protected boolean isRegisterEventBus() { return true; } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_home; + protected FragmentHomeBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentHomeBinding.inflate(inflater, container, false); } @Nullable @@ -221,10 +261,8 @@ public void onClick(View v) { abc.setOnLeftIconClickListener(new OnActionBarChildClickListener() { @Override public void onClick(View v) { - Activity activity = getActivity(); - if (activity instanceof MainActivity) { - MainActivity mainActivity = (MainActivity) activity; - mainActivity.openUserArticle(); + if (getActivity() != null) { + ScanActivity.start(getActivity()); } } }); @@ -243,9 +281,9 @@ public void onRefresh() { presenter.getArticleList(currPage, true); } }); + initSecondFloor(); rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new ArticleAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); mAdapter.setEnableLoadMore(false); mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override @@ -253,28 +291,19 @@ public void onLoadMoreRequested() { presenter.getArticleList(currPage, false); } }, rv); - mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { - @Override - public void onItemClick(BaseQuickAdapter adapter, View view, int position) { - ArticleBean item = mAdapter.getItem(position); - if (item != null) { - WebActivity.start(getContext(), item); - } - } - }); - mAdapter.setOnItemLongClickListener(new BaseQuickAdapter.OnItemLongClickListener() { - @Override - public boolean onItemLongClick(BaseQuickAdapter adapter, View view, int position) { - showWebDialog(false, position); - return true; - } - }); +// mAdapter.setOnItemLongClickListener(new BaseQuickAdapter.OnItemLongClickListener() { +// @Override +// public boolean onItemLongClick(BaseQuickAdapter adapter, View view, int position) { +// showWebDialog(position); +// return true; +// } +// }); mAdapter.setOnItemChildViewClickListener(new ArticleAdapter.OnItemChildViewClickListener() { @Override public void onCollectClick(BaseViewHolder helper, CollectView v, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - if (!v.isChecked()) { + if (v.isChecked()) { presenter.collect(item, v); } else { presenter.uncollect(item, v); @@ -292,36 +321,224 @@ public void onResult() { }); } - private void showWebDialog(boolean header, int position) { - if (!WanApp.isWebActivityStarted()) { -// return; + private BookFragment bookFragment = null; + private OnBackPressedCallback onBackPressedCallback = null; + private BottomDrawerViewOutlineProvider secondFloorOutlineProvider = null; + private Animator abcAnim = null; + + private void initSecondFloor() { + if (getActivity() == null) return; + ShiciRefreshHeader shiciRefreshHeader = (ShiciRefreshHeader) srl.getRefreshHeader(); + if (shiciRefreshHeader != null) { + shiciRefreshHeader.setColor(ResUtils.getThemeColor(getActivity(), R.attr.colorOnMainOrSurface)); } - int index = position; - if (!header) { - if (mHeaderTopItemBeans != null) { - index += mHeaderTopItemBeans.size(); - } + Fragment fragment = getChildFragmentManager().findFragmentByTag(BookFragment.class.getName()); + if (fragment == null) { + bookFragment = new BookFragment(); + FragmentTransaction ft = getChildFragmentManager().beginTransaction(); + ft.add(R.id.fl_dl_second_floor, bookFragment, BookFragment.class.getName()); + ft.commitAllowingStateLoss(); + } else { + bookFragment = (BookFragment) fragment; } - mWebDialog = WebDialog.create(getContext(), mHeaderTopItemBeans, mAdapter.getData(), index); + onBackPressedCallback = new OnBackPressedCallback(false) { + @Override + public void handleOnBackPressed() { + dl.open(300); + } + }; + getActivity().getOnBackPressedDispatcher().addCallback(onBackPressedCallback); + View bottomBar = getActivity().getWindow().getDecorView().findViewById(R.id.fl_bottom_bar); + fl_dl_second_floor.setPadding(0, 0, 0, (int) dl.getCloseHeight()); + iv_second_floor_background.setAutoMoveDuration(20_000); + iv_second_floor_background.setSmoothMoveAnimDuration(0); + iv_second_floor_background.setCropScale(1.3F); + iv_second_floor_background.setCropType(CropImageView.CropType.CENTER); + v_dl_content_mask.setOnClickListener(new OnClickListener2() { + @Override + public void onClick2(View v) { + dl.open(300); + } + }); + dl.setEnable(true); + dl.setDraggable(false); + dl.onClosed(new Function0() { + @Override + public Unit invoke() { + if (onBackPressedCallback != null) { + onBackPressedCallback.setEnabled(true); + } + v_dl_content_mask.setClickable(true); + dl.setDraggable(true); + iv_second_floor_background.setAutoMove(true); + mBanner.isAutoPlay(false); + mBanner.stopAutoPlay(); + return null; + } + }); + dl.onOpened(new Function0() { + @Override + public Unit invoke() { + if (onBackPressedCallback != null) { + onBackPressedCallback.setEnabled(false); + } + v_dl_content_mask.setClickable(false); + dl.setDraggable(false); + if (bookFragment != null) { + getChildFragmentManager().beginTransaction() + .hide(bookFragment) + .commitAllowingStateLoss(); + } + iv_second_floor_background.setAutoMove(false); + mBanner.isAutoPlay(true); + mBanner.startAutoPlay(); + return null; + } + }); + dl.onDragStart(new Function0() { + @Override + public Unit invoke() { + if (mBanner != null) { + mBanner.isAutoPlay(false); + mBanner.stopAutoPlay(); + } + return null; + } + }); + dl.onDragEnd(new Function0() { + @Override + public Unit invoke() { + return null; + } + }); + secondFloorOutlineProvider = new BottomDrawerViewOutlineProvider(ResUtils.getDimens(R.dimen.round_radius)); + secondFloorOutlineProvider.setToView(fl_dl_content); + dl.onDragging(new Function1() { + @Override + public Unit invoke(Float f) { + bottomBar.setTranslationY(bottomBar.getHeight() * f); + fl_dl_second_floor.setAlpha(f); + final float minScale = 0.95F; + float s = minScale + f * (1F - minScale); + fl_dl_second_floor.setPivotX(fl_dl_second_floor.getWidth() * 0.5F); + fl_dl_second_floor.setPivotY(fl_dl_second_floor.getHeight() * 0F); + fl_dl_second_floor.setScaleX(s); + fl_dl_second_floor.setScaleY(s); + secondFloorOutlineProvider.updateFaction(f); + final float bgScale = 1.3F; + iv_second_floor_background.setCropScale(bgScale + (1F - f) * (1F - bgScale)); + if ((abcAnim == null || !abcAnim.isRunning())) { + abc.setTranslationY(-abc.getHeight() * f); + } + final float minAlpha = 0.0F; + final float fromFaction = 0.6F; + if (f < fromFaction) { + srl.setAlpha(1F); + v_dl_content_mask.setAlpha(0); + v_dl_content_handle.setAlpha(0); + } else { + final float fa = (f - fromFaction) / (1 - fromFaction); + //srl.setAlpha(minAlpha + (1F - fa) * (1F - minAlpha)); + v_dl_content_mask.setAlpha(fa); + v_dl_content_handle.setAlpha(fa); + } + if (f == 1F) { + srl.setVisibility(View.INVISIBLE); + } else { + srl.setVisibility(View.VISIBLE); + } + return null; + } + }); + srl.setOnMultiListener(new SimpleOnMultiListener() { + private boolean isSecondFloor = false; + + @Override + public void onHeaderMoving(RefreshHeader header, boolean isDragging, float percent, int offset, int headerHeight, int maxDragHeight) { + super.onHeaderMoving(header, isDragging, percent, offset, headerHeight, maxDragHeight); + final float secondPercent = 1.5F; + if (isDragging) { + if (percent > secondPercent) { + if (!isSecondFloor) { + isSecondFloor = true; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + srl.performHapticFeedback(HapticFeedbackConstants.GESTURE_START); + } else { + srl.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); + } + if (abcAnim != null) { + abcAnim.cancel(); + } + abcAnim = ObjectAnimator.ofFloat(abc, "translationY", + abc.getTranslationY(), -abc.getHeight()); + abcAnim.start(); + if (shiciRefreshHeader != null) { + shiciRefreshHeader.setTextAndHideIcon(getString(R.string.release_into_the_book_tutorial)); + } + } + } else { + if (isSecondFloor) { + isSecondFloor = false; + if (abcAnim != null) { + abcAnim.cancel(); + } + abcAnim = ObjectAnimator.ofFloat(abc, "translationY", + abc.getTranslationY(), 0); + abcAnim.start(); + if (shiciRefreshHeader != null) { + shiciRefreshHeader.restoreToCurrState(); + } + } + } + } + } + + @Override + public void onHeaderReleased(RefreshHeader header, int headerHeight, int maxDragHeight) { + super.onHeaderReleased(header, headerHeight, maxDragHeight); + if (isSecondFloor) { + isSecondFloor = false; + if (abcAnim != null) { + abcAnim.cancel(); + } + abcAnim = ObjectAnimator.ofFloat(abc, "translationY", + abc.getTranslationY(), -abc.getHeight()); + abcAnim.setDuration(300); + abcAnim.start(); + srl.closeHeaderOrFooter(); + srl.finishRefresh(); + dl.close(300); + if (bookFragment != null) { + getChildFragmentManager().beginTransaction() + .show(bookFragment) + .commitAllowingStateLoss(); + } + } + } + + @Override + public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) { + } + }); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + } + + private void showWebDialog(int position) { + mWebDialog = WebDialog.create(getContext(), null, mAdapter.getArticleData(), position); mWebDialog.setOnPageChangedListener(new WebDialog.OnPageChangedListener() { @Override public void onPageChanged(int pos, ArticleBean data) { int headerCount = mAdapter.getHeaderLayoutCount(); int currItemPos = 0; - boolean find = false; - if (mHeaderTopItemBeans != null) { - for (int i = 0; i < mHeaderTopItemBeans.size(); i++) { - ArticleBean bean = mHeaderTopItemBeans.get(i); - if (bean.getId() == data.getId()) { - find = true; - break; - } - } - } - if (!find) { - List datas = mAdapter.getData(); - for (int i = 0; i < datas.size(); i++) { - ArticleBean bean = datas.get(i); + List datas = mAdapter.getData(); + for (int i = 0; i < datas.size(); i++) { + MultiItemEntity item = datas.get(i); + if (item.getItemType() == ArticleAdapter.ITEM_TYPE_ARTICLE) { + ArticleBean bean = (ArticleBean) item; if (bean.getId() == data.getId()) { currItemPos = headerCount + i; break; @@ -337,13 +554,13 @@ public void onPageChanged(int pos, ArticleBean data) { rv.smoothScrollToPosition(currItemPos); } }); - mWebDialog.onDismissListener(new Layer.OnDismissListener() { + mWebDialog.addOnDismissListener(new Layer.OnDismissListener() { @Override - public void onDismissing(Layer layer) { + public void onPreDismiss(@NonNull Layer layer) { } @Override - public void onDismissed(Layer layer) { + public void onPostDismiss(@NonNull Layer layer) { mWebDialog = null; } }); @@ -352,6 +569,18 @@ public void onDismissed(Layer layer) { @Override protected void loadData() { + ConfigBean configBean = ConfigUtils.getInstance().getConfig(); + if (configBean.isEnableAtNow()) { + updateActionBarByConfig(new HomeActionBarEvent( + configBean.getHomeTitle(), + configBean.getActionBarBgColor(), + configBean.getActionBarBgImageUrl(), + configBean.getSecondFloorBgImageUrl(), + configBean.getSecondFloorBgImageBlurPercent() + )); + } else { + updateActionBarByConfig(new HomeActionBarEvent()); + } MultiStateUtils.toLoading(msv); if (SettingUtils.getInstance().isShowBanner()) { presenter.getBanner(); @@ -361,26 +590,46 @@ protected void loadData() { } currPage = PAGE_START; presenter.getArticleList(currPage, false); - presenter.getConfig(); + +// if (!CDKeyUtils.getInstance().isActive()) { +// new NativeAdProvider(requireActivity(), getViewLifecycleOwner(), GoogleAdUnitIds.ARTICLE_LIST_NATIVE_AD).load(new SimpleCallback() { +// @Override +// public void onResult(NativeAd data) { +// LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager(); +// if (layoutManager != null) { +// int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition(); +// mAdapter.addData(lastVisibleItemPosition + 1, new NativeAdBean(data)); +// } +// } +// }); +// } } @Override public void onVisible(boolean isFirstVisible) { super.onVisible(isFirstVisible); - } - - @Override - public void onStart() { - super.onStart(); + if (!isFirstVisible && abc != null) { + abc.refreshStatusBarMode(); + } if (mBanner != null) { - mBanner.startAutoPlay(); + if (dl.isOpened()) { + mBanner.isAutoPlay(true); + mBanner.startAutoPlay(); + } } } @Override public void onStop() { super.onStop(); + dl.open(0); + } + + @Override + protected void onInvisible() { + super.onInvisible(); if (mBanner != null) { + mBanner.isAutoPlay(false); mBanner.stopAutoPlay(); } } @@ -393,14 +642,30 @@ public void scrollTop() { } private void createHeaderBanner() { - if (mBanner == null) { + if (mBanner == null && getContext() != null) { mBanner = new Banner(getContext()); - int height = (int) (DisplayInfoUtils.getInstance().getWidthPixels() * (9F / 16F)); + int screenWidth = DisplayInfoUtils.getInstance().getWidthPixels(); + int screenHeight = DisplayInfoUtils.getInstance().getHeightPixels(); + int height = (int) (Math.min(screenWidth, screenHeight) * (9F / 16F)); mBanner.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height)); - mBanner.setImageLoader(new com.youth.banner.loader.ImageLoader() { + mBanner.setImageLoader(new ImageLoaderInterface() { @Override - public void displayImage(Context context, Object url, ImageView imageView) { - ImageLoader.banner(imageView, (String) url); + public ImageView createImageView(Context context) { + ImageView imageView = new ImageView(context); + imageView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + return imageView; + } + + @Override + public void displayImage(Context context, Object data, ImageView imageView) { + imageView.setScaleType(ImageView.ScaleType.FIT_XY); + if (data instanceof BannerBean) { + BannerBean bean = (BannerBean) data; + ImageLoader.banner(imageView, bean.getImagePath()); + } else if (data instanceof RecommendBean.BannerBean) { + RecommendBean.BannerBean bean = (RecommendBean.BannerBean) data; + ImageLoader.banner(imageView, bean.getUrl()); + } } }); mBanner.setIndicatorGravity(BannerConfig.CENTER); @@ -411,8 +676,17 @@ public void displayImage(Context context, Object url, ImageView imageView) { mBanner.setOnBannerListener(new OnBannerListener() { @Override public void OnBannerClick(int position) { - BannerBean bean = mBannerBeans.get(position); - WebActivity.start(getContext(), bean.getTitle(), bean.getUrl()); + Object obj = mBannerDatas.get(position); + if (obj instanceof BannerBean) { + BannerBean bean = (BannerBean) obj; + UrlOpenUtils.Companion + .with(bean.getUrl()) + .title(bean.getTitle()) + .open(getContext()); + } else if (obj instanceof RecommendBean.BannerBean) { + RecommendBean.BannerBean bean = (RecommendBean.BannerBean) obj; + Router.routeTo(bean.getRoute()); + } } }); mAdapter.addHeaderView(mBanner, 0); @@ -424,79 +698,98 @@ public void OnBannerClick(int position) { } } - private void bindHeaderTopItem(View view, ArticleBean item) { - ArticleAdapter.bindArticle(view, item, new ArticleAdapter.OnCollectListener() { - @Override - public void collect(ArticleBean item, CollectView v) { - presenter.collect(item, v); - } - - @Override - public void uncollect(ArticleBean item, CollectView v) { - presenter.uncollect(item, v); + private void removeTopItems() { + List list = mAdapter.getArticleData(); + int from = -1; + int count = 0; + for (int i = 0; i < list.size(); i++) { + ArticleBean bean = list.get(i); + if (from < 0) { + if (bean.isTop()) { + from = i; + } } - }); - } - - private void removeHeaderTopItems() { - if (mHeaderTopItemViews != null) { - for (View view : mHeaderTopItemViews) { - mAdapter.removeHeaderView(view); + if (from >= 0) { + if (!bean.isTop()) { + break; + } + count++; } - mHeaderTopItemViews.clear(); - mHeaderTopItemViews = null; } - if (mHeaderTopItemBeans != null) { - mHeaderTopItemBeans.clear(); - mHeaderTopItemBeans = null; + if (from >= 0) { + for (int i = 0; i < count; i++) { + mAdapter.remove(from); + } } } - private void createHeaderTopItems(List data) { - removeHeaderTopItems(); - mHeaderTopItemBeans = data; - if (mHeaderTopItemBeans == null || mHeaderTopItemBeans.isEmpty()) { - return; + @Override + public void getBannerSuccess(int code, List data) { + if (mBannerDatas == null) { + mBannerDatas = new ArrayList<>(); + } + mBannerDatas.clear(); + RecommendBean recommendBean = RecommendManager.getInstance().getBean(); + if (recommendBean != null && recommendBean.getBannerList() != null) { + mBannerDatas.addAll(recommendBean.getBannerList()); } - LayoutInflater inflater = LayoutInflater.from(getContext()); - mHeaderTopItemViews = new ArrayList<>(); - for (int i = 0; i < mHeaderTopItemBeans.size(); i++) { - View view = inflater.inflate(R.layout.rv_item_article, rv, false); - mHeaderTopItemViews.add(view); - } - for (int i = 0; i < mHeaderTopItemViews.size(); i++) { - final int pos = i; - View view = mHeaderTopItemViews.get(i); - ArticleBean bean = mHeaderTopItemBeans.get(i); - bindHeaderTopItem(view, bean); - view.setOnLongClickListener(new View.OnLongClickListener() { + mBannerDatas.addAll(data); + mBanner.setImages(mBannerDatas); + refreshBannerTitles(); + mBanner.start(); + MultiStateUtils.toContent(msv); + + if (recommendBean == null) { + RecommendManager.getInstance().getBean(new RecommendManager.Callback() { @Override - public boolean onLongClick(View v) { - showWebDialog(true, pos); - return true; + public void onResult(@Nullable RecommendBean recommendBean) { + if (mBannerDatas == null) { + mBannerDatas = new ArrayList<>(); + } + mBannerDatas.clear(); + if (recommendBean != null && recommendBean.getBannerList() != null) { + mBannerDatas.addAll(recommendBean.getBannerList()); + } + mBannerDatas.addAll(data); + mBanner.setImages(mBannerDatas); + refreshBannerTitles(); + mBanner.start(); + MultiStateUtils.toContent(msv); } }); - mAdapter.addHeaderView(view, mAdapter.getHeaderLayout().getChildCount()); } } @Override - public void getBannerSuccess(int code, List data) { - mBannerBeans = data; - List urls = new ArrayList<>(data.size()); - List titles = new ArrayList<>(data.size()); - for (BannerBean bean : data) { - urls.add(bean.getImagePath()); - titles.add(bean.getTitle()); - } - mBanner.setImages(urls); - mBanner.setBannerTitles(titles); - mBanner.start(); - MultiStateUtils.toContent(msv); + public void getBannerFail(int code, String msg) { + RecommendManager.getInstance().getBean(new RecommendManager.Callback() { + @Override + public void onResult(@Nullable RecommendBean bean) { + if (mBannerDatas == null) { + mBannerDatas = new ArrayList<>(); + } + mBannerDatas.clear(); + if (bean != null && bean.getBannerList() != null) { + mBannerDatas.addAll(bean.getBannerList()); + } + mBanner.setImages(mBannerDatas); + refreshBannerTitles(); + mBanner.start(); + MultiStateUtils.toContent(msv); + } + }); } - @Override - public void getBannerFail(int code, String msg) { + private void refreshBannerTitles() { + List titles = new ArrayList<>(mBannerDatas.size()); + for (Object bean : mBannerDatas) { + if (bean instanceof BannerBean) { + titles.add(((BannerBean) bean).getTitle()); + } else if (bean instanceof RecommendBean.BannerBean) { + titles.add(((RecommendBean.BannerBean) bean).getTitle()); + } + } + mBanner.setBannerTitles(titles); } @Override @@ -504,7 +797,22 @@ public void getArticleListSuccess(int code, ArticleListBean data) { currPage = data.getCurPage() + PAGE_START; if (data.getCurPage() == 1) { MultiStateUtils.toContent(msv); - mAdapter.setNewData(data.getDatas()); + List adList = new ArrayList<>(); + List newList = new ArrayList<>(); + List oldList = mAdapter.getData(); + for (MultiItemEntity item : oldList) { + if (item instanceof ArticleBean) { + ArticleBean bean = (ArticleBean) item; + if (bean.isTop()) { + newList.add(bean); + } + } else { + adList.add(item); + } + } + newList.addAll(data.getDatas()); + newList.addAll(adList); + mAdapter.setNewData(newList); } else { mAdapter.addData(data.getDatas()); if (mWebDialog != null) { @@ -535,68 +843,118 @@ public void getTopArticleListSuccess(int code, List data) { for (ArticleBean bean : data) { bean.setTop(true); } - createHeaderTopItems(data); + List newList = new ArrayList<>(data); + List oldList = mAdapter.getData(); + for (MultiItemEntity item : oldList) { + if (item instanceof ArticleBean) { + ArticleBean bean = (ArticleBean) item; + if (!bean.isTop()) { + newList.add(bean); + } + } else { + newList.add(item); + } + } + mAdapter.setNewData(newList); } @Override public void getTopArticleListFailed(int code, String msg) { } - private ConfigBean mConfigBean = null; - @Override - public void getConfigSuccess(ConfigBean configBean) { - if (configBean.getHomeTitle() != null) { - if (mConfigBean == null || !TextUtils.equals(mConfigBean.getHomeTitle(), configBean.getHomeTitle())) { - if (configBean.getHomeTitle() == null) { - abc.getTitleTextView().setText("首页"); - } else { - abc.getTitleTextView().setText(configBean.getHomeTitle()); - } - } - } - if (TextUtils.isEmpty(configBean.getActionBarBgImageUrl())) { - if (TextUtils.isEmpty(configBean.getActionBarBgColor())) { - abc.setBackgroundResource(R.color.basic_ui_action_bar_bg); - } else { - if (mConfigBean == null || !TextUtils.equals(mConfigBean.getActionBarBgColor(), configBean.getActionBarBgColor())) { - try { - int color = Color.parseColor(configBean.getActionBarBgColor()); - abc.setBackgroundColor(color); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - } - } - } else { - if (TextUtils.isEmpty(configBean.getActionBarBgColor())) { - abc.setBackgroundResource(R.color.basic_ui_action_bar_bg); + public void allFail() { + MultiStateUtils.toError(msv); + } + + private void updateActionBarByConfig(@NonNull HomeActionBarEvent event) { + if (abc != null) { + if (event.getHomeTitle() == null) { + abc.getTitleTextView().setText(R.string.homepage); } else { - if (mConfigBean == null || !TextUtils.equals(mConfigBean.getActionBarBgColor(), configBean.getActionBarBgColor())) { - try { - int color = Color.parseColor(configBean.getActionBarBgColor()); - abc.setBackgroundColor(color); - } catch (IllegalArgumentException e) { - e.printStackTrace(); - } - } + abc.getTitleTextView().setText(event.getHomeTitle()); } - if (mConfigBean == null || !TextUtils.equals(mConfigBean.getActionBarBgImageUrl(), configBean.getActionBarBgImageUrl())) { + clearActionBarIconMode(); + changeActionBarBgColor(event.getActionBarBgColor()); + if (!TextUtils.isEmpty(event.getActionBarBgImageUrl())) { GlideHelper.with(getContext()) - .load(configBean.getActionBarBgImageUrl()) - .get(new SimpleCallback() { + .asBitmap() + .load(event.getActionBarBgImageUrl()) + .getBitmap(new SimpleCallback() { @Override public void onResult(Bitmap data) { - abc.setBackground(new BitmapDrawable(data)); + if (abc != null) { + abc.setBackground(new BitmapDrawable(data)); + } } }); } } - mConfigBean = configBean; + if (iv_second_floor_background != null) { + if (!TextUtils.isEmpty(event.getSecondFloorBgImageUrl())) { + GlideHelper.with(getContext()) + .asBitmap() + .load(event.getSecondFloorBgImageUrl()) + .transformation(new BlurTransformation(event.getSecondFloorBgImageBlurPercent())) + .into(iv_second_floor_background); + } else { + setSecondFloorDefBgColor(); + } + } } - @Override - public void allFail() { - MultiStateUtils.toError(msv); + private void changeActionBarBgColor(@Nullable String colorStr) { + if (!TextUtils.isEmpty(colorStr)) { + try { + int color = Color.parseColor(colorStr); + abc.setBackgroundColor(color); + changeActionBarMode(isColorLight(color)); + return; + } catch (IllegalArgumentException ignore) { + } + } + int color = ResUtils.getThemeColor(abc, R.attr.colorMainOrSurface); + abc.setBackgroundColor(color); + } + + private void setSecondFloorDefBgColor() { + if (iv_second_floor_background == null) return; + int color = ResUtils.getThemeColor(iv_second_floor_background, R.attr.colorMain); + color = ColorUtils.changingColor(color, Color.BLACK, 0.5F); + iv_second_floor_background.setImageDrawable(new ColorDrawable(color)); + } + + private boolean isColorLight(int color) { + double lumi = LuminanceUtils.calcLuminance(color); + Context context = getContext(); + if (context != null) { + if (DarkModeUtils.isDarkMode(context)) { + return lumi > 0.1F; + } + } + return LuminanceUtils.isLight(lumi); + } + + private void clearActionBarIconMode() { + int color = ResUtils.getThemeColor(abc, R.attr.colorOnMainOrSurface); + abc.getTitleTextView().setTextColor(color); + abc.getLeftTextView().setTextColor(color); + abc.getRightTextView().setTextColor(color); + abc.getRightIconView().setColorFilter(color); + abc.getLeftIconView().setColorFilter(color); + } + + private void changeActionBarMode(boolean darkIcon) { + int color; + if (darkIcon) { + color = ResUtils.getThemeColor(abc, R.attr.colorIconDark); + } else { + color = ResUtils.getThemeColor(abc, R.attr.colorIconLight); + } + abc.getTitleTextView().setTextColor(color); + abc.getLeftTextView().setTextColor(color); + abc.getRightTextView().setTextColor(color); + abc.getRightIconView().setColorFilter(color); + abc.getLeftIconView().setColorFilter(color); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/fragment/SearchHistoryFragment.java b/app/src/main/java/per/goweii/wanandroid/module/home/fragment/SearchHistoryFragment.java index 7d487304..45433284 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/fragment/SearchHistoryFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/fragment/SearchHistoryFragment.java @@ -1,14 +1,19 @@ package per.goweii.wanandroid.module.home.fragment; -import android.support.annotation.Nullable; -import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.animation.Animation; import android.view.animation.ScaleAnimation; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewbinding.ViewBinding; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.google.android.flexbox.FlexboxLayoutManager; @@ -21,6 +26,7 @@ import per.goweii.basic.ui.dialog.TipDialog; import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.FragmentSearchHistoryBinding; import per.goweii.wanandroid.module.home.activity.SearchActivity; import per.goweii.wanandroid.module.home.model.HotKeyBean; import per.goweii.wanandroid.module.home.presenter.SearchHistoryPresenter; @@ -31,23 +37,9 @@ /** * @author CuiZhen * @date 2019/5/11 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class SearchHistoryFragment extends BaseFragment implements SearchHistoryView { - - @BindView(R.id.rv_hot) - RecyclerView rv_hot; - @BindView(R.id.ll_history) - LinearLayout ll_history; - @BindView(R.id.rv_history) - RecyclerView rv_history; - @BindView(R.id.tv_clean) - TextView tv_clean; - @BindView(R.id.tv_down) - TextView tv_down; - +public class SearchHistoryFragment extends BaseFragment implements SearchHistoryView { private BaseQuickAdapter mHotAdapter; private BaseQuickAdapter mHistoryAdapter; @@ -58,9 +50,10 @@ public static SearchHistoryFragment create() { return new SearchHistoryFragment(); } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_search_history; + protected FragmentSearchHistoryBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentSearchHistoryBinding.inflate(inflater, container, false); } @Nullable @@ -71,9 +64,9 @@ protected SearchHistoryPresenter initPresenter() { @Override protected void initView() { - rv_hot.setNestedScrollingEnabled(false); - rv_hot.setHasFixedSize(true); - rv_hot.setLayoutManager(new FlexboxLayoutManager(getContext())); + binding.rvHot.setNestedScrollingEnabled(false); + binding.rvHot.setHasFixedSize(true); + binding.rvHot.setLayoutManager(new FlexboxLayoutManager(getContext())); mHotAdapter = new BaseQuickAdapter(R.layout.rv_item_search_hot) { @Override protected void convert(BaseViewHolder helper, HotKeyBean item) { @@ -89,8 +82,8 @@ public void onItemClick(BaseQuickAdapter adapter, View view, int position) { } } }); - rv_hot.setAdapter(mHotAdapter); - rv_history.setLayoutManager(new FlexboxLayoutManager(getContext())); + binding.rvHot.setAdapter(mHotAdapter); + binding.rvHistory.setLayoutManager(new FlexboxLayoutManager(getContext())); mHistoryAdapter = new BaseQuickAdapter(R.layout.rv_item_search_history) { @Override protected void convert(BaseViewHolder helper, String item) { @@ -173,7 +166,7 @@ public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) presenter.saveHistory(mHistoryAdapter.getData()); } }); - rv_history.setAdapter(mHistoryAdapter); + binding.rvHistory.setAdapter(mHistoryAdapter); } @Override @@ -185,12 +178,12 @@ protected void loadData() { private void changeHistoryVisible() { if (mHistoryAdapter == null) { - ll_history.setVisibility(View.GONE); + binding.llHistory.setVisibility(View.GONE); } else { if (mHistoryAdapter.getData().isEmpty()) { - ll_history.setVisibility(View.GONE); + binding.llHistory.setVisibility(View.GONE); } else { - ll_history.setVisibility(View.VISIBLE); + binding.llHistory.setVisibility(View.VISIBLE); } } } @@ -203,11 +196,11 @@ private void changeRemoveMode(boolean removeMode) { mRemoveMode = removeMode; mHistoryAdapter.notifyDataSetChanged(); if (removeMode) { - tv_down.setVisibility(View.VISIBLE); - tv_clean.setVisibility(View.GONE); + binding.tvDown.setVisibility(View.VISIBLE); + binding.tvClean.setVisibility(View.GONE); } else { - tv_down.setVisibility(View.GONE); - tv_clean.setVisibility(View.VISIBLE); + binding.tvDown.setVisibility(View.GONE); + binding.tvClean.setVisibility(View.VISIBLE); } } @@ -231,7 +224,7 @@ protected void onClick2(View v) { break; case R.id.tv_clean: TipDialog.with(getContext()) - .message("确定要清除搜索历史?") + .message(R.string.make_sure_you_want_to_clear_your_search_history) .onYes(new SimpleCallback() { @Override public void onResult(Void data) { @@ -263,7 +256,7 @@ public void addHistory(String key) { if (list.size() > max) { mHistoryAdapter.remove(list.size() - 1); } - RvScrollTopUtils.smoothScrollTop(rv_history); + RvScrollTopUtils.smoothScrollTop(binding.rvHistory); presenter.saveHistory(mHistoryAdapter.getData()); changeHistoryVisible(); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/fragment/SearchResultFragment.java b/app/src/main/java/per/goweii/wanandroid/module/home/fragment/SearchResultFragment.java index 3d8109e1..b2ffbc16 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/fragment/SearchResultFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/fragment/SearchResultFragment.java @@ -1,47 +1,45 @@ package per.goweii.wanandroid.module.home.fragment; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.kennyc.view.MultiStateView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import java.util.List; - import butterknife.BindView; import per.goweii.basic.core.base.BaseFragment; import per.goweii.basic.core.utils.SmartRefreshUtils; import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.FragmentSearchResultBinding; import per.goweii.wanandroid.event.CollectionEvent; -import per.goweii.wanandroid.event.SettingChangeEvent; import per.goweii.wanandroid.module.home.presenter.SearchResultPresenter; import per.goweii.wanandroid.module.home.view.SearchResultView; -import per.goweii.wanandroid.module.main.activity.WebActivity; import per.goweii.wanandroid.module.main.adapter.ArticleAdapter; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.ArticleListBean; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; -import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; import per.goweii.wanandroid.widget.CollectView; /** * @author CuiZhen * @date 2019/5/11 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class SearchResultFragment extends BaseFragment implements SearchResultView { +public class SearchResultFragment extends BaseFragment implements SearchResultView { private static final int PAGE_START = 0; @@ -62,16 +60,6 @@ public static SearchResultFragment create() { return new SearchResultFragment(); } - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { - if (isDetached()) { - return; - } - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); - } - } - @Subscribe(threadMode = ThreadMode.MAIN) public void onCollectionEvent(CollectionEvent event) { if (isDetached()) { @@ -80,17 +68,7 @@ public void onCollectionEvent(CollectionEvent event) { if (event.getArticleId() == -1) { return; } - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.getId() == event.getArticleId()) { - if (item.isCollect() != event.isCollect()) { - item.setCollect(event.isCollect()); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - break; - } - } + mAdapter.notifyCollectionEvent(event); } @Override @@ -98,9 +76,10 @@ protected boolean isRegisterEventBus() { return true; } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_search_result; + protected FragmentSearchResultBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentSearchResultBinding.inflate(inflater, container, false); } @Nullable @@ -117,34 +96,33 @@ protected void initView() { @Override public void onRefresh() { currPage = PAGE_START; - presenter.search(currPage, mKey, true); + presenter.search(currPage, mKey); } }); rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new ArticleAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); mAdapter.setEnableLoadMore(false); mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override public void onLoadMoreRequested() { - presenter.search(currPage, mKey, false); + presenter.search(currPage, mKey); } }, rv); mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - WebActivity.start(getContext(), item); + UrlOpenUtils.Companion.with(item).open(getContext()); } } }); mAdapter.setOnItemChildViewClickListener(new ArticleAdapter.OnItemChildViewClickListener() { @Override public void onCollectClick(BaseViewHolder helper, CollectView v, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - if (!v.isChecked()) { + if (v.isChecked()) { presenter.collect(item, v); } else { presenter.uncollect(item, v); @@ -163,27 +141,37 @@ public void onResult() { @Override protected void loadData() { + MultiStateUtils.toLoading(msv); } - public void search(String key){ - if (!isAdded()) { - return; - } + public void clear() { + if (!isAdded()) return; + mKey = null; + currPage = PAGE_START; MultiStateUtils.toLoading(msv); - mAdapter.setNewData(null); + if (mAdapter != null) { + mAdapter.setNewData(null, false); + } + } + + public void search(String key) { + if (!isAdded()) return; mKey = key; currPage = PAGE_START; - presenter.search(currPage, key, false); + if (!MultiStateUtils.isContent(msv)) { + MultiStateUtils.toLoading(msv); + } + presenter.search(currPage, key); } @Override public void searchSuccess(int code, ArticleListBean data) { currPage = data.getCurPage() + PAGE_START; if (data.getCurPage() == 1) { - mAdapter.setNewData(data.getDatas()); + mAdapter.setArticleData(data.getDatas()); mAdapter.setEnableLoadMore(true); if (data.getDatas() == null || data.getDatas().isEmpty()) { - MultiStateUtils.toEmpty(msv); + MultiStateUtils.toEmpty(msv, true); } else { MultiStateUtils.toContent(msv); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/model/BannerBean.java b/app/src/main/java/per/goweii/wanandroid/module/home/model/BannerBean.java index 59ba6e9e..96b64b45 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/model/BannerBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/model/BannerBean.java @@ -5,8 +5,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class BannerBean extends BaseBean { @@ -14,12 +12,12 @@ public class BannerBean extends BaseBean { /** * desc : 一起来做个App吧 * id : 10 - * imagePath : https://www.wanandroid.com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png + * imagePath : https://wanandroid.com/blogimgs/50c115c2-cf6c-4802-aa7b-a4334de444cd.png * isVisible : 1 * order : 1 * title : 一起来做个App吧 * type : 0 - * url : http://www.wanandroid.com/blog/show/2 + * url : http://wanandroid.com/blog/show/2 */ private String desc; diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/model/HomeRequest.java b/app/src/main/java/per/goweii/wanandroid/module/home/model/HomeRequest.java index 28a0eef2..0a830772 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/model/HomeRequest.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/model/HomeRequest.java @@ -1,7 +1,7 @@ package per.goweii.wanandroid.module.home.model; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; import java.util.List; @@ -16,8 +16,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class HomeRequest extends BaseRequest { @@ -60,23 +58,8 @@ public static void getHotKeyList(RxLife rxLife, @NonNull RequestListener listener) { - cacheBean(WanCache.CacheKey.SEARCH(key, page), - ArticleListBean.class, - listener); - } - - public static void search(RxLife rxLife, boolean refresh, @IntRange(from = 0) int page, String key, @NonNull RequestListener listener) { - if (page == 0) { - cacheAndNetBean(rxLife, - WanApi.api().search(page, key), - refresh, - WanCache.CacheKey.SEARCH(key, page), - ArticleListBean.class, - listener); - } else { - rxLife.add(request(WanApi.api().search(page, key), listener)); - } + public static void search(RxLife rxLife, @IntRange(from = 0) int page, String key, @NonNull RequestListener listener) { + rxLife.add(request(WanApi.api().search(page, key), listener)); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/model/HotKeyBean.java b/app/src/main/java/per/goweii/wanandroid/module/home/model/HotKeyBean.java index f2d9b096..bd5ccef3 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/model/HotKeyBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/model/HotKeyBean.java @@ -5,8 +5,6 @@ /** * @author CuiZhen * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class HotKeyBean extends BaseBean { diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/model/ImageBean.kt b/app/src/main/java/per/goweii/wanandroid/module/home/model/ImageBean.kt new file mode 100644 index 00000000..c1665cd6 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/home/model/ImageBean.kt @@ -0,0 +1,17 @@ +package per.goweii.wanandroid.module.home.model + +data class ImageBean( + val url: String, + val gravity: String, + val width: Int, + val height: Int, + val marginLeft: Int, + val marginRight: Int, + val marginTop: Int, + val marginBottom: Int, + val deviationX: Int, + val deviationY: Int, + val rotationX: Int, + val rotationY: Int, + val rotationZ: Int, +) diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/presenter/HomePresenter.java b/app/src/main/java/per/goweii/wanandroid/module/home/presenter/HomePresenter.java index 8f01fa11..b7e7a2cc 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/presenter/HomePresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/presenter/HomePresenter.java @@ -1,11 +1,10 @@ package per.goweii.wanandroid.module.home.presenter; -import android.support.annotation.IntRange; +import androidx.annotation.IntRange; import java.util.List; import per.goweii.basic.core.base.BasePresenter; -import per.goweii.basic.utils.LogUtils; import per.goweii.rxhttp.request.base.BaseBean; import per.goweii.rxhttp.request.exception.ExceptionHandle; import per.goweii.wanandroid.event.CollectionEvent; @@ -15,16 +14,12 @@ import per.goweii.wanandroid.module.home.view.HomeView; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.ArticleListBean; -import per.goweii.wanandroid.module.main.model.ConfigBean; import per.goweii.wanandroid.module.main.model.MainRequest; -import per.goweii.wanandroid.utils.ConfigUtils; import per.goweii.wanandroid.widget.CollectView; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class HomePresenter extends BasePresenter { @@ -138,7 +133,7 @@ public void onFinish() { } public void collect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.collect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.collectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } @@ -170,7 +165,7 @@ public void onFinish() { } public void uncollect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.uncollect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.uncollectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } @@ -201,40 +196,4 @@ public void onFinish() { })); } - public void getConfig() { - ConfigBean configBean = ConfigUtils.getInstance().getConfig(); - LogUtils.i("ConfigBean", "getConfig" + configBean.toFormatJson()); - getBaseView().getConfigSuccess(configBean); - LogUtils.i("ConfigBean", ConfigUtils.getInstance().isTodayUpdate()); - if (ConfigUtils.getInstance().isTodayUpdate()) { - return; - } - MainRequest.getConfig(getRxLife(), new RequestListener() { - @Override - public void onStart() { - } - - @Override - public void onSuccess(int code, ConfigBean data) { - LogUtils.i("ConfigBean", "onSuccess" + data.toFormatJson()); - getBaseView().getConfigSuccess(data); - ConfigUtils.getInstance().setConfig(data); - } - - @Override - public void onFailed(int code, String msg) { - LogUtils.i("ConfigBean", "onFailed"); - } - - @Override - public void onError(ExceptionHandle handle) { - LogUtils.i("ConfigBean", "onError" + handle.getException().toString()); - } - - @Override - public void onFinish() { - } - }); - } - } diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/presenter/SearchHistoryPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/home/presenter/SearchHistoryPresenter.java index ae217937..5273a916 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/presenter/SearchHistoryPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/presenter/SearchHistoryPresenter.java @@ -14,15 +14,13 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class SearchHistoryPresenter extends BasePresenter { private final SearchHistoryUtils mSearchHistoryUtils = SearchHistoryUtils.newInstance(); - public void getHotKeyList(){ + public void getHotKeyList() { HomeRequest.getHotKeyList(getRxLife(), new RequestListener>() { @Override public void onStart() { @@ -52,11 +50,11 @@ public void onFinish() { }); } - public List getHistory(){ + public List getHistory() { return mSearchHistoryUtils.get(); } - public void saveHistory(List list){ + public void saveHistory(List list) { List saves = list; int max = SettingUtils.getInstance().getSearchHistoryMaxCount(); if (list != null && list.size() > max) { diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/presenter/SearchResultPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/home/presenter/SearchResultPresenter.java index e06297df..1225a251 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/presenter/SearchResultPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/presenter/SearchResultPresenter.java @@ -1,6 +1,6 @@ package per.goweii.wanandroid.module.home.presenter; -import android.support.annotation.IntRange; +import androidx.annotation.IntRange; import per.goweii.basic.core.base.BasePresenter; import per.goweii.rxhttp.request.base.BaseBean; @@ -17,41 +17,12 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class SearchResultPresenter extends BasePresenter { - public void searchCache(@IntRange(from = 0) int page, String key) { - HomeRequest.searchCache(page, key, new RequestListener() { - @Override - public void onStart() { - } - - @Override - public void onSuccess(int code, ArticleListBean data) { - if (isAttach()) { - getBaseView().searchSuccess(code, data); - } - } - - @Override - public void onFailed(int code, String msg) { - } - - @Override - public void onError(ExceptionHandle handle) { - } - - @Override - public void onFinish() { - } - }); - } - - public void search(@IntRange(from = 0) int page, String key, boolean refresh){ - HomeRequest.search(getRxLife(), refresh, page, key, new RequestListener() { + public void search(@IntRange(from = 0) int page, String key) { + HomeRequest.search(getRxLife(), page, key, new RequestListener() { @Override public void onStart() { } @@ -80,8 +51,8 @@ public void onFinish() { }); } - public void collect(ArticleBean item, final CollectView v){ - addToRxLife(MainRequest.collect(item.getId(), new RequestListener() { + public void collect(ArticleBean item, final CollectView v) { + addToRxLife(MainRequest.collectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } @@ -112,8 +83,8 @@ public void onFinish() { })); } - public void uncollect(ArticleBean item, final CollectView v){ - addToRxLife(MainRequest.uncollect(item.getId(), new RequestListener() { + public void uncollect(ArticleBean item, final CollectView v) { + addToRxLife(MainRequest.uncollectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/presenter/UserPagePresenter.java b/app/src/main/java/per/goweii/wanandroid/module/home/presenter/UserPagePresenter.java index 585b028e..6bc641bc 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/presenter/UserPagePresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/presenter/UserPagePresenter.java @@ -14,8 +14,6 @@ /** * @author CuiZhen * @date 2019/10/3 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class UserPagePresenter extends BasePresenter { @@ -51,7 +49,7 @@ public void onFinish() { } public void collect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.collect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.collectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } @@ -83,7 +81,7 @@ public void onFinish() { } public void uncollect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.uncollect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.uncollectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/presenter/WebDialogPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/home/presenter/WebDialogPresenter.java index a5a29f74..dcd60468 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/presenter/WebDialogPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/presenter/WebDialogPresenter.java @@ -1,8 +1,14 @@ package per.goweii.wanandroid.module.home.presenter; +import java.util.List; + import per.goweii.basic.core.base.BasePresenter; +import per.goweii.basic.utils.listener.SimpleCallback; +import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.rxhttp.request.base.BaseBean; import per.goweii.rxhttp.request.exception.ExceptionHandle; +import per.goweii.wanandroid.db.executor.ReadLaterExecutor; +import per.goweii.wanandroid.db.model.ReadLaterModel; import per.goweii.wanandroid.event.CollectionEvent; import per.goweii.wanandroid.http.RequestListener; import per.goweii.wanandroid.module.home.view.WebDialogView; @@ -13,14 +19,20 @@ /** * @author CuiZhen * @date 2019/8/31 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WebDialogPresenter extends BasePresenter { + private ReadLaterExecutor mReadLaterExecutor = null; + + @Override + public void attach(WebDialogView baseView) { + super.attach(baseView); + mReadLaterExecutor = new ReadLaterExecutor(); + } + public void collect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.collect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.collectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } @@ -52,7 +64,7 @@ public void onFinish() { } public void uncollect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.uncollect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.uncollectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } @@ -82,4 +94,58 @@ public void onFinish() { } })); } + + public void addReadLater(String articleUrl, String articleTitle, SimpleCallback callback) { + mReadLaterExecutor.add(articleUrl, articleTitle, new SimpleCallback() { + @Override + public void onResult(ReadLaterModel data) { + if (isAttach()) { + callback.onResult(true); + } + } + }, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + callback.onResult(false); + } + } + }); + } + + public void removeReadLater(String articleUrl, SimpleCallback callback) { + mReadLaterExecutor.remove(articleUrl, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + callback.onResult(true); + } + } + }, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + callback.onResult(false); + } + } + }); + } + + public void isReadLater(String articleUrl, SimpleCallback callback) { + mReadLaterExecutor.findByLink(articleUrl, new SimpleCallback>() { + @Override + public void onResult(List data) { + if (isAttach()) { + callback.onResult(data != null && !data.isEmpty()); + } + } + }, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + callback.onResult(false); + } + } + }); + } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/view/HomeView.java b/app/src/main/java/per/goweii/wanandroid/module/home/view/HomeView.java index a41b9577..8f5aa88c 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/view/HomeView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/view/HomeView.java @@ -6,24 +6,24 @@ import per.goweii.wanandroid.module.home.model.BannerBean; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.ArticleListBean; -import per.goweii.wanandroid.module.main.model.ConfigBean; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface HomeView extends BaseView { void allFail(); + void getBannerSuccess(int code, List data); + void getBannerFail(int code, String msg); void getArticleListSuccess(int code, ArticleListBean data); + void getArticleListFailed(int code, String msg); + void getTopArticleListSuccess(int code, List data); - void getTopArticleListFailed(int code, String msg); - void getConfigSuccess(ConfigBean configBean); + void getTopArticleListFailed(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/view/SearchHistoryView.java b/app/src/main/java/per/goweii/wanandroid/module/home/view/SearchHistoryView.java index 67c90ebc..23fa3414 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/view/SearchHistoryView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/view/SearchHistoryView.java @@ -8,11 +8,10 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface SearchHistoryView extends BaseView { void getHotKeyListSuccess(int code, List data); + void getHotKeyListFail(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/view/SearchResultView.java b/app/src/main/java/per/goweii/wanandroid/module/home/view/SearchResultView.java index caa07d57..281561ca 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/view/SearchResultView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/view/SearchResultView.java @@ -6,11 +6,10 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface SearchResultView extends BaseView { void searchSuccess(int code, ArticleListBean data); + void searchFailed(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/view/UserPageView.java b/app/src/main/java/per/goweii/wanandroid/module/home/view/UserPageView.java index 4829db5d..9c9d2d58 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/view/UserPageView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/view/UserPageView.java @@ -6,8 +6,6 @@ /** * @author CuiZhen * @date 2019/10/3 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface UserPageView extends BaseView { diff --git a/app/src/main/java/per/goweii/wanandroid/module/home/view/WebDialogView.java b/app/src/main/java/per/goweii/wanandroid/module/home/view/WebDialogView.java index d3d1a1b0..821ee4ad 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/home/view/WebDialogView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/home/view/WebDialogView.java @@ -5,8 +5,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface WebDialogView extends BaseView { diff --git a/app/src/main/java/per/goweii/wanandroid/module/knowledge/activity/KnowledgeArticleActivity.java b/app/src/main/java/per/goweii/wanandroid/module/knowledge/activity/KnowledgeArticleActivity.java index 29571e10..7b93ef39 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/knowledge/activity/KnowledgeArticleActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/knowledge/activity/KnowledgeArticleActivity.java @@ -2,37 +2,51 @@ import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; -import android.support.v4.view.ViewPager; +import android.net.Uri; +import android.text.TextUtils; +import android.view.LayoutInflater; import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager.widget.ViewPager; + +import com.kennyc.view.MultiStateView; + import net.lucode.hackware.magicindicator.MagicIndicator; import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator; +import java.util.List; + import butterknife.BindView; import per.goweii.actionbarex.common.ActionBarCommon; import per.goweii.basic.core.adapter.MultiFragmentPagerAdapter; import per.goweii.basic.core.base.BaseActivity; -import per.goweii.basic.core.mvp.MvpPresenter; import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.wanandroid.R; import per.goweii.wanandroid.common.Config; +import per.goweii.wanandroid.databinding.ActivityKnowledgeArticleBinding; import per.goweii.wanandroid.event.ScrollTopEvent; import per.goweii.wanandroid.module.knowledge.fragment.KnowledgeArticleFragment; +import per.goweii.wanandroid.module.knowledge.presenter.KnowledgePresenter; +import per.goweii.wanandroid.module.knowledge.view.KnowledgeView; +import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.ChapterBean; import per.goweii.wanandroid.utils.MagicIndicatorUtils; +import per.goweii.wanandroid.utils.MultiStateUtils; +import per.goweii.wanandroid.utils.router.Router; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class KnowledgeArticleActivity extends BaseActivity { +public class KnowledgeArticleActivity extends BaseActivity implements KnowledgeView { @BindView(R.id.abc) ActionBarCommon abc; + @BindView(R.id.msv) + MultiStateView msv; @BindView(R.id.mi) MagicIndicator mi; @BindView(R.id.vp) @@ -40,6 +54,10 @@ public class KnowledgeArticleActivity extends BaseActivity { private long lastClickTime = 0L; private int lastClickPos = 0; + private MultiFragmentPagerAdapter mAdapter; + private CommonNavigator mCommonNavigator; + private int mSuperChapterId; + private int mChapterId; public static void start(Context context, ChapterBean chapterBean, int currPos) { Intent intent = new Intent(context, KnowledgeArticleActivity.class); @@ -48,58 +66,109 @@ public static void start(Context context, ChapterBean chapterBean, int currPos) context.startActivity(intent); } + public static void start(Context context, int superChapterId, String superChapterName, int chapterId) { + Intent intent = new Intent(context, KnowledgeArticleActivity.class); + intent.putExtra("superChapterName", superChapterName); + intent.putExtra("superChapterId", superChapterId); + intent.putExtra("chapterId", chapterId); + context.startActivity(intent); + } + + public static void start(Context context, ArticleBean.TagsBean tag) { + if (tag == null) return; + String url = tag.getUrl(); + if (TextUtils.isEmpty(url)) return; + // /wxarticle/list/410/1 + // /article/list/0?cid=440 + // /project/list/1?cid=367 + // wana://wanandroid.com/wxarticle/list/410/1?cid=440 + Uri uri = Uri.parse(Router.createUrlByPath(url)); + try { + String cid = uri.getQueryParameter("cid"); + if (TextUtils.isEmpty(cid)) { + List paths = uri.getPathSegments(); + if (paths != null && paths.size() >= 3) { + cid = paths.get(2); + } + } + int chapterId = 0; + if (cid != null) { + chapterId = Integer.parseInt(cid); + } + if (chapterId > 0) { + start(context, 0, "", chapterId); + } + } catch (Exception e) { + // ignore + } + } + + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_knowledge_article; + protected ActivityKnowledgeArticleBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityKnowledgeArticleBinding.inflate(inflater); } @Nullable @Override - protected MvpPresenter initPresenter() { - return null; + protected KnowledgePresenter initPresenter() { + return new KnowledgePresenter(); } @Override protected void initView() { - ChapterBean bean = (ChapterBean) getIntent().getSerializableExtra("chapterBean"); - int currPos = getIntent().getIntExtra("currPos", 0); - - abc.getTitleTextView().setText(bean.getName()); abc.getTitleTextView().setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { notifyScrollTop(vp.getCurrentItem()); } }); + mAdapter = new MultiFragmentPagerAdapter<>( + getSupportFragmentManager(), + new MultiFragmentPagerAdapter.FragmentCreator() { + @Override + public KnowledgeArticleFragment create(ChapterBean data, int pos) { + return KnowledgeArticleFragment.create(data, pos); + } - MultiFragmentPagerAdapter adapter = - new MultiFragmentPagerAdapter<>( - getSupportFragmentManager(), - new MultiFragmentPagerAdapter.FragmentCreator() { - @Override - public KnowledgeArticleFragment create(ChapterBean data, int pos) { - return KnowledgeArticleFragment.create(data, pos); - } - - @Override - public String getTitle(ChapterBean data) { - return data.getName(); - } - }); - vp.setAdapter(adapter); - CommonNavigator commonNavigator = MagicIndicatorUtils.commonNavigator(mi, vp, adapter, new SimpleCallback() { + @Override + public String getTitle(ChapterBean data) { + return data.getName(); + } + }); + vp.setAdapter(mAdapter); + mCommonNavigator = MagicIndicatorUtils.commonNavigator(mi, vp, mAdapter, new SimpleCallback() { @Override public void onResult(Integer data) { notifyScrollTop(data); } }); - adapter.setDataList(bean.getChildren()); - commonNavigator.notifyDataSetChanged(); - vp.setCurrentItem(currPos); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + // ignore } @Override protected void loadData() { + ChapterBean bean = (ChapterBean) getIntent().getSerializableExtra("chapterBean"); + int currPos = getIntent().getIntExtra("currPos", 0); + if (bean != null) { + initVP(bean, currPos); + } else { + MultiStateUtils.toLoading(msv); + mSuperChapterId = getIntent().getIntExtra("superChapterId", -1); + mChapterId = getIntent().getIntExtra("chapterId", -1); + if (mSuperChapterId <= 0 && mChapterId <= 0) { + MultiStateUtils.toError(msv); + } else { + String superChapterName = getIntent().getStringExtra("superChapterName"); + abc.getTitleTextView().setText(superChapterName); + presenter.getKnowledgeListCacheAndNet(); + } + } } private void notifyScrollTop(int pos) { @@ -110,4 +179,56 @@ private void notifyScrollTop(int pos) { lastClickPos = pos; lastClickTime = currClickTime; } + + private void initVP(ChapterBean bean, int currPos) { + MultiStateUtils.toContent(msv); + abc.getTitleTextView().setText(bean.getName()); + mAdapter.setDataList(bean.getChildren()); + mCommonNavigator.notifyDataSetChanged(); + vp.setCurrentItem(currPos); + } + + @Override + public void getKnowledgeListSuccess(int code, List data) { + ChapterBean superChapterBean = null; + for (ChapterBean bean : data) { + if (bean.getId() == mSuperChapterId) { + superChapterBean = bean; + break; + } + } + int currPos = 0; + if (superChapterBean == null) { + for (ChapterBean scb : data) { + List chapters = scb.getChildren(); + for (int i = 0; i < chapters.size(); i++) { + ChapterBean cb = chapters.get(i); + if (cb.getId() == mChapterId) { + superChapterBean = scb; + currPos = i; + break; + } + } + } + } else { + List chapters = superChapterBean.getChildren(); + for (int i = 0; i < chapters.size(); i++) { + ChapterBean cb = chapters.get(i); + if (cb.getId() == mChapterId) { + currPos = i; + break; + } + } + } + if (superChapterBean != null) { + initVP(superChapterBean, currPos); + } else { + MultiStateUtils.toError(msv); + } + } + + @Override + public void getKnowledgeListFail(int code, String msg) { + MultiStateUtils.toError(msv); + } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/knowledge/adapter/KnowledgeAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/knowledge/adapter/KnowledgeAdapter.java index 73d073fe..1130a51d 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/knowledge/adapter/KnowledgeAdapter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/knowledge/adapter/KnowledgeAdapter.java @@ -1,10 +1,12 @@ package per.goweii.wanandroid.module.knowledge.adapter; -import android.support.annotation.NonNull; +import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; +import androidx.annotation.NonNull; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.google.android.flexbox.FlexboxLayout; @@ -18,8 +20,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class KnowledgeAdapter extends BaseQuickAdapter { @@ -43,7 +43,7 @@ protected void convert(BaseViewHolder helper, ChapterBean item) { for (int i = 0; i < item.getChildren().size(); i++) { ChapterBean childItem = item.getChildren().get(i); TextView child = createOrGetCacheFlexItemTextView(fbl); - child.setText(childItem.getName()); + child.setText(Html.fromHtml(childItem.getName())); int finalI = i; child.setOnClickListener(new View.OnClickListener() { @Override diff --git a/app/src/main/java/per/goweii/wanandroid/module/knowledge/adapter/KnowledgeArticleFragmentPagerAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/knowledge/adapter/KnowledgeArticleFragmentPagerAdapter.java index d64c9549..3ca851b7 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/knowledge/adapter/KnowledgeArticleFragmentPagerAdapter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/knowledge/adapter/KnowledgeArticleFragmentPagerAdapter.java @@ -1,16 +1,14 @@ package per.goweii.wanandroid.module.knowledge.adapter; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; import java.util.List; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class KnowledgeArticleFragmentPagerAdapter extends FragmentStatePagerAdapter { diff --git a/app/src/main/java/per/goweii/wanandroid/module/knowledge/fragment/KnowledgeArticleFragment.java b/app/src/main/java/per/goweii/wanandroid/module/knowledge/fragment/KnowledgeArticleFragment.java index c68cacb6..5a92c83f 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/knowledge/fragment/KnowledgeArticleFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/knowledge/fragment/KnowledgeArticleFragment.java @@ -1,52 +1,50 @@ package per.goweii.wanandroid.module.knowledge.fragment; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.kennyc.view.MultiStateView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import java.util.List; - import butterknife.BindView; import per.goweii.basic.core.base.BaseFragment; import per.goweii.basic.core.utils.SmartRefreshUtils; import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.FragmentKnowledgeArticleBinding; import per.goweii.wanandroid.event.CollectionEvent; import per.goweii.wanandroid.event.LoginEvent; import per.goweii.wanandroid.event.ScrollTopEvent; -import per.goweii.wanandroid.event.SettingChangeEvent; import per.goweii.wanandroid.module.knowledge.presenter.KnowledgeArticlePresenter; import per.goweii.wanandroid.module.knowledge.view.KnowledgeArticleView; -import per.goweii.wanandroid.module.main.activity.WebActivity; import per.goweii.wanandroid.module.main.adapter.ArticleAdapter; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.ArticleListBean; import per.goweii.wanandroid.module.main.model.ChapterBean; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; import per.goweii.wanandroid.utils.RvScrollTopUtils; -import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; import per.goweii.wanandroid.widget.CollectView; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class KnowledgeArticleFragment extends BaseFragment implements KnowledgeArticleView { +public class KnowledgeArticleFragment extends BaseFragment implements KnowledgeArticleView { private static final int PAGE_START = 0; @@ -82,17 +80,7 @@ public void onCollectionEvent(CollectionEvent event) { if (event.getArticleId() == -1) { return; } - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.getId() == event.getArticleId()) { - if (item.isCollect() != event.isCollect()) { - item.setCollect(event.isCollect()); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - break; - } - } + mAdapter.notifyCollectionEvent(event); } @Subscribe(threadMode = ThreadMode.MAIN) @@ -104,24 +92,7 @@ public void onLoginEvent(LoginEvent event) { currPage = PAGE_START; getKnowledgeArticleList(true); } else { - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.isCollect()) { - item.setCollect(false); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - } - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { - if (isDetached()) { - return; - } - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); + mAdapter.notifyAllUnCollect(); } } @@ -143,9 +114,10 @@ protected boolean isRegisterEventBus() { return true; } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_knowledge_article; + protected FragmentKnowledgeArticleBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentKnowledgeArticleBinding.inflate(inflater, container, false); } @Nullable @@ -173,7 +145,6 @@ public void onRefresh() { }); rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new ArticleAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); mAdapter.setEnableLoadMore(false); mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override @@ -184,18 +155,18 @@ public void onLoadMoreRequested() { mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - WebActivity.start(getContext(), item); + UrlOpenUtils.Companion.with(item).open(getContext()); } } }); mAdapter.setOnItemChildViewClickListener(new ArticleAdapter.OnItemChildViewClickListener() { @Override public void onCollectClick(BaseViewHolder helper, CollectView v, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - if (!v.isChecked()) { + if (v.isChecked()) { presenter.collect(item, v); } else { presenter.uncollect(item, v); @@ -243,7 +214,7 @@ public void getKnowledgeArticleList(boolean refresh) { public void getKnowledgeArticleListSuccess(int code, ArticleListBean data) { currPage = data.getCurPage() + PAGE_START; if (data.getCurPage() == 1) { - mAdapter.setNewData(data.getDatas()); + mAdapter.setArticleData(data.getDatas()); mAdapter.setEnableLoadMore(true); if (data.getDatas() == null || data.getDatas().isEmpty()) { MultiStateUtils.toEmpty(msv); diff --git a/app/src/main/java/per/goweii/wanandroid/module/knowledge/fragment/KnowledgeFragment.java b/app/src/main/java/per/goweii/wanandroid/module/knowledge/fragment/KnowledgeFragment.java index 59a29feb..f9f0acba 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/knowledge/fragment/KnowledgeFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/knowledge/fragment/KnowledgeFragment.java @@ -1,8 +1,12 @@ package per.goweii.wanandroid.module.knowledge.fragment; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.kennyc.view.MultiStateView; @@ -13,7 +17,7 @@ import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.common.ScrollTop; +import per.goweii.wanandroid.databinding.FragmentKnowledgeNavigationChildBinding; import per.goweii.wanandroid.module.knowledge.activity.KnowledgeArticleActivity; import per.goweii.wanandroid.module.knowledge.adapter.KnowledgeAdapter; import per.goweii.wanandroid.module.knowledge.presenter.KnowledgePresenter; @@ -25,11 +29,9 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class KnowledgeFragment extends BaseFragment implements ScrollTop, KnowledgeView { +public class KnowledgeFragment extends BaseFragment implements RvScrollTopUtils.ScrollTop, KnowledgeView { @BindView(R.id.msv) MultiStateView msv; @@ -42,9 +44,10 @@ public static KnowledgeFragment create() { return new KnowledgeFragment(); } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_knowledge_navigation_child; + protected FragmentKnowledgeNavigationChildBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentKnowledgeNavigationChildBinding.inflate(inflater, container, false); } @Nullable diff --git a/app/src/main/java/per/goweii/wanandroid/module/knowledge/model/KnowledgeRequest.java b/app/src/main/java/per/goweii/wanandroid/module/knowledge/model/KnowledgeRequest.java index 8fbb0d86..ad6dbcc9 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/knowledge/model/KnowledgeRequest.java +++ b/app/src/main/java/per/goweii/wanandroid/module/knowledge/model/KnowledgeRequest.java @@ -1,7 +1,7 @@ package per.goweii.wanandroid.module.knowledge.model; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; import java.util.List; @@ -16,8 +16,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class KnowledgeRequest extends BaseRequest { @@ -37,8 +35,17 @@ public static void getKnowledgeList(RxLife rxLife, @NonNull RequestListener> listener) { + cacheAndNetList(rxLife, + WanApi.api().getKnowledgeList(), + false, + WanCache.CacheKey.KNOWLEDGE_LIST, + ChapterBean.class, + listener); + } + public static void getKnowledgeArticleListCache(int id, @IntRange(from = 0) int page, @NonNull RequestListener listener) { - cacheBean(WanCache.CacheKey.KNOWLEDGE_ARTICLE_LIST(id, page), + cacheBean(WanCache.CacheKey.CHAPTER_ARTICLE_LIST(id, page, 0), ArticleListBean.class, listener); } @@ -46,13 +53,13 @@ public static void getKnowledgeArticleListCache(int id, @IntRange(from = 0) int public static void getKnowledgeArticleList(RxLife rxLife, boolean refresh, int id, @IntRange(from = 0) int page, @NonNull RequestListener listener) { if (page == 0) { cacheAndNetBean(rxLife, - WanApi.api().getKnowledgeArticleList(page, id), + WanApi.api().getChapterArticleList(page, id, 0), refresh, - WanCache.CacheKey.KNOWLEDGE_ARTICLE_LIST(id, page), + WanCache.CacheKey.CHAPTER_ARTICLE_LIST(id, page, 0), ArticleListBean.class, listener); } else { - rxLife.add(request(WanApi.api().getKnowledgeArticleList(page, id), listener)); + rxLife.add(request(WanApi.api().getChapterArticleList(page, id, 0), listener)); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/knowledge/presenter/KnowledgeArticlePresenter.java b/app/src/main/java/per/goweii/wanandroid/module/knowledge/presenter/KnowledgeArticlePresenter.java index 50745cb0..f71d8141 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/knowledge/presenter/KnowledgeArticlePresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/knowledge/presenter/KnowledgeArticlePresenter.java @@ -16,8 +16,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class KnowledgeArticlePresenter extends BasePresenter { @@ -37,7 +35,7 @@ public void onFailed(int code, String msg) { }); } - public void getKnowledgeArticleList(int id, int page, boolean refresh){ + public void getKnowledgeArticleList(int id, int page, boolean refresh) { KnowledgeRequest.getKnowledgeArticleList(getRxLife(), refresh, id, page, new RequestCallback() { @Override public void onSuccess(int code, ArticleListBean data) { @@ -55,8 +53,8 @@ public void onFailed(int code, String msg) { }); } - public void collect(ArticleBean item, final CollectView v){ - addToRxLife(MainRequest.collect(item.getId(), new RequestListener() { + public void collect(ArticleBean item, final CollectView v) { + addToRxLife(MainRequest.collectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } @@ -87,8 +85,8 @@ public void onFinish() { })); } - public void uncollect(ArticleBean item, final CollectView v){ - addToRxLife(MainRequest.uncollect(item.getId(), new RequestListener() { + public void uncollect(ArticleBean item, final CollectView v) { + addToRxLife(MainRequest.uncollectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } diff --git a/app/src/main/java/per/goweii/wanandroid/module/knowledge/presenter/KnowledgePresenter.java b/app/src/main/java/per/goweii/wanandroid/module/knowledge/presenter/KnowledgePresenter.java index 0ece1f7e..06e755eb 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/knowledge/presenter/KnowledgePresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/knowledge/presenter/KnowledgePresenter.java @@ -12,8 +12,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class KnowledgePresenter extends BasePresenter { @@ -74,4 +72,34 @@ public void onFinish() { } }); } + + public void getKnowledgeListCacheAndNet() { + KnowledgeRequest.getKnowledgeListCacheAndNet(getRxLife(), new RequestListener>() { + @Override + public void onStart() { + } + + @Override + public void onSuccess(int code, List data) { + if (isAttach()) { + getBaseView().getKnowledgeListSuccess(code, data); + } + } + + @Override + public void onFailed(int code, String msg) { + if (isAttach()) { + getBaseView().getKnowledgeListFail(code, msg); + } + } + + @Override + public void onError(ExceptionHandle handle) { + } + + @Override + public void onFinish() { + } + }); + } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/knowledge/view/KnowledgeArticleView.java b/app/src/main/java/per/goweii/wanandroid/module/knowledge/view/KnowledgeArticleView.java index f358ebfc..f4b96d65 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/knowledge/view/KnowledgeArticleView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/knowledge/view/KnowledgeArticleView.java @@ -6,11 +6,10 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface KnowledgeArticleView extends BaseView { void getKnowledgeArticleListSuccess(int code, ArticleListBean data); + void getKnowledgeArticleListFail(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/knowledge/view/KnowledgeView.java b/app/src/main/java/per/goweii/wanandroid/module/knowledge/view/KnowledgeView.java index d21b23fb..7c2c02ce 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/knowledge/view/KnowledgeView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/knowledge/view/KnowledgeView.java @@ -8,11 +8,10 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface KnowledgeView extends BaseView { void getKnowledgeListSuccess(int code, List data); + void getKnowledgeListFail(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/activity/AuthActivity.java b/app/src/main/java/per/goweii/wanandroid/module/login/activity/AuthActivity.java new file mode 100644 index 00000000..1c4dec75 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/login/activity/AuthActivity.java @@ -0,0 +1,205 @@ +package per.goweii.wanandroid.module.login.activity; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.view.LayoutInflater; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager.widget.ViewPager; + +import butterknife.BindView; +import kotlin.Unit; +import kotlin.jvm.functions.Function0; +import per.goweii.actionbarex.common.ActionBarCommon; +import per.goweii.basic.core.adapter.FixedFragmentPagerAdapter; +import per.goweii.basic.core.base.BaseActivity; +import per.goweii.basic.ui.toast.ToastMaker; +import per.goweii.basic.utils.SoftInputHelper; +import per.goweii.basic.utils.display.DisplayInfoUtils; +import per.goweii.swipeback.SwipeBackAbility; +import per.goweii.swipeback.SwipeBackDirection; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.ActivityAuthBinding; +import per.goweii.wanandroid.module.login.fragment.LoginFragment; +import per.goweii.wanandroid.module.login.fragment.RegisterFragment; +import per.goweii.wanandroid.module.login.model.LoginInfoEntity; +import per.goweii.wanandroid.module.login.presenter.AuthPresenter; +import per.goweii.wanandroid.module.login.view.AuthView; +import per.goweii.wanandroid.widget.LogoAnimView; +import per.goweii.wanandroid.widget.ParallaxStackLayout; + +/** + * @author CuiZhen + * @date 2019/5/15 + * GitHub: https://github.com/goweii + */ +public class AuthActivity extends BaseActivity implements AuthView, SwipeBackAbility.Direction { + + private static final int REQ_CODE_OPEN_QUICK_LOGIN = 1; + private static final int REQ_CODE_USE_QUICK_LOGIN = 2; + + @BindView(R.id.abc) + ActionBarCommon abc; + @BindView(R.id.rl_input) + ParallaxStackLayout rl_input; + @BindView(R.id.iv_circle_1) + ImageView iv_circle_1; + @BindView(R.id.iv_circle_2) + ImageView iv_circle_2; + @BindView(R.id.vp) + ViewPager vp; + @BindView(R.id.lav) + LogoAnimView lav; + + private SoftInputHelper mSoftInputHelper; + + private LoginFragment loginFragment = null; + private RegisterFragment registerFragment = null; + + public static void startPasswordLogin(Context context) { + Intent intent = new Intent(context, AuthActivity.class); + context.startActivity(intent); + if (context instanceof Activity) { + Activity activity = (Activity) context; + activity.overridePendingTransition(R.anim.swipeback_activity_open_bottom_in, + R.anim.swipeback_activity_open_alpha_out); + } + } + + public static void startQuickLogin(Context context) { + QuickLoginActivity.Companion.startForUse(context, true); + } + + @SwipeBackDirection + @Override + public int swipeBackDirection() { + return SwipeBackDirection.BOTTOM; + } + + @Nullable + @Override + protected ActivityAuthBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityAuthBinding.inflate(inflater); + } + + @Nullable + @Override + protected AuthPresenter initPresenter() { + return new AuthPresenter(); + } + + @Override + protected void initView() { + int pt = DisplayInfoUtils.getInstance().getStatusBarHeight() + getResources().getDimensionPixelSize(R.dimen.action_bar_height); + rl_input.setPadding(0, pt, 0, 0); + mSoftInputHelper = SoftInputHelper.attach(this).moveBy(rl_input).moveWithScroll(); + FixedFragmentPagerAdapter adapter = new FixedFragmentPagerAdapter(getSupportFragmentManager()); + vp.setAdapter(adapter); + loginFragment = LoginFragment.create(); + registerFragment = RegisterFragment.create(); + adapter.setFragmentList(loginFragment, registerFragment); + } + + @Override + protected void loadData() { + //tryUseLoginByBiometric(); + } + + @Override + protected void onStart() { + super.onStart(); + } + + @Override + protected void onStop() { + super.onStop(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + } + + @Override + public void finish() { + super.finish(); + overridePendingTransition( + R.anim.swipeback_activity_close_alpha_in, + R.anim.swipeback_activity_close_bottom_out + ); + } + + public SoftInputHelper getSoftInputHelper() { + return mSoftInputHelper; + } + + public void changeToRegister() { + if (vp != null) { + vp.setCurrentItem(1); + } + } + + public void changeToLogin() { + if (vp != null) { + vp.setCurrentItem(0); + } + } + + public void tryOpenLoginByBiometric(String username, String password) { + QuickLoginActivity.Companion + .startForOpen(this, new LoginInfoEntity(username, password), REQ_CODE_OPEN_QUICK_LOGIN); + } + + public void tryUseLoginByBiometric() { + QuickLoginActivity.Companion + .startForUse(this, false, REQ_CODE_USE_QUICK_LOGIN); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + switch (requestCode) { + case REQ_CODE_OPEN_QUICK_LOGIN: + if (resultCode == RESULT_OK) { + ToastMaker.showShort(getString(R.string.enabled_quick_sign_in_successfully)); + } + finish(); + break; + case REQ_CODE_USE_QUICK_LOGIN: + if (resultCode == RESULT_OK) { + ToastMaker.showShort(getString(R.string.quick_sign_in_successful)); + finish(); + } else { + ToastMaker.showShort(getString(R.string.quick_sign_in_failed)); + } + break; + default: + break; + } + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + lav.randomBlink(); + } + + public void doEyeAnim(boolean close) { + if (close) { + lav.close(null); + } else { + lav.open(new Function0() { + @Override + public Unit invoke() { + if (lav != null) { + lav.randomBlink(); + } + return null; + } + }); + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/activity/LoginActivity.java b/app/src/main/java/per/goweii/wanandroid/module/login/activity/LoginActivity.java deleted file mode 100644 index b6fe06bf..00000000 --- a/app/src/main/java/per/goweii/wanandroid/module/login/activity/LoginActivity.java +++ /dev/null @@ -1,205 +0,0 @@ -package per.goweii.wanandroid.module.login.activity; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.support.annotation.Nullable; -import android.support.v4.view.ViewPager; -import android.view.View; -import android.view.WindowManager; -import android.widget.ImageView; -import android.widget.RelativeLayout; - -import java.util.Random; - -import butterknife.BindView; -import per.goweii.basic.core.adapter.FixedFragmentPagerAdapter; -import per.goweii.basic.core.base.BaseActivity; -import per.goweii.basic.utils.LogUtils; -import per.goweii.swipeback.SwipeBackDirection; -import per.goweii.wanandroid.R; -import per.goweii.wanandroid.module.login.fragment.LoginFragment; -import per.goweii.wanandroid.module.login.fragment.RegisterFragment; -import per.goweii.wanandroid.module.login.presenter.LoginPresenter; - -/** - * @author CuiZhen - * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public class LoginActivity extends BaseActivity { - - @BindView(R.id.rl_input) - RelativeLayout rl_input; - @BindView(R.id.iv_circle_1) - ImageView iv_circle_1; - @BindView(R.id.iv_circle_2) - ImageView iv_circle_2; - @BindView(R.id.vp) - ViewPager vp; - - private boolean isRunning = false; - private AnimatorSet mSet1; - private AnimatorSet mSet2; - - public static void start(Context context) { - Intent intent = new Intent(context, LoginActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - if (context instanceof Activity) { - Activity activity = (Activity) context; - activity.overridePendingTransition(R.anim.swipeback_activity_open_bottom_in, - R.anim.swipeback_activity_open_top_out); - } - } - - @Override - protected int swipeBackDirection() { - return SwipeBackDirection.FROM_TOP; - } - - @Override - protected int getLayoutId() { - return R.layout.activity_login; - } - - @Nullable - @Override - protected LoginPresenter initPresenter() { - return null; - } - - @Override - protected void initView() { - getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING); - FixedFragmentPagerAdapter adapter = new FixedFragmentPagerAdapter(getSupportFragmentManager()); - vp.setAdapter(adapter); - adapter.setFragmentList(LoginFragment.create(), RegisterFragment.create()); - } - - @Override - protected void loadData() { - } - - @Override - protected void onStart() { - isRunning = true; - mSet1 = startCircleAnim(iv_circle_1); - mSet2 = startCircleAnim(iv_circle_2); - super.onStart(); - } - - @Override - protected void onStop() { - isRunning = false; - stopCircleAnim(); - super.onStop(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - } - - @Override - public void finish() { - super.finish(); - overridePendingTransition(R.anim.swipeback_activity_close_top_in, - R.anim.swipeback_activity_close_bottom_out); - } - - public RelativeLayout getRl_input() { - return rl_input; - } - - public void changeToRegister() { - if (vp != null) { - vp.setCurrentItem(1); - } - } - - public void changeToLogin() { - if (vp != null) { - vp.setCurrentItem(0); - } - } - - private void stopCircleAnim() { - if (mSet1 != null) { - mSet1.cancel(); - mSet1 = null; - } - if (mSet2 != null) { - mSet2.cancel(); - mSet2 = null; - } - } - - private AnimatorSet startCircleAnim(View target) { - if (target == null) { - return null; - } - float[] xy = calculateRandomXY(); - AnimatorSet set = createTranslationAnimator(target, xy[0], xy[1]); - set.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - } - - @Override - public void onAnimationEnd(Animator animation) { - if (isRunning) { - startCircleAnim(target); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - } - - @Override - public void onAnimationRepeat(Animator animation) { - } - }); - set.start(); - return set; - } - - private final long mMaxMoveDuration = 20000L; - private final int mMaxMoveDistanceX = 200; - private final int mMaxMoveDistanceY = 20; - - private AnimatorSet createTranslationAnimator(View target, float toX, float toY) { - float fromX = target.getTranslationX(); - float fromY = target.getTranslationY(); - long duration = calculateDuration(fromX, fromY, toX, toY); - ObjectAnimator animatorX = ObjectAnimator.ofFloat(target, "translationX", fromX, toX); - animatorX.setDuration(duration); - ObjectAnimator animatorY = ObjectAnimator.ofFloat(target, "translationY", fromY, toY); - animatorY.setDuration(duration); - AnimatorSet set = new AnimatorSet(); - set.playTogether(animatorX, animatorY); - return set; - } - - private Random mRandom = new Random(); - - private float[] calculateRandomXY() { - float x = mRandom.nextInt(mMaxMoveDistanceX) - (mMaxMoveDistanceX * 0.5F); - float y = mRandom.nextInt(mMaxMoveDistanceY) - (mMaxMoveDistanceY * 0.5F); - return new float[]{x, y}; - } - - private long calculateDuration(float x1, float y1, float x2, float y2) { - float distance = (float) Math.abs(Math.sqrt(Math.pow(Math.abs((x1 - x2)), 2) + Math.pow(Math.abs((y1 - y2)), 2))); - float maxDistance = (float) Math.abs(Math.sqrt(Math.pow(mMaxMoveDistanceX, 2) + Math.pow(mMaxMoveDistanceY, 2))); - long duration = (long) (mMaxMoveDuration * (distance / maxDistance)); - LogUtils.i("calculateDuration", "distance=" + distance + ", duration=" + duration); - return duration; - } -} diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/activity/QuickLoginActivity.kt b/app/src/main/java/per/goweii/wanandroid/module/login/activity/QuickLoginActivity.kt new file mode 100644 index 00000000..3abae8e1 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/login/activity/QuickLoginActivity.kt @@ -0,0 +1,173 @@ +package per.goweii.wanandroid.module.login.activity + +import android.app.Activity +import android.content.Context +import android.content.ContextWrapper +import android.content.Intent +import android.os.Build +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import androidx.annotation.RequiresApi +import per.goweii.basic.core.base.BaseActivity +import per.goweii.basic.utils.Base64Utils.decodeToBytes +import per.goweii.swipeback.SwipeBackAbility +import per.goweii.swipeback.SwipeBackDirection +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.ActivityQuickLoginBinding +import per.goweii.wanandroid.event.LoginEvent +import per.goweii.wanandroid.module.login.model.LoginInfoEntity +import per.goweii.wanandroid.module.login.model.UserEntity +import per.goweii.wanandroid.module.login.presenter.QuickLoginPresenter +import per.goweii.wanandroid.module.login.view.QuickLoginView +import per.goweii.wanandroid.utils.biometric.BiometricHelper + +class QuickLoginActivity : BaseActivity(), QuickLoginView, SwipeBackAbility.Direction { + companion object { + private const val PARAMS_OPEN_OR_USE = "openOrUse" + private const val PARAMS_START_PASSWORD_LOGIN_ON_FAIL = "startPasswordLoginOnFail" + + const val PARAMS_LOGIN_INFO = "login_info" + const val RESULT_CODE_FAILED = RESULT_FIRST_USER + 1 + + fun startForOpen(activity: Activity, loginInfoEntity: LoginInfoEntity, reqCode: Int) { + activity.startActivityForResult(Intent(activity, QuickLoginActivity::class.java).apply { + putExtra(PARAMS_OPEN_OR_USE, true) + putExtra(PARAMS_LOGIN_INFO, loginInfoEntity) + }, reqCode) + activity.overridePendingTransition(0, 0) + } + + fun startForUse(activity: Activity, startPasswordLoginOnFail: Boolean, reqCode: Int) { + activity.startActivityForResult(Intent(activity, QuickLoginActivity::class.java).apply { + putExtra(PARAMS_OPEN_OR_USE, false) + putExtra(PARAMS_START_PASSWORD_LOGIN_ON_FAIL, startPasswordLoginOnFail) + }, reqCode) + activity.overridePendingTransition(0, 0) + } + + fun startForUse(context: Context, startPasswordLoginOnFail: Boolean) { + val activity: Activity? = if (context is Activity) { + context + } else { + if (context is ContextWrapper && context.baseContext is Activity) { + context.baseContext as Activity + } else { + null + } + } + context.startActivity(Intent(context, QuickLoginActivity::class.java).apply { + putExtra(PARAMS_OPEN_OR_USE, false) + putExtra(PARAMS_START_PASSWORD_LOGIN_ON_FAIL, startPasswordLoginOnFail) + if (activity == null) { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + }) + activity?.overridePendingTransition(0, 0) + } + } + + private var openOrUse = false + private var startPasswordLoginOnFail = false + private lateinit var biometricHelper: BiometricHelper + + override fun initViewBinding(inflater: LayoutInflater): ActivityQuickLoginBinding { + return ActivityQuickLoginBinding.inflate(inflater) + } + + override fun initPresenter(): QuickLoginPresenter { + return QuickLoginPresenter() + } + + override fun initView() { + openOrUse = intent.getBooleanExtra(PARAMS_OPEN_OR_USE, openOrUse) + startPasswordLoginOnFail = intent.getBooleanExtra(PARAMS_START_PASSWORD_LOGIN_ON_FAIL, startPasswordLoginOnFail) + biometricHelper = BiometricHelper(this) + } + + override fun loadData() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { + finishWithFailed() + return + } + if (!biometricHelper.canAuth()) { + finishWithFailed() + return + } + if (openOrUse) { + val loginInfoEntity = intent.getSerializableExtra(PARAMS_LOGIN_INFO) as? LoginInfoEntity? + tryOpenLoginByBiometric(loginInfoEntity) + } else { + tryLoginByBiometric() + } + } + + @RequiresApi(Build.VERSION_CODES.M) + private fun tryOpenLoginByBiometric(loginInfoEntity: LoginInfoEntity?) { + biometricHelper.onAuthSuccess = { cipher -> + if (presenter.encodeAndSaveLoginInfo(cipher, loginInfoEntity)) { + finishWithSuccess() + } else { + finishWithFailed() + } + } + biometricHelper.onAuthFailure = {} + biometricHelper.onAuthError = { _, _ -> + finishWithFailed() + } + biometricHelper.authForEncode(getString(R.string.whether_to_enable_quick_login), getString(R.string.not_enabled_yet)) + } + + @RequiresApi(Build.VERSION_CODES.M) + private fun tryLoginByBiometric() { + val loginInfoStr: String = presenter.loginInfo + val ivStr: String = presenter.cipherIV + if (TextUtils.isEmpty(loginInfoStr) || TextUtils.isEmpty(ivStr)) { + finishWithFailed() + return + } + biometricHelper.onAuthSuccess = { cipher -> + val loginInfoEntity = presenter.decodeLoginInfo(cipher, loginInfoStr) + if (loginInfoEntity == null || loginInfoEntity.isEmpty) { + finishWithFailed() + } else { + presenter?.login(loginInfoEntity.username, loginInfoEntity.password) + } + } + biometricHelper.onAuthFailure = {} + biometricHelper.onAuthError = { _, _ -> + finishWithFailed() + } + val ivBytes = decodeToBytes(ivStr) + biometricHelper.authForDecode(ivBytes, getString(R.string.quick_sign_in), getString(R.string.password_sign_in)) + } + + private fun finishWithFailed() { + if (!openOrUse) { + if (startPasswordLoginOnFail) { + AuthActivity.startPasswordLogin(this) + } + } + setResult(RESULT_CODE_FAILED) + finish() + } + + private fun finishWithSuccess() { + setResult(RESULT_OK) + finish() + } + + override fun loginSuccess(code: Int, data: UserEntity) { + LoginEvent(true).post() + finishWithSuccess() + } + + override fun loginFailed(code: Int, msg: String?) { + finishWithFailed() + } + + @SwipeBackDirection + override fun swipeBackDirection(): Int { + return 0 + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/fragment/LoginFragment.java b/app/src/main/java/per/goweii/wanandroid/module/login/fragment/LoginFragment.java index 85896d54..44d38b14 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/login/fragment/LoginFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/login/fragment/LoginFragment.java @@ -1,52 +1,57 @@ package per.goweii.wanandroid.module.login.fragment; import android.content.Context; -import android.support.annotation.Nullable; +import android.os.Bundle; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.LinearLayout; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import butterknife.BindView; import butterknife.OnClick; import per.goweii.basic.core.base.BaseFragment; import per.goweii.basic.ui.toast.ToastMaker; +import per.goweii.basic.utils.InputMethodUtils; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.FragmentLoginBinding; import per.goweii.wanandroid.event.LoginEvent; -import per.goweii.wanandroid.module.login.activity.LoginActivity; -import per.goweii.wanandroid.module.login.model.LoginBean; +import per.goweii.wanandroid.module.login.activity.AuthActivity; +import per.goweii.wanandroid.module.login.model.UserEntity; import per.goweii.wanandroid.module.login.presenter.LoginPresenter; import per.goweii.wanandroid.module.login.view.LoginView; -import per.goweii.wanandroid.utils.KeyboardHelper; import per.goweii.wanandroid.widget.InputView; +import per.goweii.wanandroid.widget.PasswordInputView; import per.goweii.wanandroid.widget.SubmitView; /** * @author CuiZhen * @date 2019/5/16 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class LoginFragment extends BaseFragment implements LoginView { +public class LoginFragment extends BaseFragment implements LoginView { @BindView(R.id.ll_go_register) LinearLayout ll_go_register; @BindView(R.id.piv_login_account) InputView piv_account; @BindView(R.id.piv_login_password) - InputView piv_password; + PasswordInputView piv_password; @BindView(R.id.sv_login) SubmitView sv_login; - private KeyboardHelper mKeyboardHelper; - private LoginActivity mActivity; + private AuthActivity mActivity; - public static LoginFragment create(){ + public static LoginFragment create() { return new LoginFragment(); } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_login; + protected FragmentLoginBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentLoginBinding.inflate(inflater, container, false); } @Nullable @@ -58,41 +63,42 @@ protected LoginPresenter initPresenter() { @Override public void onAttach(Context context) { super.onAttach(context); - mActivity = (LoginActivity) context; + mActivity = (AuthActivity) context; } @Override protected void initView() { + piv_password.setOnPwdFocusChangedListener(new PasswordInputView.OnPwdFocusChangedListener() { + @Override + public void onFocusChanged(boolean focus) { + mActivity.doEyeAnim(focus); + } + }); } @Override protected void loadData() { + } + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mActivity.getSoftInputHelper().moveWith(sv_login, + piv_account.getEditText(), piv_password.getEditText()); } @Override public void onVisible(boolean isFirstVisible) { super.onVisible(isFirstVisible); - mKeyboardHelper = KeyboardHelper.attach(mActivity) - .init(mActivity.getRl_input(), sv_login, piv_account.getEditText(), piv_password.getEditText()) - .moveWithTranslation(); } @Override public void onInvisible() { super.onInvisible(); - if (mKeyboardHelper != null) { - mKeyboardHelper.detach(); - mKeyboardHelper = null; - } } @Override public void onDestroyView() { - if (mKeyboardHelper != null) { - mKeyboardHelper.detach(); - mKeyboardHelper = null; - } super.onDestroyView(); } @@ -111,18 +117,27 @@ protected boolean onClick1(View v) { mActivity.changeToRegister(); break; case R.id.sv_login: + InputMethodUtils.hide(sv_login); String userName = piv_account.getText(); String password = piv_password.getText(); - presenter.login(userName, password); + presenter.login(userName, password, false); break; } return true; } + public void loginByBiometric(String username, String password) { + presenter.login(username, password, true); + } + @Override - public void loginSuccess(int code, LoginBean data) { + public void loginSuccess(int code, UserEntity data, String username, String password, boolean isBiometric) { new LoginEvent(true).post(); - finish(); + if (isBiometric) { + finish(); + } else { + mActivity.tryOpenLoginByBiometric(username, password); + } } @Override diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/fragment/RegisterFragment.java b/app/src/main/java/per/goweii/wanandroid/module/login/fragment/RegisterFragment.java index 74052bc8..7f40abca 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/login/fragment/RegisterFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/login/fragment/RegisterFragment.java @@ -1,54 +1,61 @@ package per.goweii.wanandroid.module.login.fragment; import android.content.Context; -import android.support.annotation.Nullable; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.LinearLayout; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import butterknife.BindView; import butterknife.OnClick; import per.goweii.basic.core.base.BaseFragment; import per.goweii.basic.ui.toast.ToastMaker; +import per.goweii.basic.utils.InputMethodUtils; +import per.goweii.basic.utils.RegexUtils; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.FragmentRegisterBinding; import per.goweii.wanandroid.event.LoginEvent; -import per.goweii.wanandroid.module.login.activity.LoginActivity; -import per.goweii.wanandroid.module.login.model.LoginBean; +import per.goweii.wanandroid.module.login.activity.AuthActivity; +import per.goweii.wanandroid.module.login.model.UserEntity; import per.goweii.wanandroid.module.login.presenter.RegisterPresenter; import per.goweii.wanandroid.module.login.view.RegisterView; -import per.goweii.wanandroid.utils.KeyboardHelper; import per.goweii.wanandroid.widget.InputView; +import per.goweii.wanandroid.widget.PasswordInputView; import per.goweii.wanandroid.widget.SubmitView; /** * @author CuiZhen * @date 2019/5/16 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class RegisterFragment extends BaseFragment implements RegisterView { +public class RegisterFragment extends BaseFragment implements RegisterView { @BindView(R.id.ll_go_login) LinearLayout ll_go_login; @BindView(R.id.piv_register_account) InputView piv_account; @BindView(R.id.piv_register_password) - InputView piv_password; + PasswordInputView piv_password; @BindView(R.id.piv_register_password_again) - InputView piv_password_again; + PasswordInputView piv_password_again; @BindView(R.id.sv_register) SubmitView sv_register; - private KeyboardHelper mKeyboardHelper; - private LoginActivity mActivity; + private AuthActivity mActivity; - public static RegisterFragment create(){ + public static RegisterFragment create() { return new RegisterFragment(); } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_register; + protected FragmentRegisterBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentRegisterBinding.inflate(inflater, container, false); } @Nullable @@ -60,40 +67,51 @@ protected RegisterPresenter initPresenter() { @Override public void onAttach(Context context) { super.onAttach(context); - mActivity = (LoginActivity) context; + mActivity = (AuthActivity) context; } @Override protected void initView() { + piv_password.setOnPwdFocusChangedListener(new PasswordInputView.OnPwdFocusChangedListener() { + @Override + public void onFocusChanged(boolean focus) { + mActivity.doEyeAnim(focus); + } + }); + piv_password_again.setOnPwdFocusChangedListener(new PasswordInputView.OnPwdFocusChangedListener() { + @Override + public void onFocusChanged(boolean focus) { + mActivity.doEyeAnim(focus); + } + }); } @Override protected void loadData() { } + @Override + public void onActivityCreated(@Nullable Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + mActivity.getSoftInputHelper().moveWith( + sv_register, + piv_account.getEditText(), + piv_password.getEditText(), + piv_password_again.getEditText()); + } + @Override public void onVisible(boolean isFirstVisible) { super.onVisible(isFirstVisible); - mKeyboardHelper = KeyboardHelper.attach(mActivity) - .init(mActivity.getRl_input(), sv_register, piv_account.getEditText(), piv_password.getEditText(), piv_password_again.getEditText()) - .moveWithTranslation(); } @Override public void onInvisible() { super.onInvisible(); - if (mKeyboardHelper != null) { - mKeyboardHelper.detach(); - mKeyboardHelper = null; - } } @Override public void onDestroyView() { - if (mKeyboardHelper != null) { - mKeyboardHelper.detach(); - mKeyboardHelper = null; - } super.onDestroyView(); } @@ -112,19 +130,29 @@ protected boolean onClick1(View v) { mActivity.changeToLogin(); break; case R.id.sv_register: - String userName = piv_account.getText(); - String password = piv_password.getText(); - String repassword = piv_password_again.getText(); - presenter.register(userName, password, repassword); + InputMethodUtils.hide(sv_register); + register(); break; } return true; } + private void register() { + String password = piv_password.getText(); + String repassword = piv_password_again.getText(); + if (!TextUtils.equals(password, repassword)) { + ToastMaker.showShort(getString(R.string.make_sure_that_the_password_is_the_same_for_the_two_times)); + return; + } + String username = piv_account.getText(); + presenter.register(username, password, repassword); + } + @Override - public void registerSuccess(int code, LoginBean data) { + public void registerSuccess(int code, UserEntity data, String username, String password) { new LoginEvent(true).post(); - finish(); + mActivity.tryOpenLoginByBiometric(username, password); + //finish(); } @Override diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/model/LoginBean.java b/app/src/main/java/per/goweii/wanandroid/module/login/model/LoginBean.java index 5aca2965..a841530a 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/login/model/LoginBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/login/model/LoginBean.java @@ -7,8 +7,6 @@ /** * @author CuiZhen * @date 2019/5/8 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class LoginBean extends BaseBean { diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/model/LoginInfoEntity.java b/app/src/main/java/per/goweii/wanandroid/module/login/model/LoginInfoEntity.java new file mode 100644 index 00000000..84643b13 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/login/model/LoginInfoEntity.java @@ -0,0 +1,56 @@ +package per.goweii.wanandroid.module.login.model; + +import android.text.TextUtils; + +import org.json.JSONException; +import org.json.JSONObject; + +import per.goweii.basic.core.base.BaseEntity; + +public class LoginInfoEntity extends BaseEntity { + private final String username; + private final String password; + + public static LoginInfoEntity fromJson(String json) { + try { + JSONObject o = new JSONObject(json); + String username = o.getString("username"); + String password = o.getString("password"); + return new LoginInfoEntity(username, password); + } catch (JSONException e) { + return null; + } + } + + public LoginInfoEntity(String username, String password) { + this.username = username; + this.password = password; + } + + public boolean isEmpty() { + return TextUtils.isEmpty(username) || TextUtils.isEmpty(password); + } + + public boolean isNotEmpty() { + return !isEmpty(); + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String toJson() { + try { + JSONObject o = new JSONObject(); + o.put("username", username); + o.put("password", password); + return o.toString(); + } catch (JSONException e) { + return ""; + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/model/LoginRequest.java b/app/src/main/java/per/goweii/wanandroid/module/login/model/LoginRequest.java index 1b7952af..e48063b9 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/login/model/LoginRequest.java +++ b/app/src/main/java/per/goweii/wanandroid/module/login/model/LoginRequest.java @@ -1,6 +1,6 @@ package per.goweii.wanandroid.module.login.model; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import io.reactivex.disposables.Disposable; import per.goweii.wanandroid.http.BaseRequest; @@ -10,8 +10,6 @@ /** * @author CuiZhen * @date 2019/5/16 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class LoginRequest extends BaseRequest { diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/model/UserEntity.kt b/app/src/main/java/per/goweii/wanandroid/module/login/model/UserEntity.kt new file mode 100644 index 00000000..71eb1828 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/login/model/UserEntity.kt @@ -0,0 +1,16 @@ +package per.goweii.wanandroid.module.login.model + +/** + * @author CuiZhen + * @date 2019/5/8 + * GitHub: https://github.com/goweii + */ +data class UserEntity( + val email: String?, + val username: String?, + val wanid: Int, + val sex: Int, + val signature: String?, + val avatar: String?, + val cover: String? +) \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/presenter/AuthPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/login/presenter/AuthPresenter.java new file mode 100644 index 00000000..1527c8e1 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/login/presenter/AuthPresenter.java @@ -0,0 +1,7 @@ +package per.goweii.wanandroid.module.login.presenter; + +import per.goweii.basic.core.base.BasePresenter; +import per.goweii.wanandroid.module.login.view.AuthView; + +public class AuthPresenter extends BasePresenter { +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/presenter/LoginPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/login/presenter/LoginPresenter.java index 90557ebb..a7f875b3 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/login/presenter/LoginPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/login/presenter/LoginPresenter.java @@ -11,14 +11,12 @@ /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class LoginPresenter extends BasePresenter { - public void login(String userName, String password){ - addToRxLife(LoginRequest.login(userName, password, new RequestListener() { + public void login(String username, String password, boolean isBiometric) { + addToRxLife(LoginRequest.login(username, password, new RequestListener() { @Override public void onStart() { showLoadingDialog(); @@ -28,15 +26,18 @@ public void onStart() { public void onSuccess(int code, LoginBean data) { UserUtils.getInstance().login(data); if (isAttach()) { - getBaseView().loginSuccess(code, data); + getBaseView().loginSuccess(0, UserUtils.getInstance().getLoginUser(), username, password, isBiometric); } + dismissLoadingDialog(); } @Override public void onFailed(int code, String msg) { + UserUtils.getInstance().logout(); if (isAttach()) { getBaseView().loginFailed(code, msg); } + dismissLoadingDialog(); } @Override @@ -46,7 +47,6 @@ public void onError(ExceptionHandle handle) { @Override public void onFinish() { - dismissLoadingDialog(); } })); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/presenter/QuickLoginPresenter.kt b/app/src/main/java/per/goweii/wanandroid/module/login/presenter/QuickLoginPresenter.kt new file mode 100644 index 00000000..ac53c2a3 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/login/presenter/QuickLoginPresenter.kt @@ -0,0 +1,88 @@ +package per.goweii.wanandroid.module.login.presenter + +import per.goweii.basic.core.base.BasePresenter +import per.goweii.basic.utils.Base64Utils.decodeToBytes +import per.goweii.basic.utils.Base64Utils.decodeToString +import per.goweii.basic.utils.Base64Utils.encodeToBytes +import per.goweii.basic.utils.Base64Utils.encodeToString +import per.goweii.basic.utils.SPUtils +import per.goweii.rxhttp.request.exception.ExceptionHandle +import per.goweii.wanandroid.http.RequestListener +import per.goweii.wanandroid.module.login.model.LoginBean +import per.goweii.wanandroid.module.login.model.LoginInfoEntity +import per.goweii.wanandroid.module.login.model.LoginRequest +import per.goweii.wanandroid.module.login.view.QuickLoginView +import per.goweii.wanandroid.utils.UserUtils +import javax.crypto.Cipher + +class QuickLoginPresenter : BasePresenter() { + private val spUtils = SPUtils.newInstance("authInfo") + + val loginInfo: String + get() = spUtils.get("loginInfo", "") + val cipherIV: String + get() = spUtils.get("cipherIV", "") + + fun encodeAndSaveLoginInfo(cipher: Cipher, loginInfoEntity: LoginInfoEntity?): Boolean { + return if (loginInfoEntity == null || loginInfoEntity.isEmpty) { + false + } else try { + val loginInfoStr = loginInfoEntity.toJson() + val loginInfoBytes = encodeToBytes(loginInfoStr) + val loginInfoEncodeBytes = cipher.doFinal(loginInfoBytes) + val loginInfoEncodeStr = encodeToString(loginInfoEncodeBytes) + val ivBytes = cipher.iv + val ivStr = encodeToString(ivBytes) + spUtils.save(SP_KEY_LOGIN_INFO, loginInfoEncodeStr) + spUtils.save(SP_KEY_CIPHER_IV, ivStr) + true + } catch (e: Exception) { + e.printStackTrace() + false + } + } + + fun decodeLoginInfo(cipher: Cipher, loginInfoStr: String?): LoginInfoEntity? { + return try { + val loginInfoBytes = decodeToBytes(loginInfoStr) + val loginInfoDecodedBytes = cipher.doFinal(loginInfoBytes) + val loginInfoJson = decodeToString(loginInfoDecodedBytes) + LoginInfoEntity.fromJson(loginInfoJson) + } catch (e: Exception) { + e.printStackTrace() + null + } + } + + fun login(username: String, password: String) { + addToRxLife(LoginRequest.login(username, password, object : RequestListener { + override fun onStart() { + showLoadingDialog() + } + + override fun onSuccess(code: Int, data: LoginBean) { + UserUtils.getInstance().login(data) + if (isAttach) { + baseView.loginSuccess(0, UserUtils.getInstance().loginUser) + } + } + + override fun onFailed(code: Int, msg: String) { + UserUtils.getInstance().logout() + if (isAttach) { + baseView.loginFailed(code, msg) + } + } + + override fun onError(handle: ExceptionHandle) {} + override fun onFinish() { + dismissLoadingDialog() + } + })) + } + + companion object { + private const val SP_KEY_LOGIN_INFO = "loginInfo" + private const val SP_KEY_CIPHER_IV = "cipherIV" + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/presenter/RegisterPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/login/presenter/RegisterPresenter.java index 8b5cbc68..45e413aa 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/login/presenter/RegisterPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/login/presenter/RegisterPresenter.java @@ -11,14 +11,16 @@ /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class RegisterPresenter extends BasePresenter { - public void register(String userName, String password, String repassword){ - addToRxLife(LoginRequest.register(userName, password, repassword, new RequestListener() { + public void register( + String username, + String password, + String repassword + ) { + addToRxLife(LoginRequest.register(username, password, repassword, new RequestListener() { @Override public void onStart() { showLoadingDialog(); @@ -28,15 +30,18 @@ public void onStart() { public void onSuccess(int code, LoginBean data) { UserUtils.getInstance().login(data); if (isAttach()) { - getBaseView().registerSuccess(code, data); + getBaseView().registerSuccess(0, UserUtils.getInstance().getLoginUser(), username, password); } + dismissLoadingDialog(); } @Override public void onFailed(int code, String msg) { + UserUtils.getInstance().logout(); if (isAttach()) { getBaseView().registerFailed(code, msg); } + dismissLoadingDialog(); } @Override @@ -46,7 +51,6 @@ public void onError(ExceptionHandle handle) { @Override public void onFinish() { - dismissLoadingDialog(); } })); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/view/AuthView.java b/app/src/main/java/per/goweii/wanandroid/module/login/view/AuthView.java new file mode 100644 index 00000000..62e4500c --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/login/view/AuthView.java @@ -0,0 +1,11 @@ +package per.goweii.wanandroid.module.login.view; + +import per.goweii.basic.core.base.BaseView; + +/** + * @author CuiZhen + * @date 2019/5/15 + * GitHub: https://github.com/goweii + */ +public interface AuthView extends BaseView { +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/view/LoginView.java b/app/src/main/java/per/goweii/wanandroid/module/login/view/LoginView.java index 5a1ecc44..3f6cec8b 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/login/view/LoginView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/login/view/LoginView.java @@ -1,16 +1,15 @@ package per.goweii.wanandroid.module.login.view; import per.goweii.basic.core.base.BaseView; -import per.goweii.wanandroid.module.login.model.LoginBean; +import per.goweii.wanandroid.module.login.model.UserEntity; /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface LoginView extends BaseView { - void loginSuccess(int code, LoginBean data); + void loginSuccess(int code, UserEntity data, String username, String password, boolean isBiometric); + void loginFailed(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/view/QuickLoginView.kt b/app/src/main/java/per/goweii/wanandroid/module/login/view/QuickLoginView.kt new file mode 100644 index 00000000..e42bd009 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/login/view/QuickLoginView.kt @@ -0,0 +1,9 @@ +package per.goweii.wanandroid.module.login.view + +import per.goweii.basic.core.base.BaseView +import per.goweii.wanandroid.module.login.model.UserEntity + +interface QuickLoginView : BaseView { + fun loginSuccess(code: Int, data: UserEntity) + fun loginFailed(code: Int, msg: String?) +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/login/view/RegisterView.java b/app/src/main/java/per/goweii/wanandroid/module/login/view/RegisterView.java index 56361a12..f08dc7ff 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/login/view/RegisterView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/login/view/RegisterView.java @@ -1,16 +1,15 @@ package per.goweii.wanandroid.module.login.view; import per.goweii.basic.core.base.BaseView; -import per.goweii.wanandroid.module.login.model.LoginBean; +import per.goweii.wanandroid.module.login.model.UserEntity; /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface RegisterView extends BaseView { - void registerSuccess(int code, LoginBean data); + void registerSuccess(int code, UserEntity data, String username, String password); + void registerFailed(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/ArticleActivity.kt b/app/src/main/java/per/goweii/wanandroid/module/main/activity/ArticleActivity.kt new file mode 100644 index 00000000..138371c0 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/activity/ArticleActivity.kt @@ -0,0 +1,810 @@ +package per.goweii.wanandroid.module.main.activity + +import android.animation.Animator +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.graphics.Rect +import android.net.Uri +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import android.view.animation.DecelerateInterpolator +import android.widget.TextView +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.doOnLayout +import androidx.core.view.doOnPreDraw +import androidx.core.view.isVisible +import androidx.core.view.updatePadding +import com.google.android.gms.ads.AdSize +import per.goweii.anylayer.Layer +import per.goweii.anylayer.guide.GuideLayer +import per.goweii.basic.core.base.BaseActivity +import per.goweii.basic.ui.toast.ToastMaker +import per.goweii.basic.utils.ResUtils +import per.goweii.basic.utils.ext.invisible +import per.goweii.basic.utils.ext.visible +import per.goweii.component.ad.BannerAdProvider +import per.goweii.statusbarcompat.StatusBarCompat +import per.goweii.swipeback.SwipeBackAbility +import per.goweii.swipeback.SwipeBackDirection +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.ActivityArticleBinding +import per.goweii.wanandroid.module.main.dialog.ArticleShareDialog +import per.goweii.wanandroid.module.main.presenter.ArticlePresenter +import per.goweii.wanandroid.module.main.utils.FloatIconTouchListener +import per.goweii.wanandroid.module.main.view.ArticleView +import per.goweii.wanandroid.utils.DarkModeUtils +import per.goweii.wanandroid.utils.GoogleAdUnitIds +import per.goweii.wanandroid.utils.GuideSPUtils +import per.goweii.wanandroid.utils.ImageLoader +import per.goweii.wanandroid.utils.RecommendManager +import per.goweii.wanandroid.utils.UrlOpenUtils +import per.goweii.wanandroid.utils.cdkey.CDKeyUtils +import per.goweii.wanandroid.utils.router.Router +import per.goweii.wanandroid.utils.tts.TtsManager +import per.goweii.wanandroid.utils.tts.TtsSource +import per.goweii.wanandroid.utils.web.WebHolder +import per.goweii.wanandroid.utils.web.WebHolder.with +import per.goweii.wanandroid.utils.web.cache.ReadingModeManager +import per.goweii.wanandroid.utils.web.interceptor.WebReadingModeInterceptor +import per.goweii.wanandroid.utils.web.interceptor.WebResUrlInterceptor + +/** + * @author CuiZhen + * @date 2020/2/20 + */ +class ArticleActivity : BaseActivity(), ArticleView, + SwipeBackAbility.Direction, SwipeBackAbility.ForceEdge { + private data class FloatIcon( + val container: View, + val shadow: View, + val icon: View, + val tip: View, + var tipAnim: Animator? = null + ) + + companion object { + fun start( + context: Context, url: String, title: String, + articleId: Int, collected: Boolean, + userName: String, userId: Int + ) { + context.startActivity(Intent(context, ArticleActivity::class.java).apply { + putExtra("url", url) + putExtra("title", title) + putExtra("articleId", articleId) + putExtra("collected", collected) + putExtra("user_name", userName) + putExtra("user_id", userId) + }) + } + } + + private lateinit var mWebHolder: WebHolder + private var lastUrlLoadTime = 0L + private var userTouched = false + private var isPageLoadFinished = false + + private var floatIconsVisible = false + private var floatIconsAnim: AnimatorSet? = null + private val floatIcons: List by lazy { + mutableListOf().apply { + add( + FloatIcon( + binding.activityArticleFloatBtn.rlIconCollect, + binding.activityArticleFloatBtn.slCollect, + binding.activityArticleFloatBtn.cvCollect, + binding.activityArticleFloatBtn.tvCollectTip + ) + ) + add( + FloatIcon( + binding.activityArticleFloatBtn.rlIconReadLater, + binding.activityArticleFloatBtn.slReadLater, + binding.activityArticleFloatBtn.aivReadLater, + binding.activityArticleFloatBtn.tvReadLaterTip + ) + ) + add( + FloatIcon( + binding.activityArticleFloatBtn.rlIconListen, + binding.activityArticleFloatBtn.slListen, + binding.activityArticleFloatBtn.aivListen, + binding.activityArticleFloatBtn.tvListenTip + ) + ) + add( + FloatIcon( + binding.activityArticleFloatBtn.rlIconOpen, + binding.activityArticleFloatBtn.slOpen, + binding.activityArticleFloatBtn.aivOpen, + binding.activityArticleFloatBtn.tvOpenTip + ) + ) + add( + FloatIcon( + binding.activityArticleFloatBtn.rlIconShare, + binding.activityArticleFloatBtn.slShare, + binding.activityArticleFloatBtn.aivShare, + binding.activityArticleFloatBtn.tvShareTip + ) + ) + } + } + + override fun initViewBinding(inflater: LayoutInflater): ActivityArticleBinding { + return ActivityArticleBinding.inflate(inflater) + } + + override fun initPresenter(): ArticlePresenter = ArticlePresenter() + + override fun initView() { + StatusBarCompat.setIconMode(this, !DarkModeUtils.isDarkMode(this)) + intent?.let { + presenter.articleUrl = it.getStringExtra("url") ?: "" + presenter.articleTitle = it.getStringExtra("title") ?: "" + presenter.articleId = it.getIntExtra("articleId", 0) + presenter.collected = it.getBooleanExtra("collected", false) + presenter.userName = it.getStringExtra("user_name") ?: "" + presenter.userId = it.getIntExtra("user_id", 0) + } + + ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, insets -> + val b = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom + binding.root.updatePadding(bottom = b) + insets + } + + switchCollectView(false) + val icons = mutableListOf() + floatIcons.forEach { + icons.add(FloatIconTouchListener.Icon(it.icon)) + } + binding.activityArticleFloatBtn.vBack.setOnTouchListener( + FloatIconTouchListener( + icons, + object : FloatIconTouchListener.OnFloatTouchedListener { + override fun onTouched(v: View?) { + var floatIcon: FloatIcon? = null + floatIcons.forEach { + if (it.icon == v) { + floatIcon = it + } + } + doFloatTipAnim(floatIcon) + } + }) + ) + binding.activityArticleFloatBtn.vBack.setOnClickListener { + if (floatIconsVisible) toggleFloatIcons() + else finish() + } + binding.activityArticleFloatBtn.vBack.setOnLongClickListener { + toggleFloatIcons() + return@setOnLongClickListener true + } + binding.activityArticleFloatBtn.aivShare.setOnClickListener { + shareQrcode() + if (floatIconsVisible) toggleFloatIcons() + } + binding.activityArticleFloatBtn.aivReadLater.setOnClickListener { + presenter.isReadLater { isReadLater -> + if (isReadLater) { + presenter.removeReadLater() + } else { + presenter.addReadLater() + } + } + if (floatIconsVisible) toggleFloatIcons() + } + binding.activityArticleFloatBtn.aivOpen.setOnClickListener { + UrlOpenUtils.with(presenter.articleUrl) + .title(presenter.articleTitle) + .articleId(presenter.articleId) + .collected(presenter.collected) + .collectId(presenter.collectId) + .author(presenter.userName) + .userId(presenter.userId) + .forceWeb() + .open(context) + if (floatIconsVisible) toggleFloatIcons() + } + binding.activityArticleFloatBtn.cvCollect.setOnClickListener { + if (binding.activityArticleFloatBtn.cvCollect.isChecked) { + presenter.collect() + } else { + presenter.uncollect() + } + if (floatIconsVisible) toggleFloatIcons() + } + binding.activityArticleFloatBtn.aivListen.setOnClickListener { + startListen() + if (floatIconsVisible) toggleFloatIcons() + } + binding.wc.setOnTouchDownListener { + if (floatIconsVisible) toggleFloatIcons() + } + mWebHolder = with(this, binding.wc, binding.activityArticleFloatBtn.pb) + .setLoadCacheElseNetwork(true) + .setUseInstanceCache(true) + .setAllowOpenOtherApp(false) + .setAllowOpenDownload(false) + .setAllowRedirect(false) + .setOverrideUrlInterceptor { + if (!isPageLoadFinished) return@setOverrideUrlInterceptor false + if (!userTouched) return@setOverrideUrlInterceptor false + val currUrlLoadTime = System.currentTimeMillis() + val intercept = if (currUrlLoadTime - lastUrlLoadTime > 1000L) { + UrlOpenUtils.with(it).open(context) + true + } else { + false + } + lastUrlLoadTime = currUrlLoadTime + return@setOverrideUrlInterceptor intercept + } + .setOnPageLoadCallback(object : WebHolder.OnPageLoadCallback { + override fun onPageStarted() { + presenter.addReadRecord(mWebHolder.url, mWebHolder.title, mWebHolder.percent) + } + + override fun onPageFinished() { + isPageLoadFinished = true + + presenter.updateReadRecordTitle(mWebHolder.url, mWebHolder.title) + + showArticleFooter() + + val uri = Uri.parse(mWebHolder.url) + val message = uri.getQueryParameter("scrollToKeywords") + if (!message.isNullOrEmpty()) { + mWebHolder.scrollToKeywords(message.split(",")) + } + } + }) + .setInterceptUrlInterceptor { uri, reqHeaders, reqMethod -> + val pageUri = Uri.parse(presenter.articleUrl) + ReadingModeManager.getUrlRegexBeanForHost(pageUri.host) + ?: return@setInterceptUrlInterceptor null + WebReadingModeInterceptor.intercept( + pageUri, + uri, + mWebHolder.userAgent, + reqHeaders, + reqMethod + )?.let { + return@setInterceptUrlInterceptor it + } + WebResUrlInterceptor.intercept( + pageUri, + uri, + mWebHolder.userAgent, + reqHeaders, + reqMethod + )?.let { + return@setInterceptUrlInterceptor it + } + return@setInterceptUrlInterceptor null + } + .setOnPageTitleCallback { + presenter.updateReadRecordTitle(mWebHolder.url, it) + } + .setOnPageScrollEndListener { + presenter.isReadLater { isReadLater -> + if (isReadLater) { + presenter.removeReadLater() + } + } + } + .setOnPageScrollChangeListener { + presenter.updateReadRecordPercent(mWebHolder.url, it) + } + binding.wc.setOnDoubleClickListener { _, _ -> + changeRevealLayoutCenterXY( + binding.activityArticleFloatBtn.rl.width * 0.5F, + binding.activityArticleFloatBtn.rl.height * 0.5F + ) + presenter.collect() + } + + window.decorView.doOnLayout { + showGuideDialogIfNeeded() + } + } + + private fun showArticleFooter() { + RecommendManager.getInstance().getBean { + it?.articleFooter?.let { articleFooter -> + binding.llFooter.isVisible = true + + binding.tvFooterTitle.isVisible = !articleFooter.title.isNullOrBlank() + binding.tvFooterTitle.text = articleFooter.title + + binding.ivFooterImage.isVisible = !articleFooter.url.isNullOrBlank() + if (!articleFooter.url.isNullOrEmpty()) { + ImageLoader.image(binding.ivFooterImage, articleFooter.url) + binding.ivFooterImage.setOnClickListener { + Router.routeTo(articleFooter.route) + } + } + } + } + } + + private fun shareQrcode() { + mWebHolder.getShareInfo { url, covers, title, desc -> + ArticleShareDialog(this, covers, title, desc, url).show() + } + } + + override fun loadData() { + lastUrlLoadTime = System.currentTimeMillis() + mWebHolder.loadUrl(presenter.articleUrl) + presenter.isReadLater { switchReadLaterIcon() } + + if (!CDKeyUtils.getInstance().isActive) { + binding.flAdContainer.isVisible = false + binding.llAd.doOnPreDraw { + val width = (binding.llAd.width / resources.displayMetrics.density).toInt() + BannerAdProvider( + this, this, + adUnitId = GoogleAdUnitIds.ARTICLE_DETAILS_BOTTOM_BANNER_AD, + adSize = AdSize.getCurrentOrientationInlineAdaptiveBannerAdSize(this, width), + ).apply { + onAdLoaded = { + binding.flAdContainer.isVisible = true + } + }.attachToContainer(binding.flAdContainer) + } + } + } + + override fun onPause() { + mWebHolder.onPause() + super.onPause() + } + + override fun onResume() { + mWebHolder.onResume() + super.onResume() + } + + override fun onDestroy() { + WebResUrlInterceptor.cancel() + WebReadingModeInterceptor.cancel() + floatIconsAnim?.cancel() + floatIcons.forEach { + it.tipAnim?.cancel() + } + mWebHolder.onDestroy(false) + super.onDestroy() + } + + private fun doFloatTipAnim(floatIconOnTouched: FloatIcon?) { + floatIcons.forEach { floatIcon -> + floatIcon.tipAnim?.cancel() + if (floatIconOnTouched == floatIcon) { + floatIcon.tipAnim = AnimatorSet().apply { + duration = 200L + interpolator = DecelerateInterpolator() + val fromX = if (floatIcon.tip.translationX == 0F) { + -floatIcon.icon.width.toFloat() + } else { + floatIcon.tip.translationX + } + val fromA = if (floatIcon.tip.alpha == 1F) { + 0F + } else { + floatIcon.tip.alpha + } + playTogether( + ObjectAnimator.ofFloat( + floatIcon.tip, "translationX", + fromX, 0F + ), + ObjectAnimator.ofFloat( + floatIcon.tip, "alpha", + fromA, 1F + ) + ) + addListener(object : Animator.AnimatorListener { + override fun onAnimationStart(animation: Animator) { + floatIcon.tip.visible() + } + + override fun onAnimationRepeat(animation: Animator) { + } + + override fun onAnimationEnd(animation: Animator) { + } + + override fun onAnimationCancel(animation: Animator) { + } + }) + } + } else { + floatIcon.tipAnim = AnimatorSet().apply { + duration = 200L + interpolator = DecelerateInterpolator() + val fromX = floatIcon.tip.translationX + val fromA = floatIcon.tip.alpha + playTogether( + ObjectAnimator.ofFloat( + floatIcon.tip, "translationX", + fromX, -floatIcon.icon.width.toFloat() + ), + ObjectAnimator.ofFloat( + floatIcon.tip, "alpha", + fromA, 0F + ) + ) + addListener(object : Animator.AnimatorListener { + private var endByCancel = false + + override fun onAnimationStart(animation: Animator) { + floatIcon.tip.visible() + } + + override fun onAnimationRepeat(animation: Animator) { + } + + override fun onAnimationEnd(animation: Animator) { + if (endByCancel) return + if (floatIcon.tip.translationX != 0F) { + floatIcon.tip.invisible() + } + } + + override fun onAnimationCancel(animation: Animator) { + endByCancel = true + } + }) + } + } + floatIcon.tipAnim?.start() + } + } + + private fun toggleFloatIcons() { + floatIconsVisible = !floatIconsVisible + if (!floatIconsVisible) { + doFloatTipAnim(null) + } + floatIconsAnim?.cancel() + floatIconsAnim = AnimatorSet().apply { + val anims = mutableListOf() + anims.add( + ObjectAnimator.ofFloat( + binding.activityArticleFloatBtn.flBack, + "rotation", + binding.activityArticleFloatBtn.flBack.rotation, + if (floatIconsVisible) 360F else 0F + ).apply { + duration = 300L + addUpdateListener { + if (it.animatedFraction > 0.5F) { + if (floatIconsVisible) binding.activityArticleFloatBtn.ivClose.visible() + else binding.activityArticleFloatBtn.ivClose.invisible() + } + } + }) + floatIcons.filterIndexed { index, floatIconModel -> + anims.add(AnimatorSet().apply { + duration = 300L + val isBegin = floatIconModel.container.translationY == 0F + playTogether( + ObjectAnimator.ofFloat( + floatIconModel.container, "translationY", + if (isBegin) 0F else floatIconModel.container.translationY, + if (floatIconsVisible) -floatIconModel.container.height.toFloat() * (index + 1) else 0F + ), + ObjectAnimator.ofFloat( + floatIconModel.shadow, "alpha", + if (isBegin) 0F else floatIconModel.shadow.alpha, + if (floatIconsVisible) 1F else 0F + ) + ) + }) + } + playTogether(anims) + interpolator = DecelerateInterpolator() + } + floatIconsAnim?.apply { + addListener(object : Animator.AnimatorListener { + override fun onAnimationStart(animation: Animator) { + floatIcons.forEach { it.container.visible() } + } + + override fun onAnimationRepeat(animation: Animator) { + } + + override fun onAnimationEnd(animation: Animator) { + floatIcons.forEach { + if (it.container.translationY == 0F) it.container.invisible() + } + } + + override fun onAnimationCancel(animation: Animator) { + } + }) + }?.start() + } + + @SwipeBackDirection + override fun swipeBackDirection(): Int { + return SwipeBackDirection.RIGHT + } + + override fun swipeBackForceEdge(): Boolean { + return true + } + + override fun dispatchTouchEvent(ev: MotionEvent?): Boolean { + userTouched = true + return super.dispatchTouchEvent(ev) + } + + private fun changeRevealLayoutCenterXY(x: Float, y: Float) { + binding.activityArticleFloatBtn.rl.setCenter(x, y) + binding.activityArticleFloatBtn.cvCollect.setCenter(x, y) + } + + private fun switchCollectView(anim: Boolean = true) { + binding.activityArticleFloatBtn.rl.setChecked(presenter.collected, anim) + binding.activityArticleFloatBtn.cvCollect.setChecked(presenter.collected, anim) + } + + override fun collectSuccess() { + switchCollectView() + } + + override fun collectFailed(msg: String) { + switchCollectView() + ToastMaker.showShort(msg) + } + + override fun uncollectSuccess() { + switchCollectView() + } + + override fun uncollectFailed(msg: String) { + switchCollectView() + ToastMaker.showShort(msg) + } + + override fun addReadLaterSuccess() { + switchReadLaterIcon() + ToastMaker.showShort(getString(R.string.added_to_my_bookmarks)) + } + + override fun addReadLaterFailed() { + switchReadLaterIcon() + ToastMaker.showShort(getString(R.string.failed_to_add_my_bookmarks)) + } + + override fun removeReadLaterSuccess() { + switchReadLaterIcon() + ToastMaker.showShort(getString(R.string.moved_out_of_my_bookmarks)) + } + + override fun removeReadLaterFailed() { + switchReadLaterIcon() + ToastMaker.showShort(getString(R.string.failed_to_move_out_of_my_bookmarks)) + } + + private fun switchReadLaterIcon() { + if (presenter.readLater) { + binding.activityArticleFloatBtn.aivReadLater.setImageResource(R.drawable.ic_read_later_added) + binding.activityArticleFloatBtn.aivReadLater.setColorFilter( + ResUtils.getThemeColor( + binding.activityArticleFloatBtn.aivReadLater, + R.attr.colorIconMain + ) + ) + } else { + binding.activityArticleFloatBtn.aivReadLater.setImageResource(R.drawable.ic_read_later) + binding.activityArticleFloatBtn.aivReadLater.setColorFilter( + ResUtils.getThemeColor( + binding.activityArticleFloatBtn.aivReadLater, + R.attr.colorIconSurface + ) + ) + } + } + + private fun showGuideDialogIfNeeded() { + if (GuideSPUtils.getInstance().isArticleGuideShown) { + return + } + window?.decorView?.post { + showGuideBackBtnDialog { + showGuideDoubleTapDialog { + showGuidePreviewImageDialog { + GuideSPUtils.getInstance().setArticleGuideShown() + } + } + } + } + } + + private fun showGuideBackBtnDialog(onDismiss: () -> Unit) { + GuideLayer(this@ArticleActivity) + .setBackgroundColorInt( + ResUtils.getThemeColor( + binding.activityArticleFloatBtn.aivReadLater, + R.attr.colorDialogBg + ) + ) + .addMapping(GuideLayer.Mapping().apply { + setTargetView(binding.activityArticleFloatBtn.ivClose) + cornerRadius = 9999F + guideView = LayoutInflater.from(this@ArticleActivity) + .inflate(R.layout.dialog_guide_tip, null, false).apply { + findViewById(R.id.dialog_guide_tv_tip).apply { + text = + getString(R.string.long_press_the_back_button_to_have_more_shortcut_menus) + } + } + marginLeft = ResUtils.getDimens(R.dimen.margin_def).toInt() + setHorizontalAlign(GuideLayer.Align.Horizontal.TO_RIGHT) + setVerticalAlign(GuideLayer.Align.Vertical.CENTER) + }) + .addMapping(GuideLayer.Mapping().apply { + val cx = window?.decorView?.width ?: 0 / 2 + val cy = window?.decorView?.height ?: 0 / 2 + targetRect = Rect(cx, cy, cx, cy) + guideView = LayoutInflater.from(this@ArticleActivity) + .inflate(R.layout.dialog_guide_btn, null, false).apply { + findViewById(R.id.dialog_guide_tv_btn).apply { + text = getString(R.string.next) + } + } + marginBottom = ResUtils.getDimens(R.dimen.margin_big).toInt() + setHorizontalAlign(GuideLayer.Align.Horizontal.CENTER) + setVerticalAlign(GuideLayer.Align.Vertical.CENTER) + addOnClickListener(Layer.OnClickListener { layer, _ -> + layer.dismiss() + }, R.id.dialog_guide_tv_btn) + }) + .addOnVisibleChangeListener(object : Layer.OnVisibleChangedListener { + override fun onShow(layer: Layer) { + } + + override fun onDismiss(layer: Layer) { + onDismiss.invoke() + } + }) + .show() + } + + @SuppressLint("InflateParams") + private fun showGuideDoubleTapDialog(onDismiss: () -> Unit) { + GuideLayer(this@ArticleActivity) + .setBackgroundColorInt( + ResUtils.getThemeColor( + binding.activityArticleFloatBtn.aivReadLater, + R.attr.colorDialogBg + ) + ) + .addMapping(GuideLayer.Mapping().apply { + val cx = window?.decorView?.width ?: 0 / 2 + val cy = window?.decorView?.height ?: 0 / 2 + targetRect = Rect(cx, cy, cx, cy) + guideView = LayoutInflater.from(this@ArticleActivity) + .inflate(R.layout.dialog_guide_tip, null, false).apply { + findViewById(R.id.dialog_guide_tv_tip).apply { + text = getString(R.string.double_click_anywhere_to_quickly_bookmark) + } + } + horizontalAlign = GuideLayer.Align.Horizontal.CENTER + verticalAlign = GuideLayer.Align.Vertical.CENTER + }) + .addMapping(GuideLayer.Mapping().apply { + val cx = window?.decorView?.width ?: 0 / 2 + val cy = window?.decorView?.height ?: 0 / 2 + targetRect = Rect(cx, cy, cx, cy) + guideView = LayoutInflater.from(this@ArticleActivity) + .inflate(R.layout.dialog_guide_btn, null, false).apply { + findViewById(R.id.dialog_guide_tv_btn).apply { + text = getString(R.string.next) + } + } + horizontalAlign = GuideLayer.Align.Horizontal.CENTER + verticalAlign = GuideLayer.Align.Vertical.ALIGN_PARENT_BOTTOM + marginBottom = ResUtils.getDimens(R.dimen.margin_big).toInt() + addOnClickListener(Layer.OnClickListener { layer, _ -> + layer.dismiss() + }, R.id.dialog_guide_tv_btn) + }) + .addOnVisibleChangeListener(object : Layer.OnVisibleChangedListener { + override fun onShow(layer: Layer) { + } + + override fun onDismiss(layer: Layer) { + onDismiss.invoke() + } + }) + .show() + } + + private fun showGuidePreviewImageDialog(onDismiss: () -> Unit) { + GuideLayer(this@ArticleActivity) + .setBackgroundColorInt( + ResUtils.getThemeColor( + binding.activityArticleFloatBtn.aivReadLater, + R.attr.colorDialogBg + ) + ) + .addMapping(GuideLayer.Mapping().apply { + val cx = window?.decorView?.width ?: 0 / 2 + val cy = window?.decorView?.height ?: 0 / 2 + targetRect = Rect(cx, cy, cx, cy) + guideView = LayoutInflater.from(this@ArticleActivity) + .inflate(R.layout.dialog_guide_tip, null, false).apply { + findViewById(R.id.dialog_guide_tv_tip).apply { + text = + getString(R.string.long_press_the_web_image_to_preview_a_larger_image) + } + } + horizontalAlign = GuideLayer.Align.Horizontal.CENTER + verticalAlign = GuideLayer.Align.Vertical.CENTER + }) + .addMapping(GuideLayer.Mapping().apply { + val cx = window?.decorView?.width ?: 0 / 2 + val cy = window?.decorView?.height ?: 0 / 2 + targetRect = Rect(cx, cy, cx, cy) + guideView = LayoutInflater.from(this@ArticleActivity) + .inflate(R.layout.dialog_guide_btn, null, false).apply { + findViewById(R.id.dialog_guide_tv_btn).apply { + text = getString(R.string.i_know) + } + } + horizontalAlign = GuideLayer.Align.Horizontal.CENTER + verticalAlign = GuideLayer.Align.Vertical.ALIGN_PARENT_BOTTOM + marginBottom = ResUtils.getDimens(R.dimen.margin_big).toInt() + addOnClickListener(Layer.OnClickListener { layer, _ -> + layer.dismiss() + }, R.id.dialog_guide_tv_btn) + }) + .addOnVisibleChangeListener(object : Layer.OnVisibleChangedListener { + override fun onShow(layer: Layer) { + } + + override fun onDismiss(layer: Layer) { + onDismiss.invoke() + } + }) + .show() + } + + private fun startListen() { + mWebHolder.getHtml { url, html -> + if (mWebHolder.url == url) { + if (isFinishing || isDestroyed) { + return@getHtml + } + mWebHolder.getShareInfo { _, covers, title, _ -> + if (isFinishing || isDestroyed) { + return@getShareInfo + } + val source = TtsSource( + url = url, + cover = covers.firstOrNull()?.takeIf { it.isNotEmpty() }, + title = title.takeIf { it.isNotEmpty() }, + html = html, + ) + val ttsm = TtsManager.getInstance(this) + if (!ttsm.isSpeaking) { + ttsm.playSource(source) + } else { + ttsm.addSource(source) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/ArticleListActivity.kt b/app/src/main/java/per/goweii/wanandroid/module/main/activity/ArticleListActivity.kt new file mode 100644 index 00000000..e5f4109a --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/activity/ArticleListActivity.kt @@ -0,0 +1,35 @@ +package per.goweii.wanandroid.module.main.activity + +import android.view.LayoutInflater +import per.goweii.basic.core.base.BaseActivity +import per.goweii.basic.core.base.BasePresenter +import per.goweii.basic.core.base.BaseView +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.ActivityArticleListBinding +import per.goweii.wanandroid.module.main.model.ChapterBean + +/** + * @author CuiZhen + * @date 2020/3/22 + */ +class ArticleListActivity : BaseActivity, ActivityArticleListBinding>() { + + companion object { + @JvmStatic + fun start(chapterBean: ChapterBean) { + + } + } + + override fun initViewBinding(inflater: LayoutInflater): ActivityArticleListBinding { + return ActivityArticleListBinding.inflate(inflater) + } + + override fun initPresenter(): BasePresenter? = null + + override fun initView() { + } + + override fun loadData() { + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/BringToFrontActivity.java b/app/src/main/java/per/goweii/wanandroid/module/main/activity/BringToFrontActivity.java new file mode 100644 index 00000000..d2a0c088 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/activity/BringToFrontActivity.java @@ -0,0 +1,40 @@ +package per.goweii.wanandroid.module.main.activity; + +import android.content.Context; +import android.content.Intent; +import android.view.LayoutInflater; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewbinding.ViewBinding; + +import per.goweii.basic.core.base.BaseActivity; +import per.goweii.basic.core.mvp.MvpPresenter; + +public class BringToFrontActivity extends BaseActivity { + public static void start(Context context) { + Intent intent = new Intent(context, BringToFrontActivity.class); + context.startActivity(intent); + } + + @Nullable + @Override + protected ViewBinding initViewBinding(@NonNull LayoutInflater inflater) { + return null; + } + + @Nullable + @Override + protected MvpPresenter initPresenter() { + return null; + } + + @Override + protected void initView() { + finish(); + } + + @Override + protected void loadData() { + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/CrashActivity.java b/app/src/main/java/per/goweii/wanandroid/module/main/activity/CrashActivity.java deleted file mode 100644 index eb7e8621..00000000 --- a/app/src/main/java/per/goweii/wanandroid/module/main/activity/CrashActivity.java +++ /dev/null @@ -1,82 +0,0 @@ -package per.goweii.wanandroid.module.main.activity; - -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.widget.ImageView; -import android.widget.ScrollView; -import android.widget.TextView; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; -import butterknife.Unbinder; -import cat.ereza.customactivityoncrash.CustomActivityOnCrash; -import cat.ereza.customactivityoncrash.config.CaocConfig; -import per.goweii.statusbarcompat.StatusBarCompat; -import per.goweii.wanandroid.R; -import per.goweii.wanandroid.common.WanApp; - -/** - * @author CuiZhen - * @date 2019/10/20 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public class CrashActivity extends AppCompatActivity { - - @BindView(R.id.tv_log) - TextView tv_log; - @BindView(R.id.tv_show_log) - TextView tv_show_log; - @BindView(R.id.iv_bug) - ImageView iv_bug; - @BindView(R.id.sv) - ScrollView sv; - - private Unbinder mUnbinder = null; - private CaocConfig mCaocConfig; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mCaocConfig = CustomActivityOnCrash.getConfigFromIntent(getIntent()); - if (mCaocConfig == null) { - finish(); - return; - } - WanApp.setDarkModeStatus(); - StatusBarCompat.setIconMode(this, !WanApp.getDarkModeStatus()); - setContentView(R.layout.activity_crash); - mUnbinder = ButterKnife.bind(this); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - if (mUnbinder != null) { - mUnbinder.unbind(); - } - } - - @OnClick({R.id.tv_restart, R.id.tv_exit, R.id.tv_show_log}) - public void onClick(View v) { - switch (v.getId()) { - default: - break; - case R.id.tv_show_log: - tv_show_log.setVisibility(View.GONE); - iv_bug.setVisibility(View.GONE); - sv.setVisibility(View.VISIBLE); - tv_log.setText(CustomActivityOnCrash.getAllErrorDetailsFromIntent(this, getIntent())); - break; - case R.id.tv_exit: - CustomActivityOnCrash.closeApplication(this, mCaocConfig); - break; - case R.id.tv_restart: - CustomActivityOnCrash.restartApplication(this, mCaocConfig); - break; - } - } -} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/CrashActivity.kt b/app/src/main/java/per/goweii/wanandroid/module/main/activity/CrashActivity.kt new file mode 100644 index 00000000..118fab82 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/activity/CrashActivity.kt @@ -0,0 +1,72 @@ +package per.goweii.wanandroid.module.main.activity + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.graphics.Color +import android.os.Bundle +import android.os.Process +import android.view.View +import android.view.WindowManager +import androidx.appcompat.app.AppCompatActivity +import per.goweii.ponyo.crash.Crash +import per.goweii.statusbarcompat.StatusBarCompat +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.ActivityCrashBinding +import per.goweii.wanandroid.utils.DarkModeUtils +import java.io.PrintWriter +import java.io.StringWriter +import kotlin.system.exitProcess + +class CrashActivity : AppCompatActivity() { + private lateinit var binding: ActivityCrashBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS) + window.decorView.systemUiVisibility = + View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) + window.statusBarColor = Color.TRANSPARENT + DarkModeUtils.initDarkMode(this) + StatusBarCompat.setIconMode(this, !DarkModeUtils.isDarkMode(this)) + binding = ActivityCrashBinding.inflate(layoutInflater) + setContentView(binding.root) + binding.tvCopyLog.setOnClickListener { + val cm = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager + cm.setPrimaryClip(ClipData.newPlainText("error", binding.tvError.text)) + binding.tvCopyLog.text = getString(R.string.copied) + } + binding.btnExit.setOnClickListener { + finish() + Process.killProcess(Process.myPid()) + exitProcess(10) + } + binding.btnRestart.setOnClickListener { + Crash.restartApp(applicationContext) + finish() + Process.killProcess(Process.myPid()) + } + showLog() + } + + private fun showLog() { + val e = intent.getSerializableExtra("error") as Throwable + val stringWriter = StringWriter() + val printWriter = PrintWriter(stringWriter) + e.printStackTrace(printWriter) + binding.tvError.text = stringWriter.toString().toDBC() + } + + private fun String.toDBC(): String { + val c = this.toCharArray() + for (i in c.indices) { + if (c[i].toInt() == 12288) { + c[i] = 32.toChar() + continue + } + if (c[i].toInt() in 65281..65374) c[i] = (c[i] - 65248) + } + return String(c) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/InstallApkActivity.java b/app/src/main/java/per/goweii/wanandroid/module/main/activity/InstallApkActivity.java new file mode 100644 index 00000000..e1e8f502 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/activity/InstallApkActivity.java @@ -0,0 +1,91 @@ +package per.goweii.wanandroid.module.main.activity; + +import android.content.Context; +import android.content.Intent; +import android.text.TextUtils; +import android.view.LayoutInflater; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewbinding.ViewBinding; + +import java.io.File; + +import per.goweii.anypermission.AnyPermission; +import per.goweii.anypermission.RequestListener; +import per.goweii.basic.core.base.BaseActivity; +import per.goweii.basic.core.base.BasePresenter; +import per.goweii.basic.core.mvp.MvpPresenter; +import per.goweii.basic.utils.LogUtils; +import per.goweii.wanandroid.common.WanApp; +import per.goweii.wanandroid.utils.ThemeUtils; + +public class InstallApkActivity extends BaseActivity { + private static final String APK_PATH = "apk_path"; + + public static void start(Context context, File apk) { + Intent intent = new Intent(context, InstallApkActivity.class); + intent.putExtra(APK_PATH, apk.getPath()); + context.startActivity(intent); + } + + private File apkFile; + + @Nullable + @Override + protected ViewBinding initViewBinding(@NonNull LayoutInflater inflater) { + return null; + } + + @Nullable + @Override + protected BasePresenter initPresenter() { + return null; + } + + @Override + protected void initView() { + String apkPath = getIntent().getStringExtra(APK_PATH); + if (TextUtils.isEmpty(apkPath)) { + finish(); + return; + } + apkFile = new File(apkPath); + if (!apkFile.exists()) { + finish(); + } + } + + @Override + protected void loadData() { + } + + @Override + protected void onResume() { + super.onResume(); + if (!apkFile.exists()) { + finish(); + return; + } + AnyPermission.with(this) + .install(apkFile) + .request(new RequestListener() { + @Override + public void onSuccess() { + LogUtils.d("ThemeUtils", "installApk"); + ThemeUtils.setWillInstall(); + if (!ThemeUtils.isDefLauncher(getApplicationContext())) { + WanApp.finishAllActivity(); + ThemeUtils.resetLauncher(getApplicationContext()); + InstallApkActivity.start(InstallApkActivity.this, apkFile); + } + } + + @Override + public void onFailed() { + ThemeUtils.setNotInstall(); + finish(); + } + }); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/MainActivity.java b/app/src/main/java/per/goweii/wanandroid/module/main/activity/MainActivity.java index ed06e9de..f4f5e3ee 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/activity/MainActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/activity/MainActivity.java @@ -1,18 +1,25 @@ package per.goweii.wanandroid.module.main.activity; import android.Manifest; -import android.content.ClipData; -import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; -import android.graphics.Color; import android.graphics.drawable.ColorDrawable; -import android.net.Uri; -import android.support.annotation.Nullable; -import android.support.v4.view.ViewPager; +import android.os.Bundle; import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager.widget.ViewPager; import butterknife.BindView; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; +import per.goweii.anylayer.Layer; +import per.goweii.anylayer.notification.NotificationLayer; import per.goweii.anypermission.RequestListener; import per.goweii.anypermission.RuntimeRequester; import per.goweii.basic.core.adapter.FixedFragmentPagerAdapter; @@ -20,30 +27,62 @@ import per.goweii.basic.core.permission.PermissionUtils; import per.goweii.basic.ui.dialog.UpdateDialog; import per.goweii.basic.utils.LogUtils; -import per.goweii.basic.utils.SPUtils; -import per.goweii.basic.utils.listener.SimpleListener; +import per.goweii.basic.utils.ResUtils; +import per.goweii.basic.utils.display.DisplayInfoUtils; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.common.WanApp; +import per.goweii.wanandroid.databinding.ActivityMainBinding; +import per.goweii.wanandroid.db.model.ReadLaterModel; +import per.goweii.wanandroid.event.BannerAutoSwitchEnableEvent; +import per.goweii.wanandroid.event.CloseSecondFloorEvent; +import per.goweii.wanandroid.event.HomeActionBarEvent; +import per.goweii.wanandroid.module.explore.ExploreFragment; +import per.goweii.wanandroid.module.main.dialog.AdvertDialog; import per.goweii.wanandroid.module.main.dialog.CopiedLinkDialog; import per.goweii.wanandroid.module.main.dialog.DownloadDialog; +import per.goweii.wanandroid.module.main.dialog.PasswordDialog; +import per.goweii.wanandroid.module.main.dialog.PrivacyPolicyDialog; import per.goweii.wanandroid.module.main.fragment.MainFragment; import per.goweii.wanandroid.module.main.fragment.UserArticleFragment; +import per.goweii.wanandroid.module.main.model.AdvertBean; +import per.goweii.wanandroid.module.main.model.ConfigBean; import per.goweii.wanandroid.module.main.model.UpdateBean; import per.goweii.wanandroid.module.main.presenter.MainPresenter; import per.goweii.wanandroid.module.main.view.MainView; +import per.goweii.wanandroid.utils.ADUtils; +import per.goweii.wanandroid.utils.ConfigUtils; +import per.goweii.wanandroid.utils.CopiedTextProcessor; +import per.goweii.wanandroid.utils.PredefinedTaskQueen; +import per.goweii.wanandroid.utils.RecommendManager; +import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.ThemeUtils; import per.goweii.wanandroid.utils.UpdateUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; +import per.goweii.wanandroid.utils.UserUtils; +import per.goweii.wanandroid.utils.wanpwd.WanPwdParser; -public class MainActivity extends BaseActivity implements MainView, ViewPager.OnPageChangeListener { +public class MainActivity extends BaseActivity implements MainView { private static final int REQ_CODE_PERMISSION = 1; + private static final String sTaskPrivacyPolicy = "PrivacyPolicy"; + private static final String sTaskUpdate = "Update"; + private static final String sTaskBetaUpdate = "BetaUpdate"; + private static final String sTaskDownload = "Download"; + private static final String sTaskAdvert = "Advert"; + private static final String sTaskWanPwd = "WanPwd"; + private static final String sTaskCopiedLink = "CopiedLink"; + private static final String sTaskReadLater = "ReadLater"; + @BindView(R.id.vp) ViewPager vp; private FixedFragmentPagerAdapter mPagerAdapter; private RuntimeRequester mRuntimeRequester; - private UpdateUtils mUpdateUtils; - private String mLastCopyLink = ""; private CopiedLinkDialog mCopiedLinkDialog = null; + private PasswordDialog mPasswordDialog = null; + + private final PredefinedTaskQueen mPredefinedTaskQueen = new PredefinedTaskQueen(); public static void start(Context context) { Intent intent = new Intent(context, MainActivity.class); @@ -51,20 +90,24 @@ public static void start(Context context) { } @Override - public boolean swipeBackEnable() { - return false; + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState == null) { + LogUtils.d("ThemeUtils", "MainActivity onCreate setNotInstall"); + ThemeUtils.setNotInstall(); + } } @Override protected void initWindow() { super.initWindow(); - setTheme(R.style.AppTheme); - getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + getWindow().setBackgroundDrawable(new ColorDrawable(ResUtils.getThemeColor(this, R.attr.colorBackground))); } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_main; + protected ActivityMainBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityMainBinding.inflate(inflater); } @Nullable @@ -75,22 +118,106 @@ protected MainPresenter initPresenter() { @Override protected void initView() { - vp.addOnPageChangeListener(this); vp.setOffscreenPageLimit(1); mPagerAdapter = new FixedFragmentPagerAdapter(getSupportFragmentManager()); vp.setAdapter(mPagerAdapter); - mPagerAdapter.setFragmentList( - UserArticleFragment.create(), - MainFragment.create() - ); + mPagerAdapter.setFragmentList(new ExploreFragment(), MainFragment.create()); vp.setCurrentItem(1); - onPageSelected(vp.getCurrentItem()); - mLastCopyLink = SPUtils.getInstance().get("LastCopyLink", ""); + vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + new CloseSecondFloorEvent().post(); + } + + @Override + public void onPageScrollStateChanged(int state) { + } + }); + + mPredefinedTaskQueen.append(new PredefinedTaskQueen.Task(sTaskPrivacyPolicy)); + mPredefinedTaskQueen.append(new PredefinedTaskQueen.Task(sTaskUpdate)); + mPredefinedTaskQueen.append(new PredefinedTaskQueen.Task(sTaskBetaUpdate)); + mPredefinedTaskQueen.append(new PredefinedTaskQueen.Task(sTaskDownload)); + mPredefinedTaskQueen.append(new PredefinedTaskQueen.Task(sTaskAdvert)); + mPredefinedTaskQueen.append(new PredefinedTaskQueen.Task(sTaskWanPwd)); + mPredefinedTaskQueen.append(new PredefinedTaskQueen.Task(sTaskCopiedLink)); + mPredefinedTaskQueen.append(new PredefinedTaskQueen.Task(sTaskReadLater)); + mPredefinedTaskQueen.start(); + + initCopiedTextProcessor(); + showPrivacyPolicyDialog(); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + switch (ev.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + new BannerAutoSwitchEnableEvent(false).post(); + break; + case MotionEvent.ACTION_UP: + new BannerAutoSwitchEnableEvent(true).post(); + break; + default: + break; + } + return super.dispatchTouchEvent(ev); + } + + private void initCopiedTextProcessor() { + CopiedTextProcessor.getInstance().setProcessCallback(new CopiedTextProcessor.ProcessCallback() { + @Override + public void isLink(String link) { + showCopiedLinkDialog(link); + mPredefinedTaskQueen.get(sTaskWanPwd).complete(); + } + + @Override + public void isWanPwd(WanPwdParser pwd) { + showWanPwdDialog(pwd); + mPredefinedTaskQueen.get(sTaskCopiedLink).complete(); + } + + @Override + public void ignored() { + mPredefinedTaskQueen.get(sTaskCopiedLink).complete(); + mPredefinedTaskQueen.get(sTaskWanPwd).complete(); + } + }); + } + + private void showPrivacyPolicyDialog() { + mPredefinedTaskQueen.get(sTaskPrivacyPolicy) + .runnable(new Function1() { + @Override + public Unit invoke(PredefinedTaskQueen.Completion completion) { + PrivacyPolicyDialog.showIfFirst(getContext(), new PrivacyPolicyDialog.CompleteCallback() { + @Override + public void onComplete() { + completion.complete(); + } + }); + return null; + } + }); } @Override protected void loadData() { + presenter.getConfig(); + presenter.getAdvert(); presenter.update(); + getWindow().getDecorView().postDelayed(new Runnable() { + @Override + public void run() { + presenter.getReadLaterArticle(); + } + }, 500L); + + RecommendManager.getInstance().load(); } @Override @@ -99,114 +226,371 @@ protected void onStart() { vp.postDelayed(new Runnable() { @Override public void run() { - isNeedOpenLink(); + CopiedTextProcessor.getInstance().process(); } }, 500L); } @Override - protected void onStop() { - super.onStop(); + protected void onDestroy() { + super.onDestroy(); + if (isFinishing()) { + if (!ThemeUtils.isWillInstall()) { + ThemeUtils.updateLauncher(getApplicationContext(), ConfigUtils.getInstance().getThemeName()); + } + } } - private void isNeedOpenLink() { - ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = clipboardManager.getPrimaryClip(); - if (clip == null || clip.getItemCount() <= 0) { - return; + private void showCopiedLinkDialog(String link) { + // 先隐藏之前显示的不同的并且置空 + if (mCopiedLinkDialog != null) { + if (!TextUtils.equals(mCopiedLinkDialog.getLink(), link)) { + mCopiedLinkDialog.dismiss(); + mCopiedLinkDialog = null; + } } - for (int i = 0; i < clip.getItemCount(); i++) { - ClipData.Item item = clip.getItemAt(i); - LogUtils.i("WanApp", "" + item.toString()); + // 没有复用的就重新创建 + if (mCopiedLinkDialog == null) { + mCopiedLinkDialog = new CopiedLinkDialog(vp, link); } - ClipData.Item item = clip.getItemAt(0); - if (TextUtils.isEmpty(item.getText())) { - return; + // 预定义任务是否完成? + if (!mPredefinedTaskQueen.isCompleted()) { + PredefinedTaskQueen.Task task = mPredefinedTaskQueen.get(sTaskCopiedLink); + // 当前任务是否完成? + if (!task.isCompleted()) { + // 当前任务没有完成,更新任务 + task.runnable(new Function1() { + @Override + public Unit invoke(PredefinedTaskQueen.Completion completion) { + mCopiedLinkDialog.addOnDismissListener(new Layer.OnDismissListener() { + @Override + public void onPreDismiss(@NonNull Layer layer) { + } + + @Override + public void onPostDismiss(@NonNull Layer layer) { + completion.complete(); + } + }); + mCopiedLinkDialog.show(); + return null; + } + }); + return; + } } - String text = item.getText().toString(); - if (TextUtils.equals(mLastCopyLink, text)) { - return; + // 当前任务没有已完成,直接显示 + mCopiedLinkDialog.show(); + } + + private void showWanPwdDialog(WanPwdParser parser) { + // 先隐藏之前显示的不同的并且置空 + if (mPasswordDialog != null) { + if (!parser.equals(mPasswordDialog.getPassword())) { + mPasswordDialog.dismiss(); + mPasswordDialog = null; + } } - Uri uri = Uri.parse(text); - if (!TextUtils.equals(uri.getScheme(), "http") && !TextUtils.equals(uri.getScheme(), "https")) { - return; + // 没有复用的就重新创建 + if (mPasswordDialog == null) { + mPasswordDialog = new PasswordDialog(getContext(), parser); } - if (mCopiedLinkDialog == null) { - mCopiedLinkDialog = new CopiedLinkDialog(vp, text, new SimpleListener() { - @Override - public void onResult() { - mLastCopyLink = text; - SPUtils.getInstance().save("LastCopyLink", mLastCopyLink); - } - }); + // 预定义任务是否完成? + if (!mPredefinedTaskQueen.isCompleted()) { + PredefinedTaskQueen.Task task = mPredefinedTaskQueen.get(sTaskWanPwd); + // 当前任务是否完成? + if (!task.isCompleted()) { + // 当前任务没有完成,更新任务 + task.runnable(new Function1() { + @Override + public Unit invoke(PredefinedTaskQueen.Completion completion) { + mPasswordDialog.addOnDismissListener(new Layer.OnDismissListener() { + @Override + public void onPreDismiss(@NonNull Layer layer) { + } + + @Override + public void onPostDismiss(@NonNull Layer layer) { + mPasswordDialog = null; + completion.complete(); + } + }); + mPasswordDialog.show(); + return null; + } + }); + return; + } } - if (!mCopiedLinkDialog.isShow()) { - mCopiedLinkDialog.show(); + // 当前任务没有已完成,直接显示 + mPasswordDialog.show(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (mRuntimeRequester != null) { + mRuntimeRequester.onActivityResult(requestCode); } } - public void openUserArticle() { - vp.setCurrentItem(0); + @Override + public void onBackPressed() { + if (vp.getCurrentItem() == 1) { + super.onBackPressed(); + } else { + vp.setCurrentItem(1); + } } @Override - public void onPageScrolled(int i, float v, int i1) { + public void updateSuccess(int code, UpdateBean data) { + PredefinedTaskQueen.Task task = mPredefinedTaskQueen.get(sTaskUpdate); + task.runnable(new Function1() { + @Override + public Unit invoke(PredefinedTaskQueen.Completion completion) { + boolean shouldForce = UpdateUtils.getInstance().shouldForceUpdate(data); + if (shouldForce || UpdateUtils.getInstance().shouldUpdate(data)) { + mPredefinedTaskQueen.get(sTaskBetaUpdate).complete(); + UpdateDialog.with(getContext()) + .setUrl(data.getUrl()) + .setUrlBackup(data.getUrl_backup()) + .setVersionCode(data.getVersion_code()) + .setVersionName(data.getVersion_name()) + .setForce(shouldForce) + .setDescription(data.getDesc()) + .setTime(data.getTime()) + .setOnUpdateListener(new UpdateDialog.OnUpdateListener() { + @Override + public void onDownload(String url, String urlBackup, boolean isForce) { + download(url, urlBackup, isForce); + } + + @Override + public void onIgnore(String versionName, int versionCode) { + UpdateUtils.getInstance().ignore(versionCode); + } + }) + .setOnDismissListener(new UpdateDialog.OnDismissListener() { + @Override + public void onDismiss() { + completion.complete(); + if (!mPredefinedTaskQueen.get(sTaskDownload).isRunnable()) { + mPredefinedTaskQueen.get(sTaskDownload).complete(); + } + } + }) + .show(); + } else { + if (!UpdateUtils.getInstance().isNewest(data) && UserUtils.getInstance().isLogin()) { + presenter.betaUpdate(); + } else { + mPredefinedTaskQueen.get(sTaskBetaUpdate).complete(); + mPredefinedTaskQueen.get(sTaskDownload).complete(); + } + completion.complete(); + } + return null; + } + }); } @Override - public void onPageSelected(int i) { + public void updateFailed(int code, String msg) { + mPredefinedTaskQueen.get(sTaskUpdate).complete(); + mPredefinedTaskQueen.get(sTaskBetaUpdate).complete(); + mPredefinedTaskQueen.get(sTaskDownload).complete(); } @Override - public void onPageScrollStateChanged(int i) { + public void betaUpdateSuccess(int code, UpdateBean data) { + PredefinedTaskQueen.Task task = mPredefinedTaskQueen.get(sTaskBetaUpdate); + task.runnable(new Function1() { + @Override + public Unit invoke(PredefinedTaskQueen.Completion completion) { + boolean shouldForce = UpdateUtils.getInstance().shouldForceUpdate(data); + if (shouldForce || UpdateUtils.getInstance().shouldUpdateBeta(data)) { + UpdateDialog.with(getContext()) + .setTest(true) + .setUrl(data.getUrl()) + .setUrlBackup(data.getUrl_backup()) + .setVersionCode(data.getVersion_code()) + .setVersionName(data.getVersion_name()) + .setForce(shouldForce) + .setDescription(data.getDesc()) + .setTime(data.getTime()) + .setOnUpdateListener(new UpdateDialog.OnUpdateListener() { + @Override + public void onDownload(String url, String urlBackup, boolean isForce) { + download(url, urlBackup, isForce); + } + + @Override + public void onIgnore(String versionName, int versionCode) { + UpdateUtils.getInstance().ignoreBeta(versionName, versionCode); + } + }) + .setOnDismissListener(new UpdateDialog.OnDismissListener() { + @Override + public void onDismiss() { + completion.complete(); + if (!mPredefinedTaskQueen.get(sTaskDownload).isRunnable()) { + mPredefinedTaskQueen.get(sTaskDownload).complete(); + } + } + }) + .show(); + } else { + completion.complete(); + mPredefinedTaskQueen.get(sTaskDownload).complete(); + } + return null; + } + }); } @Override - public void updateSuccess(int code, UpdateBean data) { - mUpdateUtils = UpdateUtils.newInstance(); - if (!mUpdateUtils.shouldUpdate(data.getVersion_code())) { - return; - } - UpdateDialog.with(getContext()) - .setUrl(data.getUrl()) - .setUrlBackup(data.getUrl_backup()) - .setVersionCode(data.getVersion_code()) - .setVersionName(data.getVersion_name()) - .setForce(data.isForce()) - .setDescription(data.getDesc()) - .setTime(data.getTime()) - .setOnUpdateListener(new UpdateDialog.OnUpdateListener() { + public void betaUpdateFailed(int code, String msg) { + mPredefinedTaskQueen.get(sTaskBetaUpdate).complete(); + mPredefinedTaskQueen.get(sTaskDownload).complete(); + } + + private void download(final String url, final String urlBackup, final boolean isForce) { + PredefinedTaskQueen.Task task = mPredefinedTaskQueen.get(sTaskDownload); + task.runnable(new Function1() { + @Override + public Unit invoke(PredefinedTaskQueen.Completion completion) { + mRuntimeRequester = PermissionUtils.request(new RequestListener() { @Override - public void onDownload(String url, String urlBackup, boolean isForce) { - download(data.getVersion_name(), url, urlBackup, isForce); + public void onSuccess() { + DownloadDialog.with(getActivity(), isForce, url, urlBackup, new DownloadDialog.OnDismissListener() { + @Override + public void onDismiss() { + completion.complete(); + } + }); } @Override - public void onIgnore(int versionCode) { - mUpdateUtils.ignore(versionCode); + public void onFailed() { + if (isForce) { + download(url, urlBackup, true); + } else { + completion.complete(); + } } - }) - .show(); + }, getContext(), REQ_CODE_PERMISSION, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE); + return null; + } + }); } @Override - protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (mRuntimeRequester != null) { - mRuntimeRequester.onActivityResult(requestCode); + public void getConfigSuccess(ConfigBean configBean) { + if (configBean.isEnableAtNow()) { + new HomeActionBarEvent( + configBean.getHomeTitle(), + configBean.getActionBarBgColor(), + configBean.getActionBarBgImageUrl(), + configBean.getSecondFloorBgImageUrl(), + configBean.getSecondFloorBgImageBlurPercent() + ).postSticky(); + } else { + new HomeActionBarEvent().postSticky(); } } - private void download(final String versionName, final String url, final String urlBackup, final boolean isForce) { - mRuntimeRequester = PermissionUtils.request(new RequestListener() { - @Override - public void onSuccess() { - DownloadDialog.with(getActivity(), isForce, url, urlBackup, versionName); - } + @Override + public void getConfigFailed(int code, String msg) { + } - @Override - public void onFailed() { - } - }, getContext(), REQ_CODE_PERMISSION, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE); + @Override + public void newThemeFounded() { + WanApp.recreate(); + } + + @Override + public void getAdvertSuccess(int code, AdvertBean advertBean) { + if (!ADUtils.getInstance().shouldShowAD(advertBean)) { + mPredefinedTaskQueen.get(sTaskAdvert).complete(); + return; + } + mPredefinedTaskQueen.get(sTaskAdvert) + .runnable(new Function1() { + @Override + public Unit invoke(PredefinedTaskQueen.Completion completion) { + new AdvertDialog(MainActivity.this) + .setAdvertBean(advertBean) + .addOnVisibleChangeListener(new Layer.OnVisibleChangedListener() { + @Override + public void onShow(@NonNull Layer layer) { + ADUtils.getInstance().setAdShown(); + } + + @Override + public void onDismiss(@NonNull Layer layer) { + completion.complete(); + } + }) + .show(); + return null; + } + }); + } + + @Override + public void getAdvertFailed(int code, String msg) { + mPredefinedTaskQueen.get(sTaskAdvert).complete(); + } + + @Override + public void getReadLaterArticleSuccess(ReadLaterModel readLaterModel) { + mPredefinedTaskQueen.get(sTaskReadLater) + .runnable(new Function1() { + @Override + public Unit invoke(PredefinedTaskQueen.Completion completion) { + if (!SettingUtils.getInstance().isShowReadLaterNotification()) { + completion.complete(); + return null; + } + new NotificationLayer(MainActivity.this) + .setContentView(R.layout.dialog_read_later_notification) + .addOnBindDataListener(new Layer.OnBindDataListener() { + @Override + public void onBindData(@NonNull Layer layer) { + View child = layer.requireView(R.id.dialog_read_later_notification); + child.setPadding(0, DisplayInfoUtils.getInstance().getStatusBarHeight(), 0, 0); + TextView tv_title = layer.requireView(R.id.dialog_read_later_notification_tv_title); + TextView tv_desc = layer.requireView(R.id.dialog_read_later_notification_tv_desc); + tv_title.setText(getString(R.string.read_on_or_not)); + tv_desc.setText(readLaterModel.getTitle()); + } + }) + .addOnClickToDismissListener(new Layer.OnClickListener() { + @Override + public void onClick(@NonNull Layer layer, @NonNull View view) { + UrlOpenUtils.Companion + .with(readLaterModel.getLink()) + .open(getContext()); + } + }, R.id.dialog_read_later_notification_ll_content) + .addOnDismissListener(new Layer.OnDismissListener() { + @Override + public void onPreDismiss(@NonNull Layer layer) { + } + + @Override + public void onPostDismiss(@NonNull Layer layer) { + completion.complete(); + } + }).show(); + return null; + } + }); + } + + @Override + public void getReadLaterArticleFailed() { + mPredefinedTaskQueen.get(sTaskReadLater).complete(); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/RouterActivity.java b/app/src/main/java/per/goweii/wanandroid/module/main/activity/RouterActivity.java index 8bb1859b..8b2c3016 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/activity/RouterActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/activity/RouterActivity.java @@ -5,33 +5,40 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.support.annotation.Nullable; import android.text.TextUtils; +import android.view.LayoutInflater; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewbinding.ViewBinding; import per.goweii.basic.core.base.BaseActivity; import per.goweii.basic.core.mvp.MvpPresenter; import per.goweii.basic.utils.LogUtils; +import per.goweii.swipeback.SwipeBackAbility; +import per.goweii.swipeback.SwipeBackDirection; import per.goweii.wanandroid.common.WanApp; +import per.goweii.wanandroid.utils.UrlOpenUtils; /** * @author CuiZhen * @date 2019/5/7 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class RouterActivity extends BaseActivity implements Runnable { +public class RouterActivity extends BaseActivity implements SwipeBackAbility.Direction, Runnable { private static final String TAG = RouterActivity.class.getSimpleName(); + @SwipeBackDirection @Override - public boolean swipeBackEnable() { - return false; + public int swipeBackDirection() { + return 0; } + @Nullable @Override - protected int getLayoutId() { - return 0; + protected ViewBinding initViewBinding(@NonNull LayoutInflater inflater) { + return null; } @Nullable @@ -118,7 +125,9 @@ private void handleOpenUrl(Uri data) { if (!TextUtils.equals(scheme, "http") && !TextUtils.equals(scheme, "https")) { return; } - WebActivity.start(getContext(), data.toString()); + UrlOpenUtils.Companion + .with(data.toString()) + .open(getContext()); } private void handleShareText(String sharedText) { diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/ScanActivity.kt b/app/src/main/java/per/goweii/wanandroid/module/main/activity/ScanActivity.kt new file mode 100644 index 00000000..d9da85e2 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/activity/ScanActivity.kt @@ -0,0 +1,388 @@ +package per.goweii.wanandroid.module.main.activity + +import android.Manifest +import android.animation.Animator +import android.animation.ObjectAnimator +import android.annotation.SuppressLint +import android.app.Activity +import android.content.Intent +import android.view.LayoutInflater +import android.view.View +import android.view.animation.DecelerateInterpolator +import per.goweii.anypermission.AnyPermission +import per.goweii.anypermission.RequestListener +import per.goweii.anypermission.RuntimeRequester +import per.goweii.basic.core.base.BaseActivity +import per.goweii.basic.utils.LogUtils +import per.goweii.basic.utils.bitmap.BitmapUtils +import per.goweii.basic.utils.ext.gone +import per.goweii.basic.utils.ext.invisible +import per.goweii.basic.utils.ext.visible +import per.goweii.codex.decoder.CodeDecoder +import per.goweii.codex.decorator.beep.BeepDecorator +import per.goweii.codex.decorator.gesture.GestureDecorator +import per.goweii.codex.decorator.vibrate.VibrateDecorator +import per.goweii.codex.processor.zxing.ZXingMultiDecodeQRCodeProcessor +import per.goweii.codex.processor.zxing.ZXingMultiScanQRCodeProcessor +import per.goweii.codex.scanner.CameraProxy +import per.goweii.codex.scanner.CodeScanner +import per.goweii.statusbarcompat.StatusBarCompat +import per.goweii.swipeback.SwipeBackAbility +import per.goweii.swipeback.SwipeBackDirection +import per.goweii.swipeback.SwipeBackTransformer +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.ActivityScanBinding +import per.goweii.wanandroid.module.main.presenter.ScanPresenter +import per.goweii.wanandroid.module.main.view.ScanView +import per.goweii.wanandroid.utils.PictureSelector +import per.goweii.wanandroid.utils.UrlOpenUtils + +/** + * @author CuiZhen + * @date 2020/2/26 + */ +class ScanActivity : BaseActivity(), ScanView, + SwipeBackAbility.Direction, SwipeBackAbility.Transformer { + + companion object { + private const val REQ_CODE_PERMISSION_CAMERA = 1 + private const val REQ_CODE_PERMISSION_ALBUM = 2 + private const val REQ_CODE_SELECT_PIC = 3 + + @JvmStatic + fun start(activity: Activity) { + val intent = Intent(activity, ScanActivity::class.java) + activity.startActivity(intent) + activity.overridePendingTransition( + R.anim.swipeback_activity_open_bottom_in, + R.anim.swipeback_activity_open_alpha_out + ) + } + + @JvmStatic + fun startForResult(activity: Activity, requestCode: Int) { + val intent = Intent(activity, ScanActivity::class.java) + activity.startActivityForResult(intent, requestCode) + activity.overridePendingTransition( + R.anim.swipeback_activity_open_bottom_in, + R.anim.swipeback_activity_open_alpha_out + ) + } + } + + private var mRuntimeRequester: RuntimeRequester? = null + private var tvTipAnim: Animator? = null + + private var codeScanner: CodeScanner? = null + + @SwipeBackDirection + override fun swipeBackDirection(): Int = SwipeBackDirection.BOTTOM + + override fun swipeBackTransformer(): SwipeBackTransformer? = null + + override fun initViewBinding(inflater: LayoutInflater): ActivityScanBinding { + return ActivityScanBinding.inflate(inflater) + } + + override fun initPresenter(): ScanPresenter = ScanPresenter() + + override fun initView() { + StatusBarCompat.transparent(this) + binding.ivClose.setOnClickListener { + finish() + } + binding.ivTorch.invisible() + binding.ivAlbum.setOnClickListener { + startAlbum() + } + binding.ivTorch.setOnClickListener { + codeScanner?.enableTorch(!binding.ivTorch.isSelected) + } + codeScanner = binding.codeScanner.apply { + cameraProxyLiveData.observe(this@ScanActivity) { cameraProxy -> + cameraProxy?.torchState?.observe(this@ScanActivity) { torchState -> + when (torchState) { + CameraProxy.TORCH_ON -> binding.ivTorch.isSelected = true + CameraProxy.TORCH_OFF -> binding.ivTorch.isSelected = false + } + } + } + addProcessor(ZXingMultiScanQRCodeProcessor()) + addDecorator( + binding.frozenView, + binding.finderView, + BeepDecorator(), + VibrateDecorator(), + GestureDecorator() + ) + onFound { + onScanQRCodeSuccess(it.first().text) + } + bindToLifecycle(this@ScanActivity) + } + startScan() + } + + override fun loadData() { + } + + override fun onDestroy() { + super.onDestroy() + cancelTipAnim() + } + + override fun finish() { + super.finish() + overridePendingTransition( + R.anim.swipeback_activity_close_alpha_in, + R.anim.swipeback_activity_close_bottom_out + ) + } + + private fun startScan() { + mRuntimeRequester = AnyPermission.with(context) + .runtime(REQ_CODE_PERMISSION_CAMERA) + .permissions(Manifest.permission.CAMERA) + .onBeforeRequest { _, executor -> + showTip( + getString(R.string.scanning_the_qr_code_requires_camera_permission), + getString(R.string.click_apply), + { + hideTip() + executor.execute() + }) + } + .onBeenDenied { _, executor -> + showTip( + getString(R.string.camera_permission_denied_tips), + getString(R.string.reapply), + { + hideTip() + executor.execute() + }) + } + .onGoSetting { _, executor -> + showTip( + getString(R.string.camera_permission_always_denied_tips), + getString(R.string.go_to_settings), + { + hideTip() + executor.execute() + }) + } + .request(object : RequestListener { + @SuppressLint("MissingPermission") + override fun onSuccess() { + binding.ivTorch.visible() + binding.finderView.visible() + codeScanner?.startScan() + } + + override fun onFailed() { + showTip( + getString(R.string.unable_to_get_camera_permissions), + getString(R.string.click_get), + { + hideTip() + startScan() + }) + } + }) + } + + private fun stopScan() { + binding.ivTorch.invisible() + binding.finderView.invisible() + codeScanner?.stopScan() + } + + private fun startAlbum() { + stopScan() + mRuntimeRequester = AnyPermission.with(context) + .runtime(REQ_CODE_PERMISSION_ALBUM) + .permissions(Manifest.permission.WRITE_EXTERNAL_STORAGE) + .onBeforeRequest { _, executor -> + showTip( + getString(R.string.selecting_an_image_requires_storage_permissions), + getString(R.string.click_apply), + { + hideTip() + executor.execute() + }, + getString(R.string.return_to_scan_the_code), + { + hideTip() + executor.cancel() + }) + } + .onBeenDenied { _, executor -> + showTip( + getString(R.string.storage_permission_denied_tips), + getString(R.string.reapply), + { + hideTip() + executor.execute() + }, + getString(R.string.return_to_scan_the_code), + { + hideTip() + executor.cancel() + }) + } + .onGoSetting { _, executor -> + showTip( + getString(R.string.storage_permission_always_denied_tips), + getString(R.string.go_to_settings), + { + hideTip() + executor.execute() + }, + getString(R.string.return_to_scan_the_code), + { + hideTip() + executor.cancel() + }) + } + .request(object : RequestListener { + override fun onSuccess() { + PictureSelector.select(this@ScanActivity, REQ_CODE_SELECT_PIC) + } + + override fun onFailed() { + startScan() + } + }) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + when (requestCode) { + REQ_CODE_PERMISSION_CAMERA -> { + mRuntimeRequester?.onActivityResult(requestCode) + } + + REQ_CODE_PERMISSION_ALBUM -> { + mRuntimeRequester?.onActivityResult(requestCode) + } + + REQ_CODE_SELECT_PIC -> { + PictureSelector.result(resultCode, data)?.let { + BitmapUtils.getBitmapFromUri(context, it)?.let { bitmap -> + val decoder = CodeDecoder(ZXingMultiDecodeQRCodeProcessor()) + decoder.decode(bitmap, onSuccess = { results -> + onAlbumQRCodeSuccess(results.first().text) + }, onFailure = { + showTip( + getString(R.string.no_qr_code_is_recognized), + getString(R.string.return_to_scan_the_code), + { + hideTip() + startScan() + }) + }) + } ?: showTip( + getString(R.string.failed_to_open_the_image), + getString(R.string.return_to_scan_the_code), + { + hideTip() + startScan() + }) + } ?: startScan() + } + } + } + + private fun onScanQRCodeSuccess(result: String) { + LogUtils.d("ScanActivity", "result=$result") + window.decorView.postDelayed({ + UrlOpenUtils.with(result).open(context) + finish() + }, 300L) + } + + private fun onAlbumQRCodeSuccess(result: String) { + LogUtils.d("ScanActivity", "result=$result") + window.decorView.postDelayed({ + UrlOpenUtils.with(result).open(context) + finish() + }, 300L) + } + + private fun cancelTipAnim() { + tvTipAnim?.cancel() + tvTipAnim = null + } + + private fun showTip( + text: String, + btnSure: String, + onSure: View.OnClickListener, + btnCancel: String? = null, + onCancel: View.OnClickListener? = null + ) { + binding.llTip ?: return + binding.tvTipText.text = text + binding.tvTipBtnSure.text = btnSure + if (btnCancel.isNullOrEmpty()) { + binding.tvTipBtnCancel.gone() + } else { + binding.tvTipBtnCancel.visible() + binding.tvTipBtnCancel.text = btnCancel + binding.tvTipBtnCancel.setOnClickListener(onCancel) + } + binding.llTip.setOnClickListener(onSure) + cancelTipAnim() + tvTipAnim = ObjectAnimator.ofFloat(binding.llTip, "alpha", 0F, 1F).apply { + duration = 300 + interpolator = DecelerateInterpolator() + addListener(object : Animator.AnimatorListener { + override fun onAnimationRepeat(animation: Animator) { + } + + override fun onAnimationEnd(animation: Animator) { + } + + override fun onAnimationCancel(animation: Animator) { + } + + override fun onAnimationStart(animation: Animator) { + binding.llTip.visible() + } + }) + start() + } + } + + private fun hideTip() { + cancelTipAnim() + if (binding.llTip.visibility != View.VISIBLE) { + return + } + binding.tvTipText.text = "" + binding.tvTipBtnSure.text = "" + binding.tvTipBtnCancel.text = "" + binding.tvTipBtnCancel.gone() + binding.tvTipBtnCancel.setOnClickListener(null) + binding.llTip.setOnClickListener(null) + tvTipAnim = ObjectAnimator.ofFloat(binding.llTip, "alpha", 1F, 0F).apply { + duration = 300 + interpolator = DecelerateInterpolator() + addListener(object : Animator.AnimatorListener { + override fun onAnimationRepeat(animation: Animator) { + } + + override fun onAnimationEnd(animation: Animator) { + binding.llTip.gone() + } + + override fun onAnimationCancel(animation: Animator) { + } + + override fun onAnimationStart(animation: Animator) { + binding.llTip.visible() + } + }) + start() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/ShareArticleActivity.java b/app/src/main/java/per/goweii/wanandroid/module/main/activity/ShareArticleActivity.java index 81734e6c..0e2e649c 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/activity/ShareArticleActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/activity/ShareArticleActivity.java @@ -2,38 +2,42 @@ import android.content.Context; import android.content.Intent; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.net.Uri; import android.text.Editable; import android.text.TextUtils; +import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import butterknife.BindView; import butterknife.OnClick; import per.goweii.actionbarex.common.ActionBarCommon; -import per.goweii.actionbarex.common.OnActionBarChildClickListener; import per.goweii.basic.core.base.BaseActivity; import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.InputMethodUtils; import per.goweii.basic.utils.LogUtils; +import per.goweii.basic.utils.listener.OnClickListener2; import per.goweii.basic.utils.listener.SimpleTextWatcher; import per.goweii.rxhttp.request.base.BaseBean; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.ActivityShareArticleBinding; import per.goweii.wanandroid.module.main.presenter.ShareArticlePresenter; import per.goweii.wanandroid.module.main.view.ShareArticleView; +import per.goweii.wanandroid.utils.UrlOpenUtils; import per.goweii.wanandroid.utils.UserUtils; -import per.goweii.wanandroid.utils.WebHolder; -import per.goweii.wanandroid.widget.WebContainer; +import per.goweii.wanandroid.utils.web.WebHolder; +import per.goweii.wanandroid.utils.web.view.WebContainer; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class ShareArticleActivity extends BaseActivity implements ShareArticleView { +public class ShareArticleActivity extends BaseActivity implements ShareArticleView { private static final String TAG = ShareArticleActivity.class.getSimpleName(); @@ -45,6 +49,8 @@ public class ShareArticleActivity extends BaseActivity im EditText et_title; @BindView(R.id.et_link) EditText et_link; + @BindView(R.id.tv_share) + TextView tv_share; private WebHolder mWebHolder; @@ -66,9 +72,10 @@ public static void start(Context context, String title, String link) { context.startActivity(intent); } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_share_article; + protected ActivityShareArticleBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityShareArticleBinding.inflate(inflater); } @Nullable @@ -79,9 +86,9 @@ protected ShareArticlePresenter initPresenter() { @Override protected void initView() { - abc.setOnRightTextClickListener(new OnActionBarChildClickListener() { + tv_share.setOnClickListener(new OnClickListener2() { @Override - public void onClick(View v) { + public void onClick2(View v) { String title = et_title.getText().toString(); if (TextUtils.isEmpty(title)) { et_title.requestFocus(); @@ -96,10 +103,19 @@ public void onClick(View v) { presenter.shareArticle(title, link); } }); + String link = getIntent().getStringExtra("link"); + if (!TextUtils.isEmpty(link)) { + et_link.setText(link); + } + String title = getIntent().getStringExtra("title"); + if (TextUtils.isEmpty(title)) { + refreshTitle(link); + } else { + resetTitle(title); + } et_link.addTextChangedListener(new SimpleTextWatcher() { @Override public void afterTextChanged(Editable s) { - LogUtils.i(TAG, "afterTextChanged=" + s.toString()); refreshTitle(s.toString()); } }); @@ -107,14 +123,6 @@ public void afterTextChanged(Editable s) { @Override protected void loadData() { - String title = getIntent().getStringExtra("title"); - String link = getIntent().getStringExtra("link"); - if (!TextUtils.isEmpty(title)) { - et_title.setText(title); - } - if (!TextUtils.isEmpty(link)) { - et_link.setText(link); - } } @Override @@ -137,14 +145,26 @@ protected void onPause() { protected void onDestroy() { super.onDestroy(); if (mWebHolder != null) { - mWebHolder.onDestroy(); + mWebHolder.onDestroy(true); } } + private boolean isCorrectUrl(String url) { + if (TextUtils.isEmpty(url)) return false; + Uri uri = Uri.parse(url); + if (uri == null) return false; + String scheme = uri.getScheme(); + if (!TextUtils.equals(scheme, "https") && !TextUtils.equals(scheme, "http")) return false; + return !TextUtils.isEmpty(uri.getHost()); + } + private void refreshTitle(String url) { LogUtils.i(TAG, "refreshTitle=" + url); - if (TextUtils.isEmpty(url)) { - et_title.setText(""); + if (mWebHolder != null) { + mWebHolder.stopLoading(); + } + if (!isCorrectUrl(url)) { + resetTitle(""); return; } if (mWebHolder == null) { @@ -152,17 +172,26 @@ private void refreshTitle(String url) { .setOnPageTitleCallback(new WebHolder.OnPageTitleCallback() { @Override public void onReceivedTitle(@NonNull String title) { - et_title.setText(title); - et_title.setSelection(title.length()); + resetTitle(title); } }) .loadUrl(url); } else { - mWebHolder.stopLoading(); mWebHolder.loadUrl(url); } } + private void resetTitle(String title) { + if (et_title == null) { + return; + } + if (TextUtils.equals(et_title.getText().toString(), title)) { + return; + } + et_title.setText(title); + et_title.setSelection(title.length()); + } + @OnClick({R.id.tv_open, R.id.tv_refresh}) @Override public void onClick(View v) { @@ -178,7 +207,10 @@ protected void onClick2(View v) { refreshTitle(et_link.getText().toString()); break; case R.id.tv_open: - WebActivity.start(getContext(), et_link.getText().toString()); + UrlOpenUtils.Companion + .with(et_link.getText().toString()) + .title(et_title.getText().toString()) + .open(getContext()); break; } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/SplashActivity.java b/app/src/main/java/per/goweii/wanandroid/module/main/activity/SplashActivity.java index 2756a0cc..42de2e19 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/activity/SplashActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/activity/SplashActivity.java @@ -1,28 +1,28 @@ package per.goweii.wanandroid.module.main.activity; -import android.support.annotation.Nullable; +import android.view.LayoutInflater; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewbinding.ViewBinding; import per.goweii.basic.core.base.BaseActivity; import per.goweii.basic.core.mvp.MvpPresenter; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.ActivitySplashBinding; /** * @author CuiZhen * @date 2019/5/7 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class SplashActivity extends BaseActivity { + @Nullable @Override - public boolean swipeBackEnable() { - return false; - } - - @Override - protected int getLayoutId() { - return 0; + protected ViewBinding initViewBinding(@NonNull LayoutInflater inflater) { +// return ActivitySplashBinding.inflate(inflater); + return null; } @Nullable diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/WebActivity.java b/app/src/main/java/per/goweii/wanandroid/module/main/activity/WebActivity.java index bd4ea78d..2db853d2 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/activity/WebActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/activity/WebActivity.java @@ -1,64 +1,62 @@ package per.goweii.wanandroid.module.main.activity; -import android.Manifest; import android.content.Context; import android.content.Intent; -import android.graphics.Bitmap; import android.net.Uri; -import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; import android.text.TextUtils; import android.view.KeyEvent; +import android.view.LayoutInflater; import android.view.View; import android.view.inputmethod.EditorInfo; -import android.webkit.WebView; import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; -import java.util.ArrayList; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.OnApplyWindowInsetsListener; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +import java.net.URLEncoder; +import java.util.Arrays; import java.util.List; import butterknife.BindView; import per.goweii.actionbarex.ActionBarEx; import per.goweii.anylayer.Layer; -import per.goweii.anypermission.RequestListener; -import per.goweii.anypermission.RuntimeRequester; import per.goweii.basic.core.base.BaseActivity; -import per.goweii.basic.core.permission.PermissionUtils; import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.CopyUtils; import per.goweii.basic.utils.InputMethodUtils; import per.goweii.basic.utils.IntentUtils; -import per.goweii.basic.utils.ShareUtils; -import per.goweii.basic.utils.coder.MD5Coder; +import per.goweii.basic.utils.LogUtils; import per.goweii.basic.utils.listener.OnClickListener2; +import per.goweii.swipeback.SwipeBackAbility; +import per.goweii.wanandroid.BuildConfig; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.module.main.dialog.QrcodeShareDialog; +import per.goweii.wanandroid.databinding.ActivityWebBinding; +import per.goweii.wanandroid.db.model.ReadLaterModel; +import per.goweii.wanandroid.module.main.dialog.ArticleShareDialog; import per.goweii.wanandroid.module.main.dialog.WebGuideDialog; import per.goweii.wanandroid.module.main.dialog.WebMenuDialog; import per.goweii.wanandroid.module.main.dialog.WebQuickDialog; -import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.CollectArticleEntity; import per.goweii.wanandroid.module.main.presenter.WebPresenter; import per.goweii.wanandroid.utils.GuideSPUtils; -import per.goweii.wanandroid.utils.RealmHelper; -import per.goweii.wanandroid.utils.SettingUtils; -import per.goweii.wanandroid.utils.WebHolder; +import per.goweii.wanandroid.utils.router.Router; +import per.goweii.wanandroid.utils.tts.TtsManager; +import per.goweii.wanandroid.utils.tts.TtsSource; +import per.goweii.wanandroid.utils.web.WebHolder; import per.goweii.wanandroid.widget.CollectView; -import per.goweii.wanandroid.widget.WebContainer; +import per.goweii.wanandroid.utils.web.view.WebContainer; /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class WebActivity extends BaseActivity implements per.goweii.wanandroid.module.main.view.WebView { - - private static final int REQ_CODE_PERMISSION = 1; +public class WebActivity extends BaseActivity implements per.goweii.wanandroid.module.main.view.WebView, SwipeBackAbility.OnlyEdge, SwipeBackAbility.ForceEdge { @BindView(R.id.ab) ActionBarEx ab; @@ -77,78 +75,44 @@ public class WebActivity extends BaseActivity implements per.gowei @BindView(R.id.iv_menu) ImageView iv_menu; - private RuntimeRequester mRuntimeRequester = null; - private int mArticleId = -1; + private int mCollectId = -1; private String mTitle = ""; private String mAuthor = ""; private String mUrl = ""; - private RealmHelper mRealmHelper = null; private WebGuideDialog mWebGuideDialog = null; private WebHolder mWebHolder; - - private List mCollectedList = new ArrayList<>(1); private WebQuickDialog mWebQuickDialog; - public static void start(Context context, ArticleBean article) { - int articleId = article.getOriginId() != 0 ? article.getOriginId() : article.getId(); - start(context, articleId, article.getTitle(), article.getLink(), article.isCollect()); - } - - public static void start(Context context, int articleId, String title, String url, boolean collected) { - Intent intent = new Intent(context, WebActivity.class); - intent.putExtra("articleId", articleId); - intent.putExtra("title", title); - intent.putExtra("url", url); - intent.putExtra("collected", collected); - context.startActivity(intent); - } - - public static void start(Context context, String title, String author, String url, boolean collected) { + public static void start(Context context, String url, String title, + int articleId, int collectId, boolean collected) { Intent intent = new Intent(context, WebActivity.class); - intent.putExtra("title", title); - intent.putExtra("author", author); intent.putExtra("url", url); - intent.putExtra("collected", collected); - context.startActivity(intent); - } - - public static void start(Context context, String title, String url) { - start(context, title, url, false); - } - - public static void start(Context context, String title, String url, boolean collected) { - Intent intent = new Intent(context, WebActivity.class); intent.putExtra("title", title); - intent.putExtra("url", url); + intent.putExtra("articleId", articleId); + intent.putExtra("collectId", collectId); intent.putExtra("collected", collected); context.startActivity(intent); } - public static void start(Context context, String url) { - start(context, url, false); - } - - public static void start(Context context, String url, boolean collected) { - Intent intent = new Intent(context, WebActivity.class); - intent.putExtra("url", url); - intent.putExtra("collected", collected); - context.startActivity(intent); + public void refreshSwipeBackOnlyEdge() { } - public void refreshSwipeBackOnlyEdge() { - mSwipeBackHelper.setSwipeBackOnlyEdge(swipeBackOnlyEdge()); + @Override + public boolean swipeBackOnlyEdge() { + return true; } @Override - protected boolean swipeBackOnlyEdge() { - return SettingUtils.getInstance().isWebSwipeBackEdge(); + public boolean swipeBackForceEdge() { + return true; } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_web; + protected ActivityWebBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityWebBinding.inflate(inflater); } @Nullable @@ -159,22 +123,29 @@ protected WebPresenter initPresenter() { @Override protected void initView() { - mArticleId = getIntent().getIntExtra("articleId", -1); - mTitle = getIntent().getStringExtra("title"); + Uri uri = Router.getUriFrom(getIntent()); + if (uri != null) { + mUrl = uri.toString(); + } else { + mArticleId = getIntent().getIntExtra("articleId", -1); + mCollectId = getIntent().getIntExtra("collectId", -1); + mTitle = getIntent().getStringExtra("title"); + mAuthor = getIntent().getStringExtra("author"); + mUrl = getIntent().getStringExtra("url"); + } mTitle = mTitle == null ? "" : mTitle; - mAuthor = getIntent().getStringExtra("author"); mAuthor = mAuthor == null ? "" : mAuthor; - mUrl = getIntent().getStringExtra("url"); mUrl = mUrl == null ? "" : mUrl; boolean collected = getIntent().getBooleanExtra("collected", false); if (collected) { CollectArticleEntity entity = new CollectArticleEntity(); entity.setArticleId(mArticleId); + entity.setCollectId(mCollectId); entity.setTitle(mTitle); entity.setAuthor(mAuthor); entity.setUrl(mUrl); entity.setCollect(true); - mCollectedList.add(entity); + presenter.addCollected(entity); } iv_menu.setOnClickListener(new OnClickListener2() { @Override @@ -238,12 +209,11 @@ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { et_title.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { + updateTitle(); if (hasFocus) { - et_title.setText(mWebHolder.getUrl()); InputMethodUtils.show(et_title); showQuickDialog(); } else { - setTitle(); InputMethodUtils.hide(et_title); dismissQuickDialog(); } @@ -252,7 +222,7 @@ public void onFocusChange(View v, boolean hasFocus) { cv_collect.setOnClickListener(new CollectView.OnClickListener() { @Override public void onClick(CollectView v) { - if (!v.isChecked()) { + if (v.isChecked()) { collect(); } else { uncollect(); @@ -260,7 +230,108 @@ public void onClick(CollectView v) { } }); - mRealmHelper = RealmHelper.create(); + ViewCompat.setOnApplyWindowInsetsListener(binding.flBottomBar, new OnApplyWindowInsetsListener() { + @NonNull + @Override + public WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) { + final int b = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom; + binding.flBottomBar.setPadding(0, 0, 0, b); + return insets; + } + }); + } + + @Override + protected void loadData() { + mWebHolder = WebHolder.with(this, wc) + .setOnPageTitleCallback(new WebHolder.OnPageTitleCallback() { + @Override + public void onReceivedTitle(@NonNull String title) { + updateTitle(); + presenter.updateReadRecordTitle(mWebHolder.getUrl(), title); + } + }) + .setOnPageLoadCallback(new WebHolder.OnPageLoadCallback() { + @Override + public void onPageStarted() { + et_title.clearFocus(); + presenter.addReadRecord(mWebHolder.getUrl(), mWebHolder.getTitle(), mWebHolder.getPercent()); + } + + @Override + public void onPageFinished() { + presenter.updateReadRecordTitle(mWebHolder.getUrl(), mWebHolder.getTitle()); + showWebGuideDialogIfNeeded(); + Uri uri = Uri.parse(mWebHolder.getUrl()); + String message = uri.getQueryParameter("scrollToKeywords"); + if (!TextUtils.isEmpty(message)) { + mWebHolder.scrollToKeywords(Arrays.asList(message.split(","))); + } + } + }) + .setOnHistoryUpdateCallback(new WebHolder.OnHistoryUpdateCallback() { + @Override + public void onHistoryUpdate(boolean isReload) { + resetCollect(); + if (mWebHolder.canGoBack()) { + iv_back.setImageResource(R.drawable.ic_back); + } else { + iv_back.setImageResource(R.drawable.ic_close); + } + switchIconEnable(iv_forward, mWebHolder.canGoForward()); + } + }) + .setOnPageProgressCallback(new WebHolder.OnPageProgressCallback() { + @Override + public void onShowProgress() { + } + + @Override + public void onProgressChanged(int progress) { + } + + @Override + public void onHideProgress() { + presenter.isAddedReadLater(mWebHolder.getUrl()); + } + }); + mWebHolder.loadUrl(mUrl); + mWebHolder.setOnPageScrollEndListener(new WebHolder.OnPageScrollEndListener() { + @Override + public void onPageScrollEnd() { + if (isReadLater()) { + presenter.deleteReadLater(mWebHolder.getUrl()); + } + } + }); + mWebHolder.setOnPageScrollChangeListener(new WebHolder.OnPageScrollChangeListener() { + @Override + public void onPageScrolled(float percent) { + presenter.updateReadRecordPercent(mWebHolder.getUrl(), percent); + } + }); + } + + private void showWebGuideDialogIfNeeded() { + if (GuideSPUtils.getInstance().isWebGuideShown()) { + return; + } + if (mWebGuideDialog != null) { + return; + } + mWebGuideDialog = new WebGuideDialog(getContext()); + mWebGuideDialog.addOnVisibleChangeListener(new Layer.OnVisibleChangedListener() { + @Override + public void onShow(@NonNull Layer layer) { + } + + @Override + public void onDismiss(@NonNull Layer layer) { + GuideSPUtils.getInstance().setWebGuideShown(); + mWebGuideDialog = null; + } + }); + mWebGuideDialog.show(); } private void showQuickDialog() { @@ -269,21 +340,33 @@ private void showQuickDialog() { @Override public void onCopyLink() { CopyUtils.copyText(mWebHolder.getUrl()); - ToastMaker.showShort("已复制"); + ToastMaker.showShort(getString(R.string.copied)); } @Override public void onBrowser() { IntentUtils.openBrowser(getContext(), mUrl); } + + @Override + public void onWanPwd() { + String url = URLEncoder.encode(mWebHolder.getUrl()); + StringBuilder s = new StringBuilder(); + String content = getString(R.string.your_friend_shared_a_link_with_you); + String key = String.format(BuildConfig.WANPWD_FORMAT, BuildConfig.WANPWD_TYPE_WEB, url); + s.append(String.format(content, key)); + LogUtils.d("UserPageActivity", s); + CopyUtils.copyText(s.toString()); + ToastMaker.showShort(getString(R.string.copied)); + } }); - mWebQuickDialog.onDismissListener(new Layer.OnDismissListener() { + mWebQuickDialog.addOnDismissListener(new Layer.OnDismissListener() { @Override - public void onDismissing(Layer layer) { + public void onPreDismiss(@NonNull Layer layer) { } @Override - public void onDismissed(Layer layer) { + public void onPostDismiss(@NonNull Layer layer) { et_title.clearFocus(); } }); @@ -298,165 +381,90 @@ private void dismissQuickDialog() { } private void showMenuDialog() { - WebMenuDialog.show(getContext(), isCollect(), new WebMenuDialog.OnMenuClickListener() { - @Override - public void onShareArticle() { - ShareArticleActivity.start(getContext(), mWebHolder.getTitle(), mWebHolder.getUrl()); - } - - @Override - public void onCollect() { - toggleCollect(); - } - - @Override - public void onReadLater() { - if (mRealmHelper != null) { - mRealmHelper.add(mWebHolder.getTitle(), mWebHolder.getUrl()); - ToastMaker.showShort("已加入稍后阅读"); - } - } - - @Override - public void onHome() { - int step = 0; - while (true) { - if (mWebHolder.canGoBackOrForward(step - 1)) { - step--; - } else { - break; + WebMenuDialog.show(getContext(), mWebHolder.getUrl(), + isCollect(), isReadLater(), + new WebMenuDialog.OnMenuClickListener() { + @Override + public void onShareArticle() { + ShareArticleActivity.start(getContext(), mWebHolder.getTitle(), mWebHolder.getUrl()); } - } - mWebHolder.goBackOrForward(step); - } - - @Override - public void onRefresh() { - mWebHolder.reload(); - } - @Override - public void onCloseActivity() { - finish(); - } - - @Override - public void onShare() { - new QrcodeShareDialog(getContext(), mWebHolder.getUrl(), mWebHolder.getTitle(), new QrcodeShareDialog.OnShareClickListener() { @Override - public void onSave(Bitmap bitmap) { - saveQrcodeGallery(bitmap); + public void onCollect() { + toggleCollect(); } @Override - public void onShare(Bitmap bitmap) { - shareBitmap(bitmap); + public void onReadLater() { + if (isReadLater()) { + presenter.deleteReadLater(mWebHolder.getUrl()); + } else { + presenter.addReadLater(mWebHolder.getUrl(), mWebHolder.getTitle()); + } } - }).show(); - } - }); - } - - private void shareBitmap(final Bitmap bitmap) { - ShareUtils.shareBitmap(getContext(), bitmap); - } - - private void saveQrcodeGallery(final Bitmap bitmap) { - mRuntimeRequester = PermissionUtils.request(new RequestListener() { - @Override - public void onSuccess() { - presenter.saveGallery(bitmap, "wanandroid_article_qrcode_" + MD5Coder.encode(mWebHolder.getUrl()) + "_" + System.currentTimeMillis()); - } - - @Override - public void onFailed() { - } - }, getContext(), REQ_CODE_PERMISSION, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (mRuntimeRequester != null) { - mRuntimeRequester.onActivityResult(requestCode); - } - } - - private void setTitle() { - et_title.setTag(mWebHolder.getUrl()); - if (!TextUtils.isEmpty(mWebHolder.getTitle())) { - et_title.setText(mWebHolder.getTitle()); - } else { - et_title.setText(mWebHolder.getUrl()); - } - } - private void setCollect() { - String url = mWebHolder.getUrl(); - boolean contains = false; - for (CollectArticleEntity entity : mCollectedList) { - if (TextUtils.equals(entity.getUrl(), url)) { - contains = true; - break; - } - } - cv_collect.setChecked(contains, true); - } - - @Override - protected void loadData() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - WebView.enableSlowWholeDocumentDraw(); - } - mWebHolder = WebHolder.with(this, wc) - .setOnPageTitleCallback(new WebHolder.OnPageTitleCallback() { @Override - public void onReceivedTitle(@NonNull String title) { - setTitle(); + public void onHome() { + int step = 0; + while (mWebHolder.canGoBackOrForward(step - 1)) step--; + mWebHolder.goBackOrForward(step); } - }) - .setOnPageLoadCallback(new WebHolder.OnPageLoadCallback() { + @Override - public void onPageStarted() { - et_title.clearFocus(); + public void onRefresh() { + mWebHolder.reload(); } @Override - public void onPageFinished() { - if (!GuideSPUtils.getInstance().isWebGuideShown()) { - if (mWebGuideDialog == null) { - mWebGuideDialog = new WebGuideDialog(getContext()); - mWebGuideDialog.show(); - } - } + public void onGoTop() { + mWebHolder.goTop(); } - }) - .setOnHistoryUpdateCallback(new WebHolder.OnHistoryUpdateCallback() { + @Override - public void onHistoryUpdate(boolean isReload) { - setCollect(); - if (mWebHolder.canGoBack()) { - iv_back.setImageResource(R.drawable.ic_back); - } else { - iv_back.setImageResource(R.drawable.ic_close); - } - switchIconEnable(iv_forward, mWebHolder.canGoForward()); + public void onCloseActivity() { + finish(); } - }) - .setOnPageProgressCallback(new WebHolder.OnPageProgressCallback() { + @Override - public void onShowProgress() { + public void onShare() { + mWebHolder.getShareInfo(new WebHolder.OnShareInfoCallback() { + @Override + public void onShareInfo(@NonNull String url, + @NonNull List covers, + @NonNull String title, + @NonNull String desc) { + new ArticleShareDialog(getContext(), covers, title, desc, url).show(); + } + }); } @Override - public void onProgressChanged(int progress) { + public void onOpenInBrowser() { + IntentUtils.openBrowser(getContext(), mUrl); } @Override - public void onHideProgress() { + public void onListen() { + startListen(); } }); - mWebHolder.loadUrl(mUrl); + } + + private void updateTitle() { + et_title.setTag(mWebHolder.getUrl()); + if (et_title.hasFocus()) { + et_title.setText(mWebHolder.getUrl()); + } else { + if (!TextUtils.isEmpty(mWebHolder.getTitle())) { + et_title.setText(mWebHolder.getTitle()); + } else { + et_title.setText(mWebHolder.getUrl()); + } + } + } + + private void resetCollect() { + cv_collect.setChecked(isCollect(), true); } private void switchIconEnable(View view, boolean enable) { @@ -483,10 +491,7 @@ protected void onResume() { @Override protected void onDestroy() { - mWebHolder.onDestroy(); - if (mRealmHelper != null) { - mRealmHelper.destroy(); - } + mWebHolder.onDestroy(true); super.onDestroy(); } @@ -499,42 +504,33 @@ public boolean onKeyDown(int keyCode, KeyEvent event) { } private boolean isCollect() { - String url = mWebHolder.getUrl(); - boolean contains = false; - for (CollectArticleEntity entity : mCollectedList) { - if (TextUtils.equals(entity.getUrl(), url)) { - contains = true; - break; - } - } - return contains; + CollectArticleEntity entity = findCollected(); + if (entity == null) return false; + return entity.isCollect(); } - private void toggleCollect() { - if (isCollect()) { - uncollect(); - } else { - collect(); - } + private boolean isReadLater() { + ReadLaterModel entity = findReadLater(); + return entity != null; } - private void collect() { + private CollectArticleEntity findCollected() { String url = mWebHolder.getUrl(); - boolean contains = false; - for (CollectArticleEntity entity : mCollectedList) { - if (TextUtils.equals(entity.getUrl(), url)) { - contains = true; - break; - } - } - if (contains) { - setCollect(); - return; - } + return presenter.findCollected(url); + } + + private ReadLaterModel findReadLater() { + String url = mWebHolder.getUrl(); + return presenter.findReadLater(url); + } + + private CollectArticleEntity newCollectArticleEntity() { CollectArticleEntity entity = new CollectArticleEntity(); entity.setCollect(false); + String url = mWebHolder.getUrl(); entity.setUrl(url); if (TextUtils.equals(url, mUrl)) { + entity.setCollectId(mCollectId); if (mArticleId > 0) { entity.setArticleId(mArticleId); } else { @@ -544,46 +540,105 @@ private void collect() { } else { entity.setTitle(mWebHolder.getTitle()); } + return entity; + } + + private void toggleCollect() { + if (isCollect()) { + uncollect(); + } else { + collect(); + } + } + + private void collect() { + CollectArticleEntity entity = findCollected(); + if (entity != null) { + if (entity.isCollect()) { + resetCollect(); + return; + } + } + if (entity == null) { + entity = newCollectArticleEntity(); + } presenter.collect(entity); } private void uncollect() { - String url = mWebHolder.getUrl(); - CollectArticleEntity collectArticleEntity = null; - for (CollectArticleEntity entity : mCollectedList) { - if (TextUtils.equals(entity.getUrl(), url)) { - collectArticleEntity = entity; - break; - } + CollectArticleEntity entity = findCollected(); + if (entity == null) { + resetCollect(); + return; } - if (collectArticleEntity == null) { - setCollect(); + if (!entity.isCollect()) { + resetCollect(); return; } - presenter.uncollect(collectArticleEntity); + presenter.uncollect(entity); + } + + private void startListen() { + mWebHolder.getHtml(new WebHolder.OnHtmlCallback() { + @Override + public void onHtml(@NonNull String url, @NonNull String html) { + if (!TextUtils.equals(mWebHolder.getUrl(), url)) { + return; + } + if (isFinishing() || isDestroyed()) { + return; + } + mWebHolder.getShareInfo(new WebHolder.OnShareInfoCallback() { + @Override + public void onShareInfo(@NonNull String url, @NonNull List covers, @NonNull String title, @NonNull String desc) { + if (isFinishing() || isDestroyed()) { + return; + } + String cover = null; + for (int i = 0; i < covers.size(); i++) { + if (!TextUtils.isEmpty(covers.get(i))) { + cover = covers.get(i); + break; + } + } + final TtsSource source = new TtsSource(url, cover, title, html); + final TtsManager ttsm = TtsManager.getInstance(WebActivity.this); + if (!ttsm.isSpeaking()) { + ttsm.playSource(source); + } else { + ttsm.addSource(source); + } + } + }); + } + }); } @Override public void collectSuccess(CollectArticleEntity entity) { - mCollectedList.add(entity); - setCollect(); + resetCollect(); } @Override public void collectFailed(String msg) { ToastMaker.showShort(msg); - setCollect(); + resetCollect(); } @Override public void uncollectSuccess(CollectArticleEntity entity) { - mCollectedList.remove(entity); - setCollect(); + resetCollect(); } @Override public void uncollectFailed(String msg) { ToastMaker.showShort(msg); - setCollect(); + resetCollect(); + } + + @Override + public void isAddedReadLaterSuccess(ReadLaterModel data) { + if (TextUtils.equals(data.getLink(), mWebHolder.getUrl())) { + } } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/activity/theme/MainActivities.kt b/app/src/main/java/per/goweii/wanandroid/module/main/activity/theme/MainActivities.kt new file mode 100644 index 00000000..19a656b8 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/activity/theme/MainActivities.kt @@ -0,0 +1,8 @@ +package per.goweii.wanandroid.module.main.activity.theme + +import per.goweii.wanandroid.module.main.activity.MainActivity + +class MainActivityRed : MainActivity() +class MainActivityGold : MainActivity() +class MainActivityPink : MainActivity() +class MainActivityGreen : MainActivity() \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/adapter/ArticleAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/main/adapter/ArticleAdapter.java index 53ed13ce..a20e063f 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/adapter/ArticleAdapter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/adapter/ArticleAdapter.java @@ -1,44 +1,195 @@ package per.goweii.wanandroid.module.main.adapter; +import android.graphics.Outline; import android.text.Html; import android.text.TextUtils; import android.view.View; +import android.view.ViewOutlineProvider; import android.widget.ImageView; import android.widget.TextView; -import com.chad.library.adapter.base.BaseQuickAdapter; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.chad.library.adapter.base.BaseMultiItemQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; +import com.chad.library.adapter.base.entity.MultiItemEntity; +import com.google.android.gms.ads.nativead.NativeAd; +import com.google.android.gms.ads.nativead.NativeAdView; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; import per.goweii.basic.utils.StringUtils; import per.goweii.basic.utils.listener.OnClickListener2; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.RvItemArticleAdBinding; +import per.goweii.wanandroid.event.CollectionEvent; import per.goweii.wanandroid.module.home.activity.UserPageActivity; -import per.goweii.wanandroid.module.main.activity.WebActivity; +import per.goweii.wanandroid.module.knowledge.activity.KnowledgeArticleActivity; import per.goweii.wanandroid.module.main.model.ArticleBean; +import per.goweii.wanandroid.module.main.model.NativeAdBean; +import per.goweii.wanandroid.utils.ArticleAndAdDiffCallback; import per.goweii.wanandroid.utils.ImageLoader; +import per.goweii.wanandroid.utils.UrlOpenUtils; +import per.goweii.wanandroid.utils.web.cache.HtmlCacheManager; +import per.goweii.wanandroid.widget.BravhLoadMoreView; import per.goweii.wanandroid.widget.CollectView; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class ArticleAdapter extends BaseQuickAdapter { +public class ArticleAdapter extends BaseMultiItemQuickAdapter { + public static final int ITEM_TYPE_ARTICLE = 0; + public static final int ITEM_TYPE_AD = 1; private OnItemChildViewClickListener mOnItemChildViewClickListener = null; public ArticleAdapter() { - super(R.layout.rv_item_article); + super(null); + addItemType(ITEM_TYPE_ARTICLE, getArticleLayoutId()); + addItemType(ITEM_TYPE_AD, R.layout.rv_item_article_ad); + setLoadMoreView(new BravhLoadMoreView()); + } + + protected int getArticleLayoutId() { + return R.layout.rv_item_article; } public void setOnItemChildViewClickListener(OnItemChildViewClickListener onItemChildViewClickListener) { mOnItemChildViewClickListener = onItemChildViewClickListener; } + public List getArticleData() { + List beans = new ArrayList<>(); + List data = getData(); + for (int i = 0; i < data.size(); i++) { + MultiItemEntity item = data.get(i); + if (item.getItemType() == ITEM_TYPE_ARTICLE) { + beans.add((ArticleBean) item); + } + } + return beans; + } + + public List getNativeAdData() { + List beans = new ArrayList<>(); + List data = getData(); + for (int i = 0; i < data.size(); i++) { + MultiItemEntity item = data.get(i); + if (item.getItemType() == ITEM_TYPE_AD) { + beans.add((NativeAdBean) item); + } + } + return beans; + } + + public void setArticleData(@Nullable List data) { + if (data == null || data.isEmpty()) { + setNewData(null, false); + } else { + setNewData(data, true); + } + } + + @Override + public void setNewData(@Nullable List data) { + if (data == null || data.isEmpty()) { + setNewData(null, false); + } else { + setNewData(data, true); + } + } + + public void setNewData(@Nullable List data, boolean useDiff) { + if (useDiff) { + boolean top = getRecyclerView().canScrollVertically(-1); + setNewDiffData(data); + if (!top) getRecyclerView().scrollToPosition(0); + } else { + super.setNewData((List) data); + } + } + + private void setNewDiffData(@Nullable List data) { + setNewDiffData(new ArticleAndAdDiffCallback(data)); + } + + @Override + public void addData(@NonNull Collection newData) { + super.addData(newData); + } + + @Nullable + public ArticleBean getArticleItem(int position) { + MultiItemEntity item = super.getItem(position); + if (item == null) { + return null; + } + if (item.getItemType() == ArticleAdapter.ITEM_TYPE_ARTICLE) { + return (ArticleBean) item; + } + return null; + } + + public void notifyAllUnCollect() { + forEach(new ArticleAdapter.ArticleForEach() { + @Override + public boolean forEach(int dataPos, int adapterPos, ArticleBean bean) { + if (bean.isCollect()) { + bean.setCollect(false); + notifyItemChanged(adapterPos); + } + return false; + } + }); + } + + public void notifyCollectionEvent(CollectionEvent event) { + forEach(new ArticleAdapter.ArticleForEach() { + @Override + public boolean forEach(int dataPos, int adapterPos, ArticleBean bean) { + if (bean.getId() == event.getArticleId()) { + if (bean.isCollect() != event.isCollect()) { + bean.setCollect(event.isCollect()); + notifyItemChanged(adapterPos); + } + return true; + } + return false; + } + }); + } + + public void forEach(@NonNull ArticleForEach articleForEach) { + List list = getData(); + for (int i = 0; i < list.size(); i++) { + MultiItemEntity item = list.get(i); + if (item.getItemType() == ITEM_TYPE_ARTICLE) { + ArticleBean bean = (ArticleBean) item; + if (articleForEach.forEach(i, i + getHeaderLayoutCount(), bean)) { + break; + } + } + } + } + @Override - protected void convert(BaseViewHolder helper, ArticleBean item) { + protected void convert(@NonNull BaseViewHolder helper, MultiItemEntity item) { + switch (helper.getItemViewType()) { + case ITEM_TYPE_ARTICLE: + convertArticle(helper, (ArticleBean) item); + break; + case ITEM_TYPE_AD: + convertAd(helper, (NativeAdBean) item); + break; + } + } + + protected void convertArticle(BaseViewHolder helper, ArticleBean item) { bindArticle(helper.itemView, item, new OnCollectListener() { @Override public void collect(ArticleBean item, CollectView v) { @@ -56,6 +207,90 @@ public void uncollect(ArticleBean item, CollectView v) { }); } + private void convertAd(BaseViewHolder helper, NativeAdBean item) { + NativeAd nativeAd = item.getNativeAd(); + if (nativeAd == null) { + helper.itemView.setVisibility(View.GONE); + return; + } + + helper.itemView.setVisibility(View.VISIBLE); + RvItemArticleAdBinding binding = RvItemArticleAdBinding.bind(helper.itemView); + NativeAdView adView = binding.adView; + + binding.adMedia.setMediaContent(nativeAd.getMediaContent()); + binding.adMedia.setImageScaleType(ImageView.ScaleType.CENTER_CROP); + binding.adMedia.setClipToOutline(true); + final int cornerRadius = (int) mContext.getResources().getDimension(R.dimen.round_radius_small); + binding.adMedia.setOutlineProvider(new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), cornerRadius); + } + }); + adView.setMediaView(binding.adMedia); + + if (TextUtils.isEmpty(nativeAd.getHeadline())) { + binding.adHeadline.setVisibility(View.GONE); + } else { + binding.adHeadline.setVisibility(View.VISIBLE); + binding.adHeadline.setText(nativeAd.getHeadline()); + adView.setHeadlineView(binding.adHeadline); + } + + if (TextUtils.isEmpty(nativeAd.getAdvertiser())) { + binding.adAdvertiser.setVisibility(View.GONE); + } else { + binding.adAdvertiser.setVisibility(View.VISIBLE); + binding.adAdvertiser.setText(nativeAd.getAdvertiser()); + adView.setAdvertiserView(binding.adAdvertiser); + } + + if (TextUtils.isEmpty(nativeAd.getBody())) { + binding.adBody.setVisibility(View.GONE); + } else { + binding.adBody.setVisibility(View.VISIBLE); + binding.adBody.setText(nativeAd.getBody()); + adView.setBodyView(binding.adBody); + } + + if (TextUtils.isEmpty(nativeAd.getPrice())) { + binding.adPrice.setVisibility(View.GONE); + } else { + binding.adPrice.setVisibility(View.VISIBLE); + binding.adPrice.setText(nativeAd.getPrice()); + adView.setPriceView(binding.adPrice); + } + + if (TextUtils.isEmpty(nativeAd.getStore())) { + binding.adStore.setVisibility(View.GONE); + } else { + binding.adStore.setVisibility(View.VISIBLE); + binding.adStore.setText(nativeAd.getStore()); + adView.setStoreView(binding.adStore); + } + + Double starRating = nativeAd.getStarRating(); + if (starRating == null) { + binding.adStars.setVisibility(View.GONE); + } else { + binding.adStars.setVisibility(View.VISIBLE); + binding.adStars.setRating(starRating.floatValue()); + adView.setStarRatingView(binding.adStars); + } + binding.adStars.setVisibility(View.GONE); + + if (TextUtils.isEmpty(nativeAd.getCallToAction())) { + binding.adCallToAction.setVisibility(View.GONE); + } else { + binding.adCallToAction.setVisibility(View.VISIBLE); +// binding.adCallToAction.setText(nativeAd.getCallToAction()); + adView.setCallToActionView(binding.adCallToAction); + } + + adView.setNativeAd(nativeAd); + } + public interface OnItemChildViewClickListener { void onCollectClick(BaseViewHolder helper, CollectView v, int position); } @@ -67,6 +302,7 @@ public interface OnCollectListener { } public static void bindArticle(View view, ArticleBean item, OnCollectListener onCollectListener) { + HtmlCacheManager.INSTANCE.submit(item.getLink()); TextView tv_top = view.findViewById(R.id.tv_top); TextView tv_new = view.findViewById(R.id.tv_new); TextView tv_author = view.findViewById(R.id.tv_author); @@ -91,6 +327,12 @@ public static void bindArticle(View view, ArticleBean item, OnCollectListener on if (item.getTags() != null && item.getTags().size() > 0) { tv_tag.setText(item.getTags().get(0).getName()); tv_tag.setVisibility(View.VISIBLE); + tv_tag.setOnClickListener(new OnClickListener2() { + @Override + public void onClick2(View v) { + KnowledgeArticleActivity.start(v.getContext(), item.getTags().get(0)); + } + }); } else { tv_tag.setVisibility(View.GONE); } @@ -114,10 +356,18 @@ public static void bindArticle(View view, ArticleBean item, OnCollectListener on } tv_chapter_name.setText(Html.fromHtml(formatChapterName(item.getSuperChapterName(), item.getChapterName()))); if (item.isCollect()) { - cv_collect.setChecked(true); + cv_collect.setChecked(true, false); } else { - cv_collect.setChecked(false); + cv_collect.setChecked(false, false); } + tv_chapter_name.setOnClickListener(new OnClickListener2() { + @Override + public void onClick2(View v) { + KnowledgeArticleActivity.start(v.getContext(), + item.getSuperChapterId(), item.getSuperChapterName(), + item.getChapterId()); + } + }); tv_author.setOnClickListener(new OnClickListener2() { @Override public void onClick2(View v) { @@ -127,7 +377,7 @@ public void onClick2(View v) { cv_collect.setOnClickListener(new CollectView.OnClickListener() { @Override public void onClick(CollectView v) { - if (!v.isChecked()) { + if (v.isChecked()) { if (onCollectListener != null) { onCollectListener.collect(item, v); } @@ -141,7 +391,7 @@ public void onClick(CollectView v) { view.setOnClickListener(new OnClickListener2() { @Override public void onClick2(View v) { - WebActivity.start(v.getContext(), item); + UrlOpenUtils.Companion.with(item).open(v.getContext()); } }); } @@ -158,4 +408,8 @@ private static String formatChapterName(String... names) { } return format.toString(); } + + public interface ArticleForEach { + boolean forEach(int dataPos, int adapterPos, ArticleBean bean); + } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/adapter/BookmarkAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/main/adapter/BookmarkAdapter.java new file mode 100644 index 00000000..1917653e --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/adapter/BookmarkAdapter.java @@ -0,0 +1,45 @@ +package per.goweii.wanandroid.module.main.adapter; + +import android.annotation.SuppressLint; +import android.text.Html; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; + +import java.text.SimpleDateFormat; +import java.util.Date; + +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.db.model.ReadLaterModel; + +/** + * @author CuiZhen + * @date 2019/5/15 + * GitHub: https://github.com/goweii + */ +public class BookmarkAdapter extends BaseQuickAdapter { + + private final SimpleDateFormat mSimpleDateFormat; + + @SuppressLint("SimpleDateFormat") + public BookmarkAdapter() { + super(R.layout.rv_item_bookmark); + mSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + + @Override + protected void convert(@NonNull BaseViewHolder helper, ReadLaterModel item) { + if (TextUtils.isEmpty(item.getTitle())) { + helper.setText(R.id.tv_title, item.getLink()); + } else { + helper.setText(R.id.tv_title, Html.fromHtml(item.getTitle())); + } + helper.setText(R.id.tv_title, Html.fromHtml(item.getTitle())); + String time = mSimpleDateFormat.format(new Date(item.getTime())); + helper.setText(R.id.tv_time, time); + helper.addOnClickListener(R.id.rl_top, R.id.tv_delete, R.id.tv_open, R.id.tv_copy); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/adapter/MainTabAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/main/adapter/MainTabAdapter.java new file mode 100644 index 00000000..44bbc588 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/adapter/MainTabAdapter.java @@ -0,0 +1,57 @@ +package per.goweii.wanandroid.module.main.adapter; + +import android.annotation.SuppressLint; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; + +import per.goweii.basic.core.adapter.TabFragmentPagerAdapter; +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.common.Config; +import per.goweii.wanandroid.module.main.model.TabEntity; +import per.goweii.wanandroid.utils.RvScrollTopUtils; + +/** + * @author CuiZhen + * @date 2020/3/22 + */ +public class MainTabAdapter implements TabFragmentPagerAdapter.Page.TabAdapter { + @SuppressLint("SetTextI18n") + @Override + public void onBindData(@NonNull View view, @NonNull TabEntity data, boolean selected) { + ImageView iv_tab_icon = view.findViewById(R.id.iv_tab_icon); + TextView tv_tab_name = view.findViewById(R.id.tv_tab_name); + TextView tv_tab_msg = view.findViewById(R.id.tv_tab_msg); + iv_tab_icon.setImageResource(data.getTabIcon()); + tv_tab_name.setText(data.getTabName()); + if (selected) { + iv_tab_icon.setColorFilter(ResUtils.getThemeColor(iv_tab_icon, R.attr.colorIconMain)); + tv_tab_name.setTextColor(ResUtils.getThemeColor(tv_tab_name, R.attr.colorTextMain)); + } else { + iv_tab_icon.setColorFilter(ResUtils.getThemeColor(iv_tab_icon, R.attr.colorIconThird)); + tv_tab_name.setTextColor(ResUtils.getThemeColor(tv_tab_name, R.attr.colorTextThird)); + } + if (data.getMsgCount() > 0) { + tv_tab_msg.setVisibility(View.VISIBLE); + if (data.getMsgCount() > Config.NOTIFICATION_MAX_SHOW_COUNT) { + tv_tab_msg.setText(Config.NOTIFICATION_MAX_SHOW_COUNT + "+"); + } else { + tv_tab_msg.setText(data.getMsgCount() + ""); + } + } else { + tv_tab_msg.setVisibility(View.GONE); + } + } + + @Override + public void onDoubleTap(Fragment fragment) { + if (fragment instanceof RvScrollTopUtils.ScrollTop) { + RvScrollTopUtils.ScrollTop scrollTop = (RvScrollTopUtils.ScrollTop) fragment; + scrollTop.scrollTop(); + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/adapter/WebDialogPagerAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/main/adapter/WebDialogPagerAdapter.java index e3f8c7ef..6bba632f 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/adapter/WebDialogPagerAdapter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/adapter/WebDialogPagerAdapter.java @@ -1,25 +1,24 @@ package per.goweii.wanandroid.module.main.adapter; import android.app.Activity; -import android.support.annotation.NonNull; -import android.support.v4.view.PagerAdapter; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.viewpager.widget.PagerAdapter; + import java.util.List; import per.goweii.wanandroid.R; import per.goweii.wanandroid.module.main.model.ArticleBean; -import per.goweii.wanandroid.utils.WebHolder; -import per.goweii.wanandroid.widget.WebContainer; +import per.goweii.wanandroid.utils.web.WebHolder; +import per.goweii.wanandroid.utils.web.view.WebContainer; /** * @author CuiZhen * @date 2019/8/31 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WebDialogPagerAdapter extends PagerAdapter { @@ -60,7 +59,7 @@ public void resumeAndPauseOthersAgentWeb(int pos) { } } - public void pauseAllAgentWeb() { + public void pauseAllWeb() { for (int i = 0; i < mWebs.size(); i++) { WebHolder web = mWebs.valueAt(i); if (web == null) { @@ -70,16 +69,16 @@ public void pauseAllAgentWeb() { } } - public void destroyAllAgentWeb() { + public void destroyAllWeb() { for (int i = 0; i < mWebs.size(); i++) { WebHolder web = mWebs.valueAt(i); if (web != null) { - web.onDestroy(); + web.onDestroy(true); } } } - public ArticleBean getBean(int pos) { + public ArticleBean getArticleBean(int pos) { int topUrlCount = mTopUrls == null ? 0 : mTopUrls.size(); if (pos < topUrlCount) { return mTopUrls.get(pos); @@ -102,18 +101,28 @@ public int getCount() { @NonNull @Override public Object instantiateItem(@NonNull ViewGroup container, int position) { - final ArticleBean data = getBean(position); + final ArticleBean bean = getArticleBean(position); View rootView = LayoutInflater.from(container.getContext()).inflate(R.layout.dialog_web_vp_item, container, false); WebContainer wc = rootView.findViewById(R.id.dialog_web_wc); wc.setOnDoubleClickListener(new WebContainer.OnDoubleClickListener() { @Override public void onDoubleClick(float x, float y) { - if (mOnDoubleClickListener != null) { - mOnDoubleClickListener.onDoubleClick(data); + if (bean != null && mOnDoubleClickListener != null) { + mOnDoubleClickListener.onDoubleClick(bean); } } }); - WebHolder web = WebHolder.with(mActivity, wc).loadUrl(data.getLink()); + WebHolder web = WebHolder.with(mActivity, wc) + .setAllowOpenOtherApp(false) + .setAllowOpenDownload(false) + .setAllowRedirect(false) + .setOverrideUrlInterceptor(new WebHolder.OverrideUrlInterceptor() { + @Override + public boolean onOverrideUrl(String url) { + return true; + } + }) + .loadUrl(bean != null ? bean.getLink() : ""); mWebs.put(position, web); container.addView(rootView); return rootView; @@ -122,7 +131,7 @@ public void onDoubleClick(float x, float y) { @Override public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) { WebHolder web = mWebs.get(position); - web.onDestroy(); + web.onDestroy(false); mWebs.remove(position); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/contract/ArticleListContract.kt b/app/src/main/java/per/goweii/wanandroid/module/main/contract/ArticleListContract.kt new file mode 100644 index 00000000..ac9ae3d0 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/contract/ArticleListContract.kt @@ -0,0 +1,16 @@ +package per.goweii.wanandroid.module.main.contract + +import per.goweii.basic.core.base.BasePresenter +import per.goweii.basic.core.base.BaseView + +/** + * @author CuiZhen + * @date 2020/3/22 + */ +interface ArticleListView : BaseView { + +} + +class ArticleListPresenter : BasePresenter() { + +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/contract/BookmarkContract.kt b/app/src/main/java/per/goweii/wanandroid/module/main/contract/BookmarkContract.kt new file mode 100644 index 00000000..78070010 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/contract/BookmarkContract.kt @@ -0,0 +1,41 @@ +package per.goweii.wanandroid.module.main.contract + +import per.goweii.basic.core.base.BasePresenter +import per.goweii.basic.core.base.BaseView +import per.goweii.wanandroid.db.executor.ReadLaterExecutor +import per.goweii.wanandroid.db.model.ReadLaterModel + +/** + * @author CuiZhen + * @date 2020/3/22 + */ +interface BookmarkView : BaseView { + fun getBookmarkListSuccess(list: List) + fun getBookmarkListFailed() +} + +class BookmarkPresenter : BasePresenter() { + private var mReadLaterExecutor: ReadLaterExecutor? = null + + override fun attach(baseView: BookmarkView) { + super.attach(baseView) + mReadLaterExecutor = ReadLaterExecutor() + } + + override fun detach() { + if (mReadLaterExecutor != null) mReadLaterExecutor!!.destroy() + super.detach() + } + + fun getList(offset: Int, perPageCount: Int) { + mReadLaterExecutor?.getList(offset, perPageCount, { + if (isAttach) { + baseView.getBookmarkListSuccess(it) + } + }, { + if (isAttach) { + baseView.getBookmarkListFailed() + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/AdvertDialog.java b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/AdvertDialog.java new file mode 100644 index 00000000..984037f9 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/AdvertDialog.java @@ -0,0 +1,274 @@ +package per.goweii.wanandroid.module.main.dialog; + +import android.animation.Animator; +import android.animation.AnimatorSet; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.view.animation.LinearInterpolator; +import android.widget.ImageView; +import android.widget.RelativeLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +import me.zhanghai.android.materialprogressbar.MaterialProgressBar; +import per.goweii.anylayer.Layer; +import per.goweii.anylayer.dialog.DialogLayer; +import per.goweii.anylayer.utils.AnimatorHelper; +import per.goweii.basic.core.glide.GlideHelper; +import per.goweii.basic.utils.ResUtils; +import per.goweii.basic.utils.display.DisplayInfoUtils; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.module.home.model.ImageBean; +import per.goweii.wanandroid.module.main.model.AdvertBean; +import per.goweii.wanandroid.utils.ImageLoader; +import per.goweii.wanandroid.utils.router.Router; +import per.goweii.wanandroid.widget.ParallaxStackLayout; + +public class AdvertDialog extends DialogLayer implements Layer.AnimatorCreator { + private AdvertBean advertBean; + private ValueAnimator closeAnimator = null; + + private Integer mActivityRequestedOrientation = null; + + public AdvertDialog(@NonNull Context context) { + super(context); + setBackgroundDimDefault(); + setCancelableOnClickKeyBack(false); + setCancelableOnTouchOutside(false); + setContentView(R.layout.dialog_advert); + setContentAnimator(this); + addOnClickToDismissListener(R.id.dialog_advert_iv_close); + mActivityRequestedOrientation = getActivity().getRequestedOrientation(); + getActivity().setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + } + + public AdvertDialog setAdvertBean(AdvertBean advertBean) { + this.advertBean = advertBean; + return this; + } + + @Nullable + @Override + public Animator createInAnimator(@NonNull View target) { + View psl = requireView(R.id.dialog_advert_psl); + View rl_close = requireView(R.id.dialog_advert_rl_close); + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.playTogether( + AnimatorHelper.createAlphaInAnim(psl), + AnimatorHelper.createZoomAlphaInAnim(rl_close) + ); + return animatorSet; + } + + @Nullable + @Override + public Animator createOutAnimator(@NonNull View target) { + View psl = requireView(R.id.dialog_advert_psl); + View rl_close = requireView(R.id.dialog_advert_rl_close); + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.playTogether( + AnimatorHelper.createAlphaOutAnim(psl), + AnimatorHelper.createZoomAlphaOutAnim(rl_close) + ); + return animatorSet; + } + + @Override + public void onAttach() { + super.onAttach(); + MaterialProgressBar pb_close = requireView(R.id.dialog_advert_pb_close); + if (advertBean.getDuration() > 0) { + pb_close.setVisibility(View.VISIBLE); + pb_close.setMax(advertBean.getDuration()); + pb_close.setProgress(advertBean.getDuration()); + } else { + pb_close.setVisibility(View.INVISIBLE); + } + ParallaxStackLayout psl = requireView(R.id.dialog_advert_psl); + psl.removeAllViews(); + if (advertBean.getImages() == null || advertBean.getImages().isEmpty()) { + fillBySingleImage(psl, advertBean.getImage()); + } else { + fillByMultiImages(psl, advertBean.getImages()); + } + } + + @Override + protected void onPostShow() { + super.onPostShow(); + if (advertBean.getDuration() > 0) { + startCloseAnimator(); + } + } + + @Override + protected void onPreDismiss() { + super.onPreDismiss(); + if (closeAnimator != null) { + closeAnimator.cancel(); + } + } + + @Override + public void onDetach() { + super.onDetach(); + if (mActivityRequestedOrientation != null) { + getActivity().setRequestedOrientation(mActivityRequestedOrientation); + } + } + + private void handleRouteTo() { + if (TextUtils.isEmpty(advertBean.getRoute())) { + return; + } + Router.routeTo(advertBean.getRoute()); + dismiss(); + } + + private void fillBySingleImage(ParallaxStackLayout psl, String url) { + RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) psl.getLayoutParams(); + layoutParams.addRule(RelativeLayout.ABOVE, R.id.dialog_advert_rl_close); + ImageView imageView = new ImageView(getActivity()); + imageView.setAdjustViewBounds(true); + imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); + int p32 = (int) ResUtils.getDimens(R.dimen.margin_middle); + int p16 = (int) ResUtils.getDimens(R.dimen.margin_def); + imageView.setPadding(p32, p32, p32, p16); + ImageLoader.roundImage(imageView, url, (int) ResUtils.getDimens(R.dimen.round_radius)); + psl.addView(imageView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + + imageView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + handleRouteTo(); + } + }); + } + + @SuppressLint("RtlHardcoded") + private void fillByMultiImages(ParallaxStackLayout psl, List images) { + for (ImageBean imageBean : images) { + ImageView imageView = new ImageView(getActivity()); + ParallaxStackLayout.LayoutParams layoutParams = new ParallaxStackLayout.LayoutParams(0, 0); + if (imageBean.getWidth() == ParallaxStackLayout.LayoutParams.MATCH_PARENT) { + layoutParams.width = ParallaxStackLayout.LayoutParams.MATCH_PARENT; + } else if (imageBean.getWidth() == ParallaxStackLayout.LayoutParams.WRAP_CONTENT) { + layoutParams.width = ParallaxStackLayout.LayoutParams.WRAP_CONTENT; + } else { + layoutParams.width = (int) (0.5F + DisplayInfoUtils.getInstance().dp2px(imageBean.getWidth())); + } + if (imageBean.getHeight() == ParallaxStackLayout.LayoutParams.MATCH_PARENT) { + layoutParams.height = ParallaxStackLayout.LayoutParams.MATCH_PARENT; + } else if (imageBean.getHeight() == ParallaxStackLayout.LayoutParams.WRAP_CONTENT) { + layoutParams.height = ParallaxStackLayout.LayoutParams.WRAP_CONTENT; + } else { + layoutParams.height = (int) (0.5F + DisplayInfoUtils.getInstance().dp2px(imageBean.getHeight())); + } + if ((layoutParams.width == ParallaxStackLayout.LayoutParams.WRAP_CONTENT && + layoutParams.height != ParallaxStackLayout.LayoutParams.WRAP_CONTENT) || + (layoutParams.height == ParallaxStackLayout.LayoutParams.WRAP_CONTENT && + layoutParams.width != ParallaxStackLayout.LayoutParams.WRAP_CONTENT)) { + imageView.setScaleType(ImageView.ScaleType.FIT_XY); + imageView.setAdjustViewBounds(true); + } else if (layoutParams.width == ParallaxStackLayout.LayoutParams.MATCH_PARENT && + layoutParams.height == ParallaxStackLayout.LayoutParams.MATCH_PARENT) { + imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); + imageView.setAdjustViewBounds(false); + } else { + imageView.setScaleType(ImageView.ScaleType.FIT_CENTER); + imageView.setAdjustViewBounds(false); + } + switch (imageBean.getGravity()) { + case "center_top": + layoutParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; + break; + case "center_bottom": + layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL; + break; + case "center_left": + layoutParams.gravity = Gravity.LEFT | Gravity.CENTER_VERTICAL; + break; + case "center_right": + layoutParams.gravity = Gravity.RIGHT | Gravity.CENTER_VERTICAL; + break; + case "top_left": + layoutParams.gravity = Gravity.LEFT | Gravity.TOP; + break; + case "bottom_left": + layoutParams.gravity = Gravity.LEFT | Gravity.BOTTOM; + break; + case "top_right": + layoutParams.gravity = Gravity.RIGHT | Gravity.TOP; + break; + case "bottom_right": + layoutParams.gravity = Gravity.RIGHT | Gravity.BOTTOM; + break; + case "center": + layoutParams.gravity = Gravity.CENTER; + break; + default: + break; + } + layoutParams.leftMargin = (int) (0.5F + DisplayInfoUtils.getInstance().dp2px(imageBean.getMarginLeft())); + layoutParams.rightMargin = (int) (0.5F + DisplayInfoUtils.getInstance().dp2px(imageBean.getMarginRight())); + layoutParams.topMargin = (int) (0.5F + DisplayInfoUtils.getInstance().dp2px(imageBean.getMarginTop())); + layoutParams.bottomMargin = (int) (0.5F + DisplayInfoUtils.getInstance().dp2px(imageBean.getMarginBottom())); + layoutParams.setDeviationX(DisplayInfoUtils.getInstance().dp2px(imageBean.getDeviationX())); + layoutParams.setDeviationY(DisplayInfoUtils.getInstance().dp2px(imageBean.getDeviationY())); + layoutParams.setRotationX(imageBean.getRotationX()); + layoutParams.setRotationY(imageBean.getRotationY()); + layoutParams.setRotationZ(imageBean.getRotationZ()); + psl.addView(imageView, layoutParams); + GlideHelper.with(getActivity()).load(imageBean.getUrl()).into(imageView); + + psl.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + handleRouteTo(); + } + }); + } + } + + private void startCloseAnimator() { + closeAnimator = ValueAnimator.ofInt(advertBean.getDuration(), 0); + closeAnimator.setInterpolator(new LinearInterpolator()); + closeAnimator.setDuration(advertBean.getDuration()); + closeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + MaterialProgressBar pb_close = requireView(R.id.dialog_advert_pb_close); + pb_close.setMax(advertBean.getDuration()); + pb_close.setProgress((int) animation.getAnimatedValue()); + } + }); + closeAnimator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + dismiss(); + } + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + }); + closeAnimator.start(); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/ArticleShareDialog.kt b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/ArticleShareDialog.kt new file mode 100644 index 00000000..847d28d6 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/ArticleShareDialog.kt @@ -0,0 +1,253 @@ +package per.goweii.wanandroid.module.main.dialog + +import android.Manifest +import android.animation.Animator +import android.animation.AnimatorSet +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.Canvas +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.RelativeLayout +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.isVisible +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.LinearSnapHelper +import androidx.recyclerview.widget.RecyclerView +import per.goweii.anylayer.Layer +import per.goweii.anylayer.dialog.DialogLayer +import per.goweii.anylayer.utils.AnimatorHelper +import per.goweii.anylayer.utils.Utils +import per.goweii.anypermission.RequestListener +import per.goweii.anypermission.RuntimeRequester +import per.goweii.basic.core.glide.GlideHelper +import per.goweii.basic.core.permission.PermissionUtils +import per.goweii.basic.ui.toast.ToastMaker +import per.goweii.basic.utils.ResUtils +import per.goweii.basic.utils.ShareUtils +import per.goweii.basic.utils.bitmap.BitmapUtils +import per.goweii.codex.encoder.CodeEncoder +import per.goweii.codex.processor.zxing.ZXingEncodeQRCodeProcessor +import per.goweii.wanandroid.R +import per.goweii.wanandroid.utils.ImageLoader +import java.util.* + +class ArticleShareDialog( + context: Context, + covers: List?, + private var title: String, + private var desc: String, + private var url: String +) : DialogLayer(context), Layer.AnimatorCreator { + private val reqCodePermission = 99 + private var runtimeRequester: RuntimeRequester? = null + private var permissionFragment: PermissionFragment? = null + + private val allCovers = arrayListOf().apply { + add(null) + covers?.filter { !it.isNullOrBlank() } + ?.forEach { add(it) } + } + + private val hasCover: Boolean + get() = null != allCovers.find { !it.isNullOrBlank() } + + private var curCover: String? = null + + override fun onCreate() { + setContentView(R.layout.dialog_article_share) + setBackgroundDimDefault() + setContentAnimator(this) + addOnClickToDismissListener(R.id.dialog_article_share_ll_card) + addOnClickToDismissListener({ _, _ -> + createCardBitmap()?.let { saveBitmap(it) } + }, R.id.dialog_article_share_ll_album) + addOnClickToDismissListener({ _, _ -> + createCardBitmap()?.let { shareBitmap(it) } + }, R.id.dialog_article_share_ll_share) + super.onCreate() + } + + override fun createInAnimator(target: View): Animator { + val srl_content = requireView(R.id.dialog_article_share_srl_content) + val ll_bottom = requireView(R.id.dialog_article_share_ll_bottom) + val animator = AnimatorSet() + animator.playTogether( + AnimatorHelper.createZoomAlphaInAnim(srl_content), + AnimatorHelper.createBottomInAnim(ll_bottom) + ) + return animator + } + + override fun createOutAnimator(target: View): Animator { + val srl_content = requireView(R.id.dialog_article_share_srl_content) + val ll_bottom = requireView(R.id.dialog_article_share_ll_bottom) + val animator = AnimatorSet() + animator.playTogether( + AnimatorHelper.createZoomAlphaOutAnim(srl_content), + AnimatorHelper.createBottomOutAnim(ll_bottom) + ) + return animator + } + + override fun onAttach() { + super.onAttach() + val rv_covers = requireView(R.id.dialog_article_share_rv_covers) + val iv_qrcode = requireView(R.id.dialog_article_share_iv_qrcode) + val tv_title = requireView(R.id.dialog_article_share_tv_title) + val tv_desc = requireView(R.id.dialog_article_share_tv_desc) + + if (hasCover) { + rv_covers.isVisible = true + rv_covers.layoutManager = LinearLayoutManager(rv_covers.context, LinearLayoutManager.HORIZONTAL, false) + rv_covers.adapter = CoverAdapter() + LinearSnapHelper().attachToRecyclerView(rv_covers) + } else { + rv_covers.isVisible = false + } + + tv_title.text = title + tv_desc.text = desc + CodeEncoder(ZXingEncodeQRCodeProcessor()).encode(url, { + iv_qrcode.setImageBitmap(it) + }, {}) + + bindCurCover() + } + + override fun onPreShow() { + super.onPreShow() + val ll_card = requireView(R.id.dialog_article_share_ll_card) + val ll_bottom = requireView(R.id.dialog_article_share_ll_bottom) + Utils.setViewPaddingBottom(ll_card, ll_bottom.height) + } + + override fun onDetach() { + super.onDetach() + permissionFragment?.removeFromActivity(activity) + } + + private fun bindCurCover() { + val iv_cover = requireView(R.id.dialog_article_share_iv_cover) + if (curCover.isNullOrBlank()) { + iv_cover.isVisible = false + } else { + GlideHelper.with(iv_cover.context).asBitmap().load(curCover).getBitmap { + iv_cover.isVisible = true + iv_cover.setImageBitmap(it) + } + } + } + + private fun createCardBitmap(): Bitmap? { + val view = requireView(R.id.dialog_article_share_fl_card) + val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.RGB_565) + val canvas = Canvas(bitmap) + view.draw(canvas) + return bitmap + } + + private fun shareBitmap(bitmap: Bitmap) { + ShareUtils.shareBitmap(activity, bitmap) + } + + private fun saveBitmap(bitmap: Bitmap) { + runtimeRequester = PermissionUtils.request(object : RequestListener { + override fun onSuccess() { + if (BitmapUtils.saveGallery(bitmap, "wanandroid_app_" + System.currentTimeMillis())) { + ToastMaker.showShort(activity.getString(R.string.save_successful)) + } else { + ToastMaker.showShort(activity.getString(R.string.save_failed)) + } + bitmap.recycle() + } + + override fun onFailed() { + ToastMaker.showShort(activity.getString(R.string.authorization_failed)) + bitmap.recycle() + } + }, activity, reqCodePermission, Manifest.permission.WRITE_EXTERNAL_STORAGE) + permissionFragment = PermissionFragment.findOrAddToActivity(activity) + permissionFragment?.onActivityResult = { + runtimeRequester?.onActivityResult(it) + } + } + + private inner class CoverAdapter : RecyclerView.Adapter() { + override fun getItemCount(): Int = allCovers.size + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CoverHolder { + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.rv_item_article_share_cover, parent, false) + return CoverHolder(view) + } + + override fun onBindViewHolder(holder: CoverHolder, position: Int) { + val url = allCovers[position] + if (!TextUtils.isEmpty(url)) { + holder.iv_none.isVisible = false + ImageLoader.image(holder.iv_cover, url) + } else { + holder.iv_none.isVisible = true + holder.iv_cover.setImageBitmap(null) + } + if (TextUtils.equals(url, curCover)) { + holder.rl_cover.setBackgroundResource(R.drawable.bg_solid_round_color_main_radius_def) + } else { + holder.rl_cover.setBackgroundResource(R.color.color_transparent) + } + } + + private inner class CoverHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val rl_cover = itemView.findViewById(R.id.rl_cover) + val iv_cover = itemView.findViewById(R.id.iv_cover) + val iv_none = itemView.findViewById(R.id.iv_none) + + init { + iv_none.setColorFilter(ResUtils.getThemeColor(activity, R.attr.colorOnSurface)) + rl_cover.setOnClickListener { + curCover = allCovers[adapterPosition] + notifyDataSetChanged() + bindCurCover() + } + } + } + } + + private class PermissionFragment : Fragment() { + var onActivityResult: ((requestCode: Int) -> Unit)? = null + + companion object { + fun findOrAddToActivity(activity: Activity): PermissionFragment { + activity as AppCompatActivity + activity.supportFragmentManager.findFragmentByTag(PermissionFragment::class.java.name) + ?.let { return it as PermissionFragment } + return PermissionFragment().also { fg -> + activity.supportFragmentManager.beginTransaction().apply { + add(fg, PermissionFragment::class.java.name) + }.commitNowAllowingStateLoss() + } + } + } + + fun removeFromActivity(activity: Activity) { + activity as AppCompatActivity + activity.supportFragmentManager.beginTransaction().apply { + remove(this@PermissionFragment) + }.commitNowAllowingStateLoss() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + onActivityResult?.invoke(requestCode) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/CardShareDialog.kt b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/CardShareDialog.kt new file mode 100644 index 00000000..6abc6911 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/CardShareDialog.kt @@ -0,0 +1,167 @@ +package per.goweii.wanandroid.module.main.dialog + +import android.Manifest +import android.animation.Animator +import android.animation.AnimatorSet +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.graphics.Canvas +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.Fragment +import per.goweii.anylayer.Layer +import per.goweii.anylayer.dialog.DialogLayer +import per.goweii.anylayer.utils.AnimatorHelper +import per.goweii.anypermission.RequestListener +import per.goweii.anypermission.RuntimeRequester +import per.goweii.basic.core.permission.PermissionUtils +import per.goweii.basic.ui.toast.ToastMaker +import per.goweii.basic.utils.ShareUtils +import per.goweii.basic.utils.bitmap.BitmapUtils +import per.goweii.wanandroid.R + +class CardShareDialog( + context: Context, + private var layoutId: Int, + private var bindData: (View) -> Unit +) : DialogLayer(context), Layer.AnimatorCreator { + private val reqCodePermission = 99 + private var runtimeRequester: RuntimeRequester? = null + private var permissionFragment: PermissionFragment? = null + + init { + setContentView(R.layout.dialog_card_share) + setBackgroundDimDefault() + setContentAnimator(this) + addOnClickToDismissListener(R.id.dialog_card_share_rl_content) + addOnClickToDismissListener(R.id.dialog_card_share_fl_card) + addOnClickToDismissListener(R.id.dialog_card_share_srl_card) + addOnClickToDismissListener(R.id.dialog_card_share_nsv_card) + addOnClickToDismissListener(R.id.dialog_card_share_ll_card) + addOnClickToDismissListener(R.id.dialog_card_share_ll_btn) + addOnClickToDismissListener(R.id.dialog_card_share_ll_album) + addOnClickToDismissListener(R.id.dialog_card_share_ll_share) + addOnClickToDismissListener({ _, _ -> + createCardBitmap()?.let { saveBitmap(it) } + }, R.id.dialog_card_share_iv_album) + addOnClickToDismissListener({ _, _ -> + createCardBitmap()?.let { shareBitmap(it) } + }, R.id.dialog_card_share_iv_share) + } + + override fun createInAnimator(target: View): Animator { + val nsv_card = requireView(R.id.dialog_card_share_srl_card) + val ll_btn = requireView(R.id.dialog_card_share_ll_btn) + val ll_album = requireView(R.id.dialog_card_share_ll_album) + val ll_share = requireView(R.id.dialog_card_share_ll_share) + val animator = AnimatorSet() + animator.playTogether( + AnimatorHelper.createAlphaInAnim(nsv_card), + AnimatorHelper.createAlphaInAnim(ll_btn), + AnimatorHelper.createZoomAlphaInAnim(ll_album, 0.6F), + AnimatorHelper.createZoomAlphaInAnim(ll_share, 0.6F) + ) + return animator + } + + override fun createOutAnimator(target: View): Animator { + val nsv_card = requireView(R.id.dialog_card_share_srl_card) + val ll_btn = requireView(R.id.dialog_card_share_ll_btn) + val ll_album = requireView(R.id.dialog_card_share_ll_album) + val ll_share = requireView(R.id.dialog_card_share_ll_share) + val animator = AnimatorSet() + animator.playTogether( + AnimatorHelper.createAlphaOutAnim(nsv_card), + AnimatorHelper.createAlphaOutAnim(ll_btn), + AnimatorHelper.createZoomAlphaOutAnim(ll_album, 0.6F), + AnimatorHelper.createZoomAlphaOutAnim(ll_share, 0.6F) + ) + return animator + } + + override fun onAttach() { + super.onAttach() + val fl_card = requireView(R.id.dialog_card_share_ll_card) + val view = LayoutInflater.from(activity).inflate(layoutId, fl_card, false) + view.isClickable = true + fl_card.removeAllViews() + fl_card.addView(view) + bindData.invoke(view) + } + + override fun onDetach() { + super.onDetach() + val fl_card = requireView(R.id.dialog_card_share_ll_card) + fl_card.removeAllViews() + permissionFragment?.removeFromActivity(activity) + } + + private fun createCardBitmap(): Bitmap? { + val fl_card = requireView(R.id.dialog_card_share_ll_card) + if (fl_card.childCount == 0) return null + val view = fl_card.getChildAt(0) + val bitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.RGB_565) + val canvas = Canvas(bitmap) + view.draw(canvas) + return bitmap + } + + private fun shareBitmap(bitmap: Bitmap) { + ShareUtils.shareBitmap(activity, bitmap) + } + + private fun saveBitmap(bitmap: Bitmap) { + runtimeRequester = PermissionUtils.request(object : RequestListener { + override fun onSuccess() { + if (BitmapUtils.saveGallery(bitmap, "wanandroid_app_" + System.currentTimeMillis())) { + ToastMaker.showShort(activity.getString(R.string.save_successful)) + } else { + ToastMaker.showShort(activity.getString(R.string.save_failed)) + } + bitmap.recycle() + } + + override fun onFailed() { + ToastMaker.showShort(activity.getString(R.string.authorization_failed)) + bitmap.recycle() + } + }, activity, reqCodePermission, Manifest.permission.WRITE_EXTERNAL_STORAGE) + permissionFragment = PermissionFragment.findOrAddToActivity(activity) + permissionFragment?.onActivityResult = { + runtimeRequester?.onActivityResult(it) + } + } + + private class PermissionFragment : Fragment() { + var onActivityResult: ((requestCode: Int) -> Unit)? = null + + companion object { + fun findOrAddToActivity(activity: Activity): PermissionFragment { + activity as AppCompatActivity + activity.supportFragmentManager.findFragmentByTag(PermissionFragment::class.java.name) + ?.let { return it as PermissionFragment } + return PermissionFragment().also { fg -> + activity.supportFragmentManager.beginTransaction().apply { + add(fg, PermissionFragment::class.java.name) + }.commitNowAllowingStateLoss() + } + } + } + + fun removeFromActivity(activity: Activity) { + activity as AppCompatActivity + activity.supportFragmentManager.beginTransaction().apply { + remove(this@PermissionFragment) + }.commitNowAllowingStateLoss() + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + onActivityResult?.invoke(requestCode) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/CopiedLinkDialog.java b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/CopiedLinkDialog.java index be32547e..3e7cac30 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/CopiedLinkDialog.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/CopiedLinkDialog.java @@ -1,147 +1,59 @@ package per.goweii.wanandroid.module.main.dialog; -import android.animation.Animator; -import android.animation.ObjectAnimator; -import android.annotation.SuppressLint; -import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; -import android.view.animation.DecelerateInterpolator; -import android.widget.RelativeLayout; import android.widget.TextView; -import per.goweii.anylayer.Align; -import per.goweii.anylayer.AnimatorHelper; +import androidx.annotation.NonNull; + import per.goweii.anylayer.Layer; -import per.goweii.anylayer.PopupLayer; -import per.goweii.basic.utils.listener.SimpleListener; +import per.goweii.anylayer.popup.PopupLayer; +import per.goweii.anylayer.widget.SwipeLayout; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.module.main.activity.WebActivity; +import per.goweii.wanandroid.utils.CopiedTextProcessor; +import per.goweii.wanandroid.utils.router.Router; /** * @author CuiZhen * @date 2019/10/19 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class CopiedLinkDialog extends PopupLayer implements View.OnTouchListener { +public class CopiedLinkDialog extends PopupLayer { private final String link; - private RelativeLayout rl; - private TextView tvLink; - private ObjectAnimator restoreAnim; - public CopiedLinkDialog(View targetView, String link, SimpleListener onClose) { + public CopiedLinkDialog(View targetView, String link) { super(targetView); this.link = link; - contentView(R.layout.dialog_copied_link); - interceptKeyEvent(false); - outsideInterceptTouchEvent(false); - horizontal(Align.Horizontal.ALIGN_LEFT); - vertical(Align.Vertical.ALIGN_BOTTOM); - direction(Align.Direction.HORIZONTAL); - contentAnimator(new AnimatorCreator() { - @Override - public Animator createInAnimator(View target) { - return AnimatorHelper.createLeftInAnim(target); - } - + setContentView(R.layout.dialog_copied_link); + setInterceptKeyEvent(false); + setOutsideInterceptTouchEvent(false); + setHorizontal(Align.Horizontal.ALIGN_LEFT); + setVertical(Align.Vertical.ALIGN_BOTTOM); + setDirection(Align.Direction.HORIZONTAL); + setSwipeDismiss(SwipeLayout.Direction.LEFT); + addOnClickToDismissListener(new OnClickListener() { @Override - public Animator createOutAnimator(View target) { - return AnimatorHelper.createLeftOutAnim(target); - } - }); - onClickToDismiss(new OnClickListener() { - @Override - public void onClick(Layer layer, View v) { - if (onClose != null) { - onClose.onResult(); - } + public void onClick(@NonNull Layer layer, @NonNull View v) { + CopiedTextProcessor.getInstance().processed(); } }, R.id.dialog_copied_link_iv_close); - onClickToDismiss(new OnClickListener() { + addOnClickToDismissListener(new OnClickListener() { @Override - public void onClick(Layer layer, View v) { - if (onClose != null) { - onClose.onResult(); - } - WebActivity.start(getActivity(), link); + public void onClick(@NonNull Layer layer, @NonNull View v) { + CopiedTextProcessor.getInstance().processed(); + Router.routeTo(link); } }, R.id.dialog_copied_link_rl); } - @SuppressLint("ClickableViewAccessibility") + public String getLink() { + return link; + } + @Override public void onAttach() { super.onAttach(); - tvLink = getView(R.id.dialog_copied_link_tv_link); - rl = getView(R.id.dialog_copied_link_rl); + TextView tvLink = requireView(R.id.dialog_copied_link_tv_link); tvLink.setText(link); - rl.setOnTouchListener(this); - } - - private float touchSlop = ViewConfiguration.getTouchSlop(); - private float tapTimeout = ViewConfiguration.getTapTimeout(); - private float downX = 0; - private float downY = 0; - private float moveStartX = 0; - private float lastTransX = 0; - private long downTime = 0; - private boolean dragging = false; - - @Override - public boolean onTouch(View v, MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - if (restoreAnim != null) { - restoreAnim.cancel(); - } - dragging = false; - downTime = System.currentTimeMillis(); - downX = event.getRawX(); - downY = event.getRawY(); - break; - case MotionEvent.ACTION_MOVE: - float moveX = event.getRawX(); - float moveY = event.getRawY(); - if (dragging) { - float tx = lastTransX + moveX - moveStartX; - tx = tx > 0 ? 0 : tx; - rl.setTranslationX(tx); - } else if (Math.abs(moveX - downX) >= touchSlop || Math.abs(moveY - downY) >= touchSlop) { - // drag - dragging = true; - moveStartX = moveX; - lastTransX = rl.getTranslationX(); - } - break; - case MotionEvent.ACTION_UP: - long upTime = System.currentTimeMillis(); - if (dragging) { - dragging = false; - if (Math.abs(rl.getTranslationX() - lastTransX) > rl.getWidth() * 0.3F) { - dismiss(); - } else { - restore(); - } - } else { - if (upTime - downTime < tapTimeout) { - // tap - rl.performClick(); - } - } - break; - } - return true; - } - - private void restore() { - if (restoreAnim != null) { - restoreAnim.cancel(); - } - restoreAnim = ObjectAnimator.ofFloat(rl, "translationX", rl.getTranslationX(), 0F); - restoreAnim.setInterpolator(new DecelerateInterpolator()); - restoreAnim.start(); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/DailyDialog.kt b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/DailyDialog.kt new file mode 100644 index 00000000..54054f8d --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/DailyDialog.kt @@ -0,0 +1,12 @@ +package per.goweii.wanandroid.module.main.dialog + +import android.content.Context +import per.goweii.anylayer.dialog.DialogLayer + +/** + * @author CuiZhen + * @date 2020/2/16 + * GitHub: https://github.com/goweii + */ +class DailyDialog(context: Context) : DialogLayer(context) { +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/DownloadDialog.java b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/DownloadDialog.java index 1e730b40..91cc0d95 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/DownloadDialog.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/DownloadDialog.java @@ -1,5 +1,6 @@ package per.goweii.wanandroid.module.main.dialog; +import android.annotation.SuppressLint; import android.app.Activity; import android.view.Gravity; import android.view.View; @@ -7,12 +8,12 @@ import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.NonNull; + import java.io.File; import per.goweii.anylayer.AnyLayer; import per.goweii.anylayer.Layer; -import per.goweii.anypermission.AnyPermission; -import per.goweii.anypermission.RequestListener; import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.ResUtils; import per.goweii.basic.utils.coder.MD5Coder; @@ -21,6 +22,7 @@ import per.goweii.rxhttp.download.DownloadInfo; import per.goweii.rxhttp.download.RxDownload; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.module.main.activity.InstallApkActivity; /** @@ -36,7 +38,6 @@ public class DownloadDialog { private final boolean isForce; private final String url; private final String urlBackup; - private final String versionName; private boolean isAutoInstall = true; private ProgressBar progressBar; @@ -50,16 +51,26 @@ public class DownloadDialog { private int retryCount = 0; - public static DownloadDialog with(Activity activity, boolean isForce, String url, String urlBackup, String versionName) { - return new DownloadDialog(activity, isForce, url, urlBackup, versionName); + private OnDismissListener mOnDismissListener; + + public static DownloadDialog with(Activity activity, + boolean isForce, + String url, + String urlBackup, + OnDismissListener onDismissListener) { + return new DownloadDialog(activity, isForce, url, urlBackup, onDismissListener); } - private DownloadDialog(Activity activity, boolean isForce, String url, String urlBackup, String versionName) { + private DownloadDialog(Activity activity, + boolean isForce, + String url, + String urlBackup, + OnDismissListener onDismissListener) { this.mActivity = activity; this.url = url; this.urlBackup = urlBackup; this.isForce = isForce; - this.versionName = versionName; + this.mOnDismissListener = onDismissListener; showDialog(); startDownload(this.url); } @@ -119,7 +130,7 @@ public void onError(DownloadInfo info, Throwable e) { } else if (retryCount <= 6) { startDownload(DownloadDialog.this.urlBackup); } else { - ToastMaker.showShort("下载失败,可前往酷安手动更新"); + ToastMaker.showShort(mActivity.getString(R.string.download_update_failed_tips)); dismiss(); } } @@ -137,31 +148,31 @@ public void onSpeedChange(float bytesPerSecond, String speedFormat) { private void showDialog() { mAnyLayer = AnyLayer.dialog(mActivity) - .contentView(R.layout.dialog_download) - .backgroundColorRes(R.color.dialog_bg) - .gravity(Gravity.CENTER) - .cancelableOnTouchOutside(false) - .cancelableOnClickKeyBack(false) - .bindData(new Layer.DataBinder() { + .setContentView(R.layout.dialog_download) + .setBackgroundDimDefault() + .setGravity(Gravity.CENTER) + .setCancelableOnTouchOutside(false) + .setCancelableOnClickKeyBack(false) + .addOnBindDataListener(new Layer.OnBindDataListener() { @Override - public void bindData(Layer layer) { - ImageView ivClose = layer.getView(R.id.iv_dialog_download_close); + public void onBindData(@NonNull Layer layer) { + ImageView ivClose = layer.requireView(R.id.iv_dialog_download_close); if (isForce) { ivClose.setVisibility(View.GONE); } else { ivClose.setVisibility(View.VISIBLE); } - progressBar = layer.getView(R.id.pb_dialog_download); - tvProgress = layer.getView(R.id.tv_dialog_download_progress); - tvApkSize = layer.getView(R.id.tv_dialog_download_apk_size); - tvState = layer.getView(R.id.tv_dialog_download_state); - tvSpeed = layer.getView(R.id.tv_dialog_download_speed); - tvLine = layer.getView(R.id.tv_dialog_download_line); + progressBar = layer.requireView(R.id.pb_dialog_download); + tvProgress = layer.requireView(R.id.tv_dialog_download_progress); + tvApkSize = layer.requireView(R.id.tv_dialog_download_apk_size); + tvState = layer.requireView(R.id.tv_dialog_download_state); + tvSpeed = layer.requireView(R.id.tv_dialog_download_speed); + tvLine = layer.requireView(R.id.tv_dialog_download_line); } }) - .onClick(new Layer.OnClickListener() { + .addOnClickListener(new Layer.OnClickListener() { @Override - public void onClick(Layer layer, View v) { + public void onClick(@NonNull Layer layer, @NonNull View v) { if (mApk == null) { return; } @@ -171,29 +182,31 @@ public void onClick(Layer layer, View v) { installApk(); } }, R.id.tv_dialog_download_state) - .onClick(new Layer.OnClickListener() { + .addOnClickListener(new Layer.OnClickListener() { @Override - public void onClick(Layer layer, View v) { + public void onClick(@NonNull Layer layer, @NonNull View v) { if (!isForce) { dismiss(); } } - }, R.id.iv_dialog_download_close); - mAnyLayer.show(); - } - - private void installApk(){ - AnyPermission.with(mActivity) - .install(mApk) - .request(new RequestListener() { + }, R.id.iv_dialog_download_close) + .addOnDismissListener(new Layer.OnDismissListener() { @Override - public void onSuccess() { + public void onPreDismiss(@NonNull Layer layer) { } @Override - public void onFailed() { + public void onPostDismiss(@NonNull Layer layer) { + if (mOnDismissListener != null) { + mOnDismissListener.onDismiss(); + } } }); + mAnyLayer.show(); + } + + private void installApk() { + InstallApkActivity.start(mActivity, mApk); } private void preDownload() { @@ -208,10 +221,11 @@ private void preDownload() { tvProgress.setText(""); } if (tvState != null) { - tvState.setText(R.string.basic_ui_dialog_download_state_downloading); + tvState.setText(R.string.dialog_download_state_downloading); } } + @SuppressLint("SetTextI18n") private void setProgress(int progress) { if (progressBar != null) { progressBar.setProgress(progress); @@ -221,8 +235,8 @@ private void setProgress(int progress) { } if (progress >= 100) { if (tvState != null) { - tvState.setText(R.string.basic_ui_dialog_download_state_install); - tvState.setTextColor(ResUtils.getColor(mActivity, R.color.text_main)); + tvState.setText(R.string.dialog_download_state_install); + tvState.setTextColor(ResUtils.getThemeColor(tvState, R.attr.colorTextMain)); } } } @@ -235,4 +249,8 @@ private void dismiss() { mAnyLayer.dismiss(); } } + + public interface OnDismissListener { + void onDismiss(); + } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/ImageListPreviewDialog.kt b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/ImageListPreviewDialog.kt new file mode 100644 index 00000000..96ebd0ce --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/ImageListPreviewDialog.kt @@ -0,0 +1,150 @@ +package per.goweii.wanandroid.module.main.dialog + +import android.animation.Animator +import android.animation.AnimatorSet +import android.annotation.SuppressLint +import android.content.Context +import android.view.View +import android.view.ViewGroup +import per.goweii.actionbarex.common.ActionBarCommon +import per.goweii.anylayer.dialog.DialogLayer +import per.goweii.anylayer.utils.AnimatorHelper +import per.goweii.anylayer.widget.SwipeLayout +import per.goweii.wanandroid.R +import per.goweii.wanandroid.utils.ImageLoader +import per.goweii.wanandroid.widget.ImagePreviewView + +/** + * @author CuiZhen + * @date 2020/3/7 + */ +class ImageListPreviewDialog( + context: Context, + private val imgs: List, + private val index: Int +) : DialogLayer(context) { + + private var imageMenuDialog: ImageMenuDialog? = null + + init { + setBackgroundDimAmount(1F) + setContentView(R.layout.dialog_image_list_preview) + setContentAnimator(object : AnimatorCreator { + override fun createInAnimator(target: View): Animator { + val abc = requireView(R.id.dialog_image_list_preview_abc) + val vp = requireView(R.id.dialog_image_list_preview_vp) + return AnimatorSet().apply { + playTogether( + AnimatorHelper.createTopInAnim(abc), + AnimatorHelper.createZoomAlphaInAnim(vp) + ) + } + } + + override fun createOutAnimator(target: View): Animator { + val abc = requireView(R.id.dialog_image_list_preview_abc) + val vp = requireView(R.id.dialog_image_list_preview_vp) + return AnimatorSet().apply { + playTogether( + AnimatorHelper.createTopOutAnim(abc), + AnimatorHelper.createZoomAlphaOutAnim(vp) + ) + } + } + }) + } + + private val abc by lazy { requireView(R.id.dialog_image_list_preview_abc) } + private val vp by lazy { requireView(R.id.dialog_image_list_preview_vp) } + private val dl by lazy { requireView(R.id.dialog_image_list_preview_dl) } + + @SuppressLint("ClickableViewAccessibility", "SetTextI18n") + override fun onAttach() { + super.onAttach() + abc.setOnLeftIconClickListener { + dismiss() + } + vp.addOnPageChangeListener(object : androidx.viewpager.widget.ViewPager.OnPageChangeListener { + override fun onPageScrollStateChanged(p0: Int) { + when (p0) { + androidx.viewpager.widget.ViewPager.SCROLL_STATE_DRAGGING -> { + } + else -> { + } + } + } + + override fun onPageScrolled(p0: Int, p1: Float, p2: Int) { + dl.setSwipeDirection(0) + } + + override fun onPageSelected(p0: Int) { + dl.setSwipeDirection(SwipeLayout.Direction.BOTTOM) + abc.rightTextView.text = "${p0 + 1}/${imgs.size}" + } + }) + vp.adapter = object : androidx.viewpager.widget.PagerAdapter() { + override fun isViewFromObject(v: View, o: Any): Boolean { + return v === o + } + + override fun getCount(): Int { + return imgs.size + } + + override fun instantiateItem(container: ViewGroup, position: Int): Any { + val ipv = ImagePreviewView(container.context) + container.addView(ipv) + ImageLoader.image(ipv, imgs[position]) + ipv.onImagePreviewListener = object : ImagePreviewView.OnImagePreviewListener { + override fun onTap() { + dismiss() + } + + override fun onTouching1() { + dl.setSwipeDirection(SwipeLayout.Direction.BOTTOM) + } + + override fun onTouching2() { + dl.setSwipeDirection(0) + } + + override fun onTouchingUp() { + dl.setSwipeDirection(SwipeLayout.Direction.BOTTOM) + } + + override fun onLongClick() { + imageMenuDialog?.dismiss() + ImageMenuDialog.create(activity, ipv) { + imageMenuDialog = it + } + } + } + return ipv + } + + override fun destroyItem(container: ViewGroup, position: Int, obj: Any) { + container.removeView(obj as View) + } + } + dl.setSwipeDirection(SwipeLayout.Direction.BOTTOM) + dl.setOnSwipeListener(object : SwipeLayout.OnSwipeListener { + override fun onStart(direction: Int, fraction: Float) { + imageMenuDialog?.dismiss() + } + + override fun onSwiping(direction: Int, fraction: Float) { + viewHolder.background.alpha = 1F - fraction + abc.translationY = -abc.bottom * fraction + } + + override fun onEnd(direction: Int, fraction: Float) { + if (fraction == 1F) { + dl.post { dismiss(false) } + } + } + }) + abc.rightTextView.text = "${index + 1}/${imgs.size}" + vp.setCurrentItem(index, false) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/ImageMenuDialog.kt b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/ImageMenuDialog.kt new file mode 100644 index 00000000..c8b9ec2f --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/ImageMenuDialog.kt @@ -0,0 +1,108 @@ +package per.goweii.wanandroid.module.main.dialog + +import android.Manifest +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.BitmapDrawable +import android.view.Gravity +import android.widget.TextView +import com.github.chrisbanes.photoview.PhotoView +import per.goweii.anylayer.dialog.DialogLayer +import per.goweii.anylayer.widget.SwipeLayout +import per.goweii.anypermission.RequestListener +import per.goweii.basic.core.permission.PermissionUtils +import per.goweii.basic.ui.toast.ToastMaker +import per.goweii.basic.utils.bitmap.BitmapUtils +import per.goweii.basic.utils.ext.gone +import per.goweii.basic.utils.ext.visible +import per.goweii.codex.decoder.CodeDecoder +import per.goweii.codex.processor.zxing.ZXingMultiDecodeQRCodeProcessor +import per.goweii.wanandroid.R +import per.goweii.wanandroid.utils.UrlOpenUtils +import kotlin.math.max + +/** + * @author CuiZhen + * @date 2020/3/7 + */ +class ImageMenuDialog( + private val context: Context, + private val bitmap: Bitmap, + private val qrcode: String? +) : DialogLayer(context) { + + companion object { + fun create(context: Context, iv: PhotoView, onCreate: (ImageMenuDialog) -> Unit) { + try { + val drawable = iv.drawable ?: return + val bd = drawable as BitmapDrawable + val bitmap = bd.bitmap ?: return + val scale: Float = max(720F / bitmap.width.toFloat(), 720F / bitmap.height.toFloat()) + val newBitmap = if (scale < 1F) { + val w = (bitmap.width * scale).toInt() + val h = (bitmap.height * scale).toInt() + val bitmapScaled = Bitmap.createScaledBitmap(bitmap, w, h, false) + bitmapScaled + } else { + bitmap + } + CodeDecoder(ZXingMultiDecodeQRCodeProcessor()).decode(newBitmap, onSuccess = { + val dialog = ImageMenuDialog(context, bitmap, it.first().text).apply { + show() + } + onCreate.invoke(dialog) + }, onFailure = { + val dialog = ImageMenuDialog(context, bitmap, null).apply { + show() + } + onCreate.invoke(dialog) + }) + } catch (e: Exception) { + } + } + } + + init { + setBackgroundDimAmount(0.3F) + setContentView(R.layout.dialog_image_menu) + setSwipeDismiss(SwipeLayout.Direction.BOTTOM) + setGravity(Gravity.BOTTOM) + addOnClickToDismissListener(R.id.dialog_image_menu_iv_dismiss) + } + + private val tv_save by lazy { requireView(R.id.dialog_image_menu_tv_save) } + private val tv_qrcode by lazy { requireView(R.id.dialog_image_menu_tv_qrcode) } + + @SuppressLint("ClickableViewAccessibility") + override fun onAttach() { + super.onAttach() + if (qrcode.isNullOrEmpty()) { + tv_qrcode.gone() + } else { + tv_qrcode.visible() + tv_qrcode.setOnClickListener { + UrlOpenUtils.with(qrcode).open(context) + dismiss() + } + } + tv_save.setOnClickListener { + saveBitmap(bitmap) + dismiss() + } + } + + private fun saveBitmap(bitmap: Bitmap) { + PermissionUtils.request(object : RequestListener { + override fun onSuccess() { + if (BitmapUtils.saveGallery(bitmap, "wanandroid_article_image_${System.currentTimeMillis()}")) { + ToastMaker.showShort(activity.getString(R.string.save_successful)) + } else { + ToastMaker.showShort(activity.getString(R.string.save_failed)) + } + } + + override fun onFailed() {} + }, context, 0, Manifest.permission.WRITE_EXTERNAL_STORAGE) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/ImagePreviewDialog.kt b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/ImagePreviewDialog.kt new file mode 100644 index 00000000..76403446 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/ImagePreviewDialog.kt @@ -0,0 +1,154 @@ +package per.goweii.wanandroid.module.main.dialog + +import android.animation.Animator +import android.animation.AnimatorSet +import android.annotation.SuppressLint +import android.content.Context +import android.view.View +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.core.content.ContextCompat +import per.goweii.anylayer.dialog.DialogLayer +import per.goweii.anylayer.ktx.interceptKeyEvent +import per.goweii.anylayer.utils.AnimatorHelper +import per.goweii.anylayer.widget.SwipeLayout +import per.goweii.basic.core.glide.GlideHelper +import per.goweii.basic.core.glide.transformation.ScaleDownTransformation +import per.goweii.basic.utils.ext.gone +import per.goweii.basic.utils.ext.visible +import per.goweii.wanandroid.R +import per.goweii.wanandroid.widget.ImagePreviewView + +/** + * @author CuiZhen + * @date 2020/3/7 + */ +class ImagePreviewDialog( + context: Context, + private val imageUrl: String +) : DialogLayer(context) { + + private var imageMenuDialog: ImageMenuDialog? = null + + init { + setBackgroundDimAmount(1F) + setInterceptKeyEvent(true) + setCancelableOnClickKeyBack(true) + setContentView(R.layout.dialog_image_preview) + setContentAnimator(object : AnimatorCreator { + override fun createInAnimator(target: View): Animator { + val ll_bar = requireView(R.id.dialog_image_preview_ll_bar) + val dl = requireView(R.id.dialog_image_preview_dl) + return AnimatorSet().apply { + playTogether( + AnimatorHelper.createTopInAnim(ll_bar), + AnimatorHelper.createZoomAlphaInAnim(dl) + ) + } + } + + override fun createOutAnimator(target: View): Animator { + val ll_bar = requireView(R.id.dialog_image_preview_ll_bar) + val dl = requireView(R.id.dialog_image_preview_dl) + return AnimatorSet().apply { + playTogether( + AnimatorHelper.createTopOutAnim(ll_bar), + AnimatorHelper.createZoomAlphaOutAnim(dl) + ) + } + } + }) + } + + private val ll_bar by lazy { requireView(R.id.dialog_image_preview_ll_bar) } + private val iv_close by lazy { requireView(R.id.dialog_image_preview_iv_close) } + private val ipv by lazy { requireView(R.id.dialog_image_preview_pv) } + private val dl by lazy { requireView(R.id.dialog_image_preview_dl) } + private val tv_tip by lazy { requireView(R.id.dialog_image_preview_tv_tip) } + + @SuppressLint("ClickableViewAccessibility", "SetTextI18n") + override fun onAttach() { + super.onAttach() + iv_close.setOnClickListener { + dismiss() + } + ipv.onImagePreviewListener = object : ImagePreviewView.OnImagePreviewListener { + override fun onTap() { + dismiss() + } + + override fun onTouching1() { + if (ipv.isShown) { + val dl = requireView(R.id.dialog_image_preview_dl) + dl.setSwipeDirection(SwipeLayout.Direction.BOTTOM) + } + } + + override fun onTouching2() { + if (ipv.isShown) { + val dl = requireView(R.id.dialog_image_preview_dl) + dl.setSwipeDirection(0) + } + } + + override fun onTouchingUp() { + if (ipv.isShown) { + val dl = requireView(R.id.dialog_image_preview_dl) + dl.setSwipeDirection(SwipeLayout.Direction.BOTTOM) + } + } + + override fun onLongClick() { + imageMenuDialog?.dismiss() + ImageMenuDialog.create(activity, ipv) { + imageMenuDialog = it + } + } + } + GlideHelper.with(ipv.context) + .cache(true) + .placeHolder(ContextCompat.getDrawable(ipv.context, R.drawable.shape_image_perview_place_holder)) + .errorHolder(ContextCompat.getDrawable(ipv.context, R.drawable.shape_image_perview_place_holder)) + .load(imageUrl) + .onProgressListener { progress -> + when { + progress >= 1F -> { + tv_tip.gone() + tv_tip.text = activity.getString(R.string.loaded_successfully) + } + progress < 0F -> { + tv_tip.visible() + tv_tip.text = activity.getString(R.string.loaded_failed) + } + progress == 0F -> { + tv_tip.visible() + tv_tip.text = activity.getString(R.string.loading) + } + else -> { + tv_tip.visible() + tv_tip.text = activity.getString(R.string.loading_n_percent, (progress * 100).toInt()) + } + } + } + .transformation(ScaleDownTransformation()) + .into(ipv) + dl.setSwipeDirection(SwipeLayout.Direction.BOTTOM) + dl.setOnSwipeListener(object : SwipeLayout.OnSwipeListener { + override fun onStart(direction: Int, fraction: Float) { + imageMenuDialog?.dismiss() + } + + override fun onSwiping(direction: Int, fraction: Float) { + viewHolder.background.alpha = 1F - fraction + ll_bar.translationY = -ll_bar.bottom * fraction + } + + override fun onEnd(direction: Int, fraction: Float) { + if (fraction == 1F) { + dl.post { dismiss(false) } + } + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/PasswordDialog.java b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/PasswordDialog.java new file mode 100644 index 00000000..0b746c27 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/PasswordDialog.java @@ -0,0 +1,144 @@ +package per.goweii.wanandroid.module.main.dialog; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Handler; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.view.animation.OvershootInterpolator; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import per.goweii.anylayer.Layer; +import per.goweii.anylayer.dialog.DialogLayer; +import per.goweii.anylayer.utils.AnimatorHelper; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.utils.CopiedTextProcessor; +import per.goweii.wanandroid.utils.wanpwd.WanPwdParser; + +/** + * @author CuiZhen + * @date 2019/10/19 + * GitHub: https://github.com/goweii + */ +public class PasswordDialog extends DialogLayer { + + private final WanPwdParser mParser; + + private Handler mHandler; + private ObjectAnimator mAnim; + + public PasswordDialog(Context context, WanPwdParser parser) { + super(context); + this.mParser = parser; + setContentView(R.layout.dialog_password); + setBackgroundDimAmount(0.5F); + setCancelableOnClickKeyBack(false); + setCancelableOnTouchOutside(false); + setContentAnimator(new AnimatorCreator() { + @Override + public Animator createInAnimator(@NonNull View target) { + Animator a = AnimatorHelper.createZoomAlphaInAnim(target); + a.setInterpolator(new OvershootInterpolator()); + return a; + } + + @Override + public Animator createOutAnimator(@NonNull View target) { + Animator a = AnimatorHelper.createZoomAlphaOutAnim(target); + a.setInterpolator(new OvershootInterpolator()); + return a; + } + }); + addOnClickToDismissListener(new OnClickListener() { + @Override + public void onClick(@NonNull Layer layer, @NonNull View v) { + CopiedTextProcessor.getInstance().processed(); + } + }, R.id.dialog_password_iv_close); + addOnClickToDismissListener(new OnClickListener() { + @Override + public void onClick(@NonNull Layer layer, @NonNull View v) { + CopiedTextProcessor.getInstance().processed(); + Runnable runnable = mParser.getWanPwd().getRunnable(); + if (runnable != null) runnable.run(); + } + }, R.id.dialog_password_tv_open); + } + + public WanPwdParser getPassword() { + return mParser; + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public void onAttach() { + super.onAttach(); + mHandler = new Handler(); + TextView tvContent = requireView(R.id.dialog_password_tv_content); + TextView tvOpen = requireView(R.id.dialog_password_tv_open); + tvContent.setText(mParser.getWanPwd().getShowText()); + tvOpen.setText(mParser.getWanPwd().getBtnText()); + } + + @Override + protected void onPostShow() { + super.onPostShow(); + doEyeAnim(); + } + + @Override + protected void onPreDismiss() { + super.onPreDismiss(); + mHandler.removeCallbacksAndMessages(null); + mHandler = null; + if (mAnim != null) { + mAnim.cancel(); + mAnim = null; + } + } + + private void doEyeAnim() { + View flEye = findView(R.id.dialog_password_fl_eye); + if (flEye == null) { + return; + } + if (mAnim == null) { + int h = flEye.getHeight(); + if (h <= 0) return; + mAnim = ObjectAnimator.ofFloat(flEye, "translationY", 0, h, 0); + mAnim.setInterpolator(new AccelerateDecelerateInterpolator()); + mAnim.setDuration(350); + mAnim.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + if (mHandler != null) { + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + doEyeAnim(); + } + }, (long) (Math.random() * 5000 + 5000)); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + }); + } + mAnim.setRepeatCount((int) (Math.random() + 0.5)); + mAnim.start(); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/PrivacyPolicyDialog.java b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/PrivacyPolicyDialog.java new file mode 100644 index 00000000..63d8c9c8 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/PrivacyPolicyDialog.java @@ -0,0 +1,117 @@ +package per.goweii.wanandroid.module.main.dialog; + +import android.content.Context; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.SpannableStringBuilder; +import android.text.TextPaint; +import android.text.method.LinkMovementMethod; +import android.text.style.ClickableSpan; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import per.goweii.anylayer.Layer; +import per.goweii.anylayer.dialog.DialogLayer; +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.common.Constant; +import per.goweii.wanandroid.common.WanApp; +import per.goweii.wanandroid.utils.GuideSPUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; + +/** + * @author CuiZhen + * @date 2019/8/31 + * GitHub: https://github.com/goweii + */ +public class PrivacyPolicyDialog extends DialogLayer { + + public static void showIfFirst(Context context, CompleteCallback callback) { + if (GuideSPUtils.getInstance().isPrivacyPolicyShown()) { + if (callback != null) { + callback.onComplete(); + } + return; + } + new PrivacyPolicyDialog(context, callback).show(); + } + + private PrivacyPolicyDialog(Context context, CompleteCallback callback) { + super(context); + addOnDismissListener(new OnDismissListener() { + @Override + public void onPreDismiss(@NonNull Layer layer) { + } + + @Override + public void onPostDismiss(@NonNull Layer layer) { + if (callback != null) { + callback.onComplete(); + } + } + }); + setContentView(R.layout.dialog_privacy_policy); + setBackgroundBlurRadius(4); + setBackgroundBlurSimple(20); + setCancelableOnClickKeyBack(false); + setCancelableOnTouchOutside(false); + addOnClickToDismissListener(new OnClickListener() { + @Override + public void onClick(@NonNull Layer layer, @NonNull View v) { + GuideSPUtils.getInstance().setPrivacyPolicyShown(); + } + }, R.id.dialog_privacy_policy_tv_yes); + addOnClickToDismissListener(new OnClickListener() { + @Override + public void onClick(@NonNull Layer layer, @NonNull View v) { + WanApp.exitApp(); + } + }, R.id.dialog_privacy_policy_tv_no); + } + + @Override + public void onAttach() { + super.onAttach(); + TextView content = requireView(R.id.dialog_privacy_policy_tv_content); + String text = getActivity().getString(R.string.privacy_policy_content); + String link = getActivity().getString(R.string.privacy_policy_content_link); + int start = text.indexOf(link); + int end = start + link.length(); + SpannableString spannableString = new SpannableString(text); + spannableString.setSpan(new Clickable(new View.OnClickListener() { + @Override + public void onClick(View v) { + UrlOpenUtils.Companion + .with(Constant.PRIVACY_POLICY_URL) + .open(getActivity()); + } + }), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + content.setText(new SpannableStringBuilder().append(spannableString)); + content.setMovementMethod(LinkMovementMethod.getInstance()); + } + + private class Clickable extends ClickableSpan implements View.OnClickListener { + private final View.OnClickListener mListener; + + private Clickable(View.OnClickListener mListener) { + this.mListener = mListener; + } + + @Override + public void onClick(@NonNull View v) { + mListener.onClick(v); + } + + @Override + public void updateDrawState(TextPaint ds) { + ds.setColor(ResUtils.getThemeColor(getActivity(), R.attr.colorMain)); + ds.setUnderlineText(false); + } + } + + public interface CompleteCallback { + void onComplete(); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/QrcodeShareDialog.java b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/QrcodeShareDialog.java deleted file mode 100644 index 2d14f3d6..00000000 --- a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/QrcodeShareDialog.java +++ /dev/null @@ -1,163 +0,0 @@ -package per.goweii.wanandroid.module.main.dialog; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; -import android.text.TextUtils; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.uuzuche.lib_zxing.activity.CodeUtils; - -import per.goweii.anylayer.AnimatorHelper; -import per.goweii.anylayer.DialogLayer; -import per.goweii.anylayer.Layer; -import per.goweii.rxhttp.core.RxLife; -import per.goweii.wanandroid.R; -import per.goweii.wanandroid.http.RequestCallback; -import per.goweii.wanandroid.module.main.model.JinrishiciBean; -import per.goweii.wanandroid.module.main.model.MainRequest; - -/** - * @author CuiZhen - * @date 2019/11/9 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public class QrcodeShareDialog extends DialogLayer { - - private RxLife mRxLife = null; - private final String mUrl; - private final String mTitle; - private final OnShareClickListener mOnShareClickListener; - - public QrcodeShareDialog(Context context, String url, String title, OnShareClickListener listener) { - super(context); - mUrl = url; - mTitle = title; - mOnShareClickListener = listener; - contentView(R.layout.dialog_qrcode_share); - backgroundColorRes(R.color.dialog_bg); - contentAnimator(new AnimatorCreator() { - @Override - public Animator createInAnimator(View target) { - View rl_card = getView(R.id.dialog_qrcode_share_rl_card); - View rl_btn = getView(R.id.dialog_qrcode_share_ll_btn); - AnimatorSet animator = new AnimatorSet(); - animator.playTogether( - AnimatorHelper.createAlphaInAnim(rl_card), - AnimatorHelper.createBottomInAnim(rl_btn) - ); - return animator; - } - - @Override - public Animator createOutAnimator(View target) { - View rl_card = getView(R.id.dialog_qrcode_share_rl_card); - View rl_btn = getView(R.id.dialog_qrcode_share_ll_btn); - AnimatorSet animator = new AnimatorSet(); - animator.playTogether( - AnimatorHelper.createAlphaOutAnim(rl_card), - AnimatorHelper.createBottomOutAnim(rl_btn) - ); - return animator; - } - }); - onClickToDismiss(R.id.dialog_qrcode_share_rl_content); - onClickToDismiss(new OnClickListener() { - @Override - public void onClick(Layer layer, View v) { - if (mOnShareClickListener != null) { - mOnShareClickListener.onSave(createCardBitmap()); - } - } - }, R.id.dialog_qrcode_share_iv_album); - onClickToDismiss(new OnClickListener() { - @Override - public void onClick(Layer layer, View v) { - if (mOnShareClickListener != null) { - mOnShareClickListener.onShare(createCardBitmap()); - } - } - }, R.id.dialog_qrcode_share_iv_share); - onClick(new OnClickListener() { - @Override - public void onClick(Layer layer, View v) { - refreshJinrishici(); - } - }, R.id.dialog_qrcode_share_rv_shici); - } - - @Override - public void onAttach() { - super.onAttach(); - mRxLife = RxLife.create(); - ImageView iv_qrcode = getView(R.id.dialog_qrcode_share_piv_qrcode); - TextView tv_title = getView(R.id.dialog_qrcode_share_tv_title); - Bitmap qrcode = CodeUtils.createImage(mUrl, 300, 300, BitmapFactory.decodeResource(getActivity().getResources(), R.drawable.ic_icon)); - iv_qrcode.setImageBitmap(qrcode); - tv_title.setText(mTitle); - refreshJinrishici(); - } - - @Override - public void onDetach() { - super.onDetach(); - if (mRxLife != null) { - mRxLife.destroy(); - } - } - - private void refreshJinrishici() { - MainRequest.getJinrishici(mRxLife, new RequestCallback() { - @Override - public void onSuccess(int code, JinrishiciBean data) { - TextView tv_shici = getView(R.id.dialog_qrcode_share_tv_shici); - if (tv_shici != null) { - tv_shici.setText(formatShici(data.getContent())); - } - } - - @Override - public void onFailed(int code, String msg) { - TextView tv_shici = getView(R.id.dialog_qrcode_share_tv_shici); - if (tv_shici != null) { - tv_shici.setText("获取失败,点击刷新"); - } - } - }); - } - - private String formatShici(String shici) { - String[] split = shici.split("[,。;?!]"); - StringBuilder sb = new StringBuilder(); - for (String s : split) { - if (!TextUtils.isEmpty(s)) { - if (sb.length() > 0) { - sb.append("\n"); - } - sb.append(s); - } - } - return sb.toString(); - } - - private Bitmap createCardBitmap() { - View rl_card = getView(R.id.dialog_qrcode_share_rl_card); - Bitmap bitmap = Bitmap.createBitmap(rl_card.getWidth(), rl_card.getHeight(), Bitmap.Config.RGB_565); - Canvas canvas = new Canvas(bitmap); - rl_card.draw(canvas); - return bitmap; - } - - public interface OnShareClickListener { - void onSave(Bitmap bitmap); - - void onShare(Bitmap bitmap); - } -} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebDialog.java b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebDialog.java index 5f1b9ace..8d2b305b 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebDialog.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebDialog.java @@ -4,32 +4,33 @@ import android.animation.AnimatorSet; import android.animation.ValueAnimator; import android.content.Context; -import android.support.v4.view.ViewPager; import android.view.View; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.ImageView; +import androidx.annotation.NonNull; +import androidx.viewpager.widget.ViewPager; + import java.util.ArrayList; import java.util.List; -import per.goweii.anylayer.AnimatorHelper; -import per.goweii.anylayer.DialogLayer; +import per.goweii.anylayer.dialog.DialogLayer; +import per.goweii.anylayer.utils.AnimatorHelper; +import per.goweii.basic.utils.ResUtils; import per.goweii.basic.utils.display.DisplayInfoUtils; import per.goweii.basic.utils.listener.OnClickListener2; +import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.wanandroid.R; import per.goweii.wanandroid.module.home.presenter.WebDialogPresenter; import per.goweii.wanandroid.module.home.view.WebDialogView; import per.goweii.wanandroid.module.main.adapter.WebDialogPagerAdapter; import per.goweii.wanandroid.module.main.model.ArticleBean; -import per.goweii.wanandroid.utils.WebHolder; import per.goweii.wanandroid.widget.CollectView; /** * @author CuiZhen * @date 2019/8/31 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WebDialog extends DialogLayer implements WebDialogView { @@ -61,15 +62,15 @@ private WebDialog(final Context context, super(context); this.currPos = currPos; this.singleTipMode = singleTipMode; - contentView(R.layout.dialog_web); - backgroundColorRes(R.color.dialog_bg); - cancelableOnTouchOutside(true); - cancelableOnClickKeyBack(true); - onClickToDismiss(R.id.dialog_web_iv_close); - contentAnimator(new AnimatorCreator() { + setContentView(R.layout.dialog_web); + setBackgroundDimDefault(); + setCancelableOnTouchOutside(true); + setCancelableOnClickKeyBack(true); + addOnClickToDismissListener(R.id.dialog_web_iv_close); + setContentAnimator(new AnimatorCreator() { @Override - public Animator createInAnimator(View target) { - final ViewPager vp = getView(R.id.dialog_web_vp); + public Animator createInAnimator(@NonNull View target) { + final ViewPager vp = requireView(R.id.dialog_web_vp); View bar = target.findViewById(R.id.dialog_web_rl_bottom_bar); bar.setTranslationY(1000); vp.setPageMargin((int) DisplayInfoUtils.getInstance().dp2px(12)); @@ -96,8 +97,8 @@ public void onAnimationUpdate(ValueAnimator animation) { } @Override - public Animator createOutAnimator(View target) { - final ViewPager vp = getView(R.id.dialog_web_vp); + public Animator createOutAnimator(@NonNull View target) { + final ViewPager vp = requireView(R.id.dialog_web_vp); View bar = target.findViewById(R.id.dialog_web_rl_bottom_bar); ValueAnimator vpMargin = ValueAnimator.ofInt(vp.getPageMargin(), vp.getPageMargin()); vpMargin.setInterpolator(new AccelerateInterpolator()); @@ -139,22 +140,41 @@ public void onAttach() { super.onAttach(); presenter = new WebDialogPresenter(); presenter.attach(this); - final ViewPager vp = getView(R.id.dialog_web_vp); - final ImageView iv_back = getView(R.id.dialog_web_iv_back); - final CollectView cv_collect = getView(R.id.dialog_web_cv_collect); + final ViewPager vp = requireView(R.id.dialog_web_vp); + final ImageView iv_read_later = requireView(R.id.dialog_web_iv_read_later); + final CollectView cv_collect = requireView(R.id.dialog_web_cv_collect); if (singleTipMode) { - iv_back.setVisibility(View.GONE); + iv_read_later.setVisibility(View.GONE); cv_collect.setVisibility(View.GONE); } else { - iv_back.setVisibility(View.VISIBLE); + iv_read_later.setVisibility(View.VISIBLE); cv_collect.setVisibility(View.VISIBLE); - iv_back.setOnClickListener(new OnClickListener2() { + iv_read_later.setOnClickListener(new OnClickListener2() { @Override public void onClick2(View v) { if (mAdapter != null) { - WebHolder web = mAdapter.getWeb(vp.getCurrentItem()); - if (web != null) { - web.goBack(); + ArticleBean data = mAdapter.getArticleBean(vp.getCurrentItem()); + if (data != null) { + presenter.isReadLater(data.getLink(), new SimpleCallback() { + @Override + public void onResult(Boolean isReadLater) { + if (isReadLater) { + presenter.removeReadLater(data.getLink(), new SimpleCallback() { + @Override + public void onResult(Boolean data) { + switchIvReadLaterState(false); + } + }); + } else { + presenter.addReadLater(data.getLink(), data.getTitle(), new SimpleCallback() { + @Override + public void onResult(Boolean data) { + switchIvReadLaterState(true); + } + }); + } + } + }); } } } @@ -162,11 +182,13 @@ public void onClick2(View v) { cv_collect.setOnClickListener(new CollectView.OnClickListener() { @Override public void onClick(CollectView v) { - ArticleBean data = mAdapter.getBean(vp.getCurrentItem()); - if (!v.isChecked()) { - presenter.collect(data, v); - } else { - presenter.uncollect(data, v); + ArticleBean data = mAdapter.getArticleBean(vp.getCurrentItem()); + if (data != null) { + if (v.isChecked()) { + presenter.collect(data, v); + } else { + presenter.uncollect(data, v); + } } } }); @@ -179,10 +201,18 @@ public void onPageScrolled(int i, float v, int i1) { @Override public void onPageSelected(int i) { if (mAdapter != null) { - ArticleBean data = mAdapter.getBean(i); - cv_collect.setChecked(data.isCollect(), true); - if (mOnPageChangedListener != null) { - mOnPageChangedListener.onPageChanged(i, data); + ArticleBean data = mAdapter.getArticleBean(i); + if (data != null) { + cv_collect.setChecked(data.isCollect(), true); + presenter.isReadLater(data.getLink(), new SimpleCallback() { + @Override + public void onResult(Boolean isReadLater) { + switchIvReadLaterState(isReadLater); + } + }); + if (mOnPageChangedListener != null) { + mOnPageChangedListener.onPageChanged(i, data); + } } } } @@ -201,25 +231,27 @@ public void onDoubleClick(ArticleBean data) { }); vp.setAdapter(mAdapter); vp.setCurrentItem(currPos); + ArticleBean data = mAdapter.getArticleBean(currPos); + presenter.isReadLater(data.getLink(), new SimpleCallback() { + @Override + public void onResult(Boolean isReadLater) { + switchIvReadLaterState(isReadLater); + } + }); } @Override - public void onShow() { - super.onShow(); - } - - @Override - public void onPreRemove() { + protected void onPreDismiss() { + super.onPreDismiss(); if (mAdapter != null) { - mAdapter.pauseAllAgentWeb(); + mAdapter.pauseAllWeb(); } - super.onPreRemove(); } @Override public void onDetach() { if (mAdapter != null) { - mAdapter.destroyAllAgentWeb(); + mAdapter.destroyAllWeb(); } if (presenter != null) { presenter.detach(); @@ -227,6 +259,15 @@ public void onDetach() { super.onDetach(); } + private void switchIvReadLaterState(boolean checked) { + final ImageView iv_read_later = requireView(R.id.dialog_web_iv_read_later); + if (checked) { + iv_read_later.setColorFilter(ResUtils.getThemeColor(iv_read_later, R.attr.colorIconMain)); + } else { + iv_read_later.setColorFilter(ResUtils.getThemeColor(iv_read_later, R.attr.colorOnMainOrSurface)); + } + } + @Override public Context getContext() { return getActivity(); diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebGuideDialog.java b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebGuideDialog.java index 022bb0e5..8dd834db 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebGuideDialog.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebGuideDialog.java @@ -1,56 +1,43 @@ package per.goweii.wanandroid.module.main.dialog; -import android.animation.Animator; import android.animation.ValueAnimator; import android.content.Context; import android.view.View; import android.widget.ImageView; -import per.goweii.anylayer.AnimatorHelper; -import per.goweii.anylayer.DialogLayer; +import androidx.annotation.NonNull; + import per.goweii.anylayer.Layer; +import per.goweii.anylayer.dialog.DialogLayer; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.utils.GuideSPUtils; /** * @author CuiZhen * @date 2019/8/31 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WebGuideDialog extends DialogLayer { + public WebGuideDialog(Context context) { super(context); - contentView(R.layout.dialog_web_guide) - .backgroundColorRes(R.color.dialog_bg) - .cancelableOnClickKeyBack(false) - .cancelableOnTouchOutside(false) - .contentAnimator(new Layer.AnimatorCreator() { - @Override - public Animator createInAnimator(View target) { - return AnimatorHelper.createAlphaInAnim(target); - } - - @Override - public Animator createOutAnimator(View target) { - return AnimatorHelper.createAlphaOutAnim(target); - } - }) - .onClick(new Layer.OnClickListener() { - @Override - public void onClick(Layer layer, View v) { - layer.dismiss(); - GuideSPUtils.getInstance().setWebGuideShown(); - } - }, R.id.dialog_web_guide_tv_know); + setContentView(R.layout.dialog_web_guide); + setBackgroundDimDefault(); + setCancelableOnClickKeyBack(false); + setCancelableOnTouchOutside(false); + setAnimStyle(AnimStyle.ALPHA); + addOnClickListener(new Layer.OnClickListener() { + @Override + public void onClick(@NonNull Layer layer, @NonNull View v) { + layer.dismiss(); + } + }, R.id.dialog_web_guide_tv_know); } @Override public void onAttach() { super.onAttach(); - ImageView iv_1 = getView(R.id.dialog_web_guide_iv_1); - ImageView iv_2 = getView(R.id.dialog_web_guide_iv_2); + ImageView iv_1 = requireView(R.id.dialog_web_guide_iv_1); + ImageView iv_2 = requireView(R.id.dialog_web_guide_iv_2); ValueAnimator animator1 = ValueAnimator.ofFloat(1, 1.5F); animator1.setDuration(3000L); animator1.setRepeatCount(ValueAnimator.INFINITE); diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebMenuDialog.java b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebMenuDialog.java index 0b918ac5..7b911496 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebMenuDialog.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebMenuDialog.java @@ -1,146 +1,147 @@ package per.goweii.wanandroid.module.main.dialog; -import android.animation.Animator; import android.content.Context; -import android.support.annotation.NonNull; +import android.net.Uri; +import android.text.TextUtils; import android.view.Gravity; import android.view.View; import android.widget.ImageView; import android.widget.TextView; -import per.goweii.anylayer.AnimatorHelper; +import androidx.annotation.NonNull; + import per.goweii.anylayer.AnyLayer; import per.goweii.anylayer.Layer; +import per.goweii.anylayer.widget.SwipeLayout; +import per.goweii.basic.utils.ResUtils; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.module.main.activity.WebActivity; import per.goweii.wanandroid.module.mine.activity.HostInterruptActivity; import per.goweii.wanandroid.module.mine.activity.SettingActivity; -import per.goweii.wanandroid.utils.HostInterceptUtils; import per.goweii.wanandroid.utils.SettingUtils; import per.goweii.wanandroid.utils.UserUtils; +import per.goweii.wanandroid.utils.web.HostInterceptUtils; /** * @author CuiZhen * @date 2019/5/20 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WebMenuDialog { public static void show(@NonNull Context context, + final String url, final boolean collected, + final boolean readLater, @NonNull OnMenuClickListener listener) { AnyLayer.dialog(context) - .contentView(R.layout.dialog_web_menu) - .gravity(Gravity.BOTTOM) - .backgroundColorRes(R.color.dialog_bg) - .contentAnimator(new Layer.AnimatorCreator() { - @Override - public Animator createInAnimator(View target) { - return AnimatorHelper.createBottomInAnim(target); - } - - @Override - public Animator createOutAnimator(View target) { - return AnimatorHelper.createBottomOutAnim(target); - } - }) - .onClickToDismiss(new Layer.OnClickListener() { - @Override - public void onClick(Layer layer, View v) { - switch (v.getId()) { - default: - break; - case R.id.dialog_web_menu_iv_share_article: - if (UserUtils.getInstance().doIfLogin(v.getContext())) { - listener.onShareArticle(); - } - break; - case R.id.dialog_web_menu_iv_collect: - if (UserUtils.getInstance().doIfLogin(v.getContext())) { - listener.onCollect(); - } - break; - case R.id.dialog_web_menu_iv_read_later: - listener.onReadLater(); - break; - case R.id.dialog_web_menu_iv_home: - listener.onHome(); - break; - case R.id.dialog_web_menu_iv_refresh: - listener.onRefresh(); - break; - case R.id.dialog_web_menu_iv_close_activity: - listener.onCloseActivity(); - break; - case R.id.dialog_web_menu_iv_setting: - SettingActivity.start(context); - break; - case R.id.dialog_web_menu_iv_share: - listener.onShare(); - break; - } - } - }, + .setContentView(R.layout.dialog_web_menu) + .setBackgroundDimDefault() + .setSwipeDismiss(SwipeLayout.Direction.BOTTOM) + .setGravity(Gravity.BOTTOM) + .addOnClickToDismissListener( + new Layer.OnClickListener() { + @Override + public void onClick(@NonNull Layer layer, @NonNull View v) { + switch (v.getId()) { + default: + break; + case R.id.dialog_web_menu_iv_share_article: + if (UserUtils.getInstance().doIfLogin(v.getContext())) { + listener.onShareArticle(); + } + break; + case R.id.dialog_web_menu_iv_collect: + if (UserUtils.getInstance().doIfLogin(v.getContext())) { + listener.onCollect(); + } + break; + case R.id.dialog_web_menu_iv_read_later: + listener.onReadLater(); + break; + case R.id.dialog_web_menu_iv_home: + listener.onHome(); + break; + case R.id.dialog_web_menu_iv_refresh: + listener.onRefresh(); + break; + case R.id.dialog_web_menu_iv_close_activity: + listener.onCloseActivity(); + break; + case R.id.dialog_web_menu_iv_setting: + SettingActivity.start(context); + break; + case R.id.dialog_web_menu_iv_share: + listener.onShare(); + break; + case R.id.dialog_web_menu_iv_go_top: + listener.onGoTop(); + break; + case R.id.dialog_web_menu_iv_browser: + listener.onOpenInBrowser(); + break; + case R.id.dialog_web_menu_iv_listen: + listener.onListen(); + break; + } + } + }, R.id.dialog_web_menu_iv_share_article, R.id.dialog_web_menu_iv_read_later, R.id.dialog_web_menu_iv_home, R.id.dialog_web_menu_iv_collect, R.id.dialog_web_menu_iv_refresh, + R.id.dialog_web_menu_iv_go_top, R.id.dialog_web_menu_iv_close_activity, R.id.dialog_web_menu_iv_dismiss, R.id.dialog_web_menu_iv_setting, - R.id.dialog_web_menu_iv_share) - .onClick(new Layer.OnClickListener() { - @Override - public void onClick(Layer layer, View v) { - switch (v.getId()) { - default: - break; - case R.id.dialog_web_menu_iv_interrupt: - switch (SettingUtils.getInstance().getUrlInterceptType()) { - default: - case HostInterceptUtils.TYPE_NOTHING: - SettingUtils.getInstance().setUrlInterceptType(HostInterceptUtils.TYPE_ONLY_WHITE); - break; - case HostInterceptUtils.TYPE_ONLY_WHITE: - SettingUtils.getInstance().setUrlInterceptType(HostInterceptUtils.TYPE_INTERCEPT_BLACK); - break; - case HostInterceptUtils.TYPE_INTERCEPT_BLACK: - SettingUtils.getInstance().setUrlInterceptType(HostInterceptUtils.TYPE_NOTHING); - break; - } - ImageView iv_interrupt = layer.getView(R.id.dialog_web_menu_iv_interrupt); - TextView tv_interrupt = layer.getView(R.id.dialog_web_menu_tv_interrupt); - switchInterruptState(iv_interrupt, tv_interrupt); - break; - case R.id.dialog_web_menu_iv_swipe_back: - boolean webSwipeBackEdge = !SettingUtils.getInstance().isWebSwipeBackEdge(); - SettingUtils.getInstance().setWebSwipeBackEdge(webSwipeBackEdge); - if (context instanceof WebActivity) { - WebActivity webActivity = (WebActivity) context; - webActivity.refreshSwipeBackOnlyEdge(); - } - ImageView iv_swipe_back = layer.getView(R.id.dialog_web_menu_iv_swipe_back); - switchSwipeBackState(iv_swipe_back); - break; - } - } - }, - R.id.dialog_web_menu_iv_interrupt, - R.id.dialog_web_menu_iv_swipe_back) - .bindData(new Layer.DataBinder() { + R.id.dialog_web_menu_iv_share, + R.id.dialog_web_menu_iv_browser, + R.id.dialog_web_menu_iv_listen) + .addOnClickListener( + new Layer.OnClickListener() { + @Override + public void onClick(@NonNull Layer layer, @NonNull View v) { + switch (SettingUtils.getInstance().getUrlInterceptType()) { + default: + case HostInterceptUtils.TYPE_NOTHING: + SettingUtils.getInstance().setUrlInterceptType(HostInterceptUtils.TYPE_ONLY_WHITE); + break; + case HostInterceptUtils.TYPE_ONLY_WHITE: + SettingUtils.getInstance().setUrlInterceptType(HostInterceptUtils.TYPE_INTERCEPT_BLACK); + break; + case HostInterceptUtils.TYPE_INTERCEPT_BLACK: + SettingUtils.getInstance().setUrlInterceptType(HostInterceptUtils.TYPE_NOTHING); + break; + } + ImageView iv_interrupt = layer.requireView(R.id.dialog_web_menu_iv_interrupt); + TextView tv_interrupt = layer.requireView(R.id.dialog_web_menu_tv_interrupt); + switchInterruptState(iv_interrupt, tv_interrupt); + } + }, + R.id.dialog_web_menu_iv_interrupt) + .addOnBindDataListener(new Layer.OnBindDataListener() { @Override - public void bindData(Layer layer) { - ImageView iv_collect = layer.getView(R.id.dialog_web_menu_iv_collect); - TextView tv_collect = layer.getView(R.id.dialog_web_menu_tv_collect); + public void onBindData(@NonNull Layer layer) { + TextView tv_host = layer.requireView(R.id.dialog_web_menu_tv_host); + String host = null; + if (!TextUtils.isEmpty(url)) { + Uri uri = Uri.parse(url); + host = uri.getHost(); + } + if (TextUtils.isEmpty(host)) { + tv_host.setVisibility(View.GONE); + } else { + tv_host.setVisibility(View.VISIBLE); + tv_host.setText(tv_host.getContext().getString(R.string.this_webpage_is_provided_by, host)); + } + ImageView iv_collect = layer.requireView(R.id.dialog_web_menu_iv_collect); + TextView tv_collect = layer.requireView(R.id.dialog_web_menu_tv_collect); switchCollectState(iv_collect, tv_collect, collected); - ImageView iv_interrupt = layer.getView(R.id.dialog_web_menu_iv_interrupt); - TextView tv_interrupt = layer.getView(R.id.dialog_web_menu_tv_interrupt); + ImageView iv_read_later = layer.requireView(R.id.dialog_web_menu_iv_read_later); + switchReadLaterState(iv_read_later, readLater); + ImageView iv_interrupt = layer.requireView(R.id.dialog_web_menu_iv_interrupt); + TextView tv_interrupt = layer.requireView(R.id.dialog_web_menu_tv_interrupt); switchInterruptState(iv_interrupt, tv_interrupt); - ImageView iv_swipe_back = layer.getView(R.id.dialog_web_menu_iv_swipe_back); - switchSwipeBackState(iv_swipe_back); iv_interrupt.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { @@ -157,12 +158,16 @@ public boolean onLongClick(View v) { private static void switchCollectState(ImageView iv_collect, TextView tv_collect, boolean collected) { setIconChecked(iv_collect, collected); if (collected) { - tv_collect.setText("已收藏"); + tv_collect.setText(R.string.collected); } else { - tv_collect.setText("收藏"); + tv_collect.setText(R.string.collect); } } + private static void switchReadLaterState(ImageView iv_read_later, boolean readLater) { + setIconChecked(iv_read_later, readLater); + } + private static void switchInterruptState(ImageView iv_interrupt, TextView tv_interrupt) { switch (SettingUtils.getInstance().getUrlInterceptType()) { default: @@ -181,14 +186,12 @@ private static void switchInterruptState(ImageView iv_interrupt, TextView tv_int } } - private static void switchSwipeBackState(ImageView iv_swipe_back) { - setIconChecked(iv_swipe_back, SettingUtils.getInstance().isWebSwipeBackEdge()); - } - private static void setIconChecked(ImageView iv, boolean checked) { if (checked) { + iv.setColorFilter(ResUtils.getThemeColor(iv, R.attr.colorIconOnMain)); iv.setBackgroundResource(R.drawable.bg_press_color_main_radius_max); } else { + iv.setColorFilter(ResUtils.getThemeColor(iv, R.attr.colorIconSurface)); iv.setBackgroundResource(R.drawable.bg_press_color_surface_top_radius_max); } } @@ -204,9 +207,15 @@ public interface OnMenuClickListener { void onRefresh(); + void onGoTop(); + void onCloseActivity(); void onShare(); + + void onOpenInBrowser(); + + void onListen(); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebQuickDialog.java b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebQuickDialog.java index 30cf29b3..f22a781f 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebQuickDialog.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebQuickDialog.java @@ -1,60 +1,59 @@ package per.goweii.wanandroid.module.main.dialog; -import android.animation.Animator; import android.view.View; -import per.goweii.anylayer.AnimatorHelper; +import androidx.annotation.NonNull; + import per.goweii.anylayer.Layer; -import per.goweii.anylayer.PopupLayer; +import per.goweii.anylayer.popup.PopupLayer; +import per.goweii.anylayer.widget.SwipeLayout; import per.goweii.wanandroid.R; /** * @author CuiZhen * @date 2019/11/9 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WebQuickDialog extends PopupLayer { public WebQuickDialog(View targetView, OnQuickClickListener onQuickClickListener) { super(targetView); - contentView(R.layout.dialog_web_quick); - backgroundColorRes(R.color.dialog_bg); - outsideInterceptTouchEvent(false); - interceptKeyEvent(false); - contentAnimator(new AnimatorCreator() { - @Override - public Animator createInAnimator(View target) { - return AnimatorHelper.createTopInAnim(target); - } - - @Override - public Animator createOutAnimator(View target) { - return AnimatorHelper.createTopOutAnim(target); - } - }); - onClickToDismiss(new OnClickListener() { + setContentView(R.layout.dialog_web_quick); + setOutsideInterceptTouchEvent(false); + setInterceptKeyEvent(false); + setBackgroundDimDefault(); + setSwipeDismiss(SwipeLayout.Direction.TOP); + addOnClickToDismissListener(new OnClickListener() { @Override - public void onClick(Layer layer, View v) { + public void onClick(@NonNull Layer layer, @NonNull View v) { if (onQuickClickListener != null) { onQuickClickListener.onCopyLink(); } } }, R.id.dialog_web_quick_tv_copy_link); - onClickToDismiss(new OnClickListener() { + addOnClickToDismissListener(new OnClickListener() { @Override - public void onClick(Layer layer, View v) { + public void onClick(@NonNull Layer layer, @NonNull View v) { if (onQuickClickListener != null) { onQuickClickListener.onBrowser(); } } }, R.id.dialog_web_quick_tv_browser); + addOnClickToDismissListener(new OnClickListener() { + @Override + public void onClick(@NonNull Layer layer, @NonNull View v) { + if (onQuickClickListener != null) { + onQuickClickListener.onWanPwd(); + } + } + }, R.id.dialog_web_quick_tv_wanpwd); } public interface OnQuickClickListener { void onCopyLink(); void onBrowser(); + + void onWanPwd(); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebShareDialog.java b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebShareDialog.java index ad94707a..23de42d6 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebShareDialog.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/dialog/WebShareDialog.java @@ -1,21 +1,19 @@ package per.goweii.wanandroid.module.main.dialog; -import android.animation.Animator; import android.content.Context; -import android.support.annotation.NonNull; import android.view.Gravity; import android.view.View; -import per.goweii.anylayer.AnimatorHelper; +import androidx.annotation.NonNull; + import per.goweii.anylayer.AnyLayer; import per.goweii.anylayer.Layer; +import per.goweii.anylayer.widget.SwipeLayout; import per.goweii.wanandroid.R; /** * @author CuiZhen * @date 2019/5/20 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WebShareDialog { @@ -23,33 +21,23 @@ public class WebShareDialog { public static void show(@NonNull Context context, @NonNull OnShareClickListener listener) { AnyLayer.dialog(context) - .contentView(R.layout.dialog_web_share) - .gravity(Gravity.BOTTOM) - .backgroundColorRes(R.color.dialog_bg) - .contentAnimator(new Layer.AnimatorCreator() { - @Override - public Animator createInAnimator(View target) { - return AnimatorHelper.createBottomInAnim(target); - } - - @Override - public Animator createOutAnimator(View target) { - return AnimatorHelper.createBottomOutAnim(target); - } - }) - .onClickToDismiss(new Layer.OnClickListener() { - @Override - public void onClick(Layer layer, View v) { - switch (v.getId()) { - default: - break; - case R.id.dialog_web_share_iv_capture: - listener.onCapture(); - break; - case R.id.dialog_web_share_iv_qrcode: - listener.onQrcode(); - break; - } + .setContentView(R.layout.dialog_web_share) + .setGravity(Gravity.BOTTOM) + .setBackgroundDimDefault() + .setSwipeDismiss(SwipeLayout.Direction.BOTTOM) + .addOnClickToDismissListener(new Layer.OnClickListener() { + @Override + public void onClick(@NonNull Layer layer, @NonNull View v) { + switch (v.getId()) { + default: + break; + case R.id.dialog_web_share_iv_capture: + listener.onCapture(); + break; + case R.id.dialog_web_share_iv_qrcode: + listener.onQrcode(); + break; + } } }, R.id.dialog_web_share_iv_capture, diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/fragment/ArticleListFragment.kt b/app/src/main/java/per/goweii/wanandroid/module/main/fragment/ArticleListFragment.kt new file mode 100644 index 00000000..5200ad13 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/fragment/ArticleListFragment.kt @@ -0,0 +1,31 @@ +package per.goweii.wanandroid.module.main.fragment + +import android.view.LayoutInflater +import android.view.ViewGroup +import per.goweii.basic.core.base.BaseFragment +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.FragmentArticleListBinding +import per.goweii.wanandroid.module.main.contract.ArticleListPresenter +import per.goweii.wanandroid.module.main.contract.ArticleListView + +/** + * @author CuiZhen + * @date 2020/3/22 + */ +class ArticleListFragment : BaseFragment(), ArticleListView { + + override fun initViewBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentArticleListBinding { + return FragmentArticleListBinding.inflate(inflater, container, false) + } + + override fun initPresenter(): ArticleListPresenter = ArticleListPresenter() + + override fun initView() { + } + + override fun loadData() { + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/fragment/BookmarkFragment.kt b/app/src/main/java/per/goweii/wanandroid/module/main/fragment/BookmarkFragment.kt new file mode 100644 index 00000000..d1c2255b --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/fragment/BookmarkFragment.kt @@ -0,0 +1,126 @@ +package per.goweii.wanandroid.module.main.fragment + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import com.scwang.smart.refresh.layout.api.RefreshFooter +import com.scwang.smart.refresh.layout.constant.RefreshState +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import per.goweii.basic.core.base.BaseFragment +import per.goweii.basic.core.utils.SmartRefreshUtils +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.FragmentBookmarkBinding +import per.goweii.wanandroid.db.model.ReadLaterModel +import per.goweii.wanandroid.event.CloseSecondFloorEvent +import per.goweii.wanandroid.event.ReadLaterEvent +import per.goweii.wanandroid.module.main.adapter.BookmarkAdapter +import per.goweii.wanandroid.module.main.contract.BookmarkPresenter +import per.goweii.wanandroid.module.main.contract.BookmarkView +import per.goweii.wanandroid.utils.MultiStateUtils.Companion.setEmptyAndErrorClick +import per.goweii.wanandroid.utils.MultiStateUtils.Companion.toContent +import per.goweii.wanandroid.utils.MultiStateUtils.Companion.toEmpty +import per.goweii.wanandroid.utils.MultiStateUtils.Companion.toError +import per.goweii.wanandroid.utils.MultiStateUtils.Companion.toLoading +import per.goweii.wanandroid.utils.RvConfigUtils +import per.goweii.wanandroid.utils.UrlOpenUtils +import per.goweii.wanandroid.widget.refresh.SimpleOnMultiListener + +class BookmarkFragment : BaseFragment(), BookmarkView { + + private lateinit var mAdapter: BookmarkAdapter + + private var offset = 0 + private val perPageCount = 20 + + override fun initViewBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentBookmarkBinding { + return FragmentBookmarkBinding.inflate(inflater, container, false) + } + + override fun initPresenter(): BookmarkPresenter = BookmarkPresenter() + + override fun initView() { + SmartRefreshUtils.with(binding.srl).pureScrollMode() + binding.srl.setOnMultiListener(object : SimpleOnMultiListener() { + override fun onFooterMoving(footer: RefreshFooter?, isDragging: Boolean, percent: Float, offset: Int, footerHeight: Int, maxDragHeight: Int) { + super.onFooterMoving(footer, isDragging, percent, offset, footerHeight, maxDragHeight) + if (binding.srl.state != RefreshState.PullUpCanceled && isDragging && percent > 1.2F) { + binding.srl.closeHeaderOrFooter() + CloseSecondFloorEvent().post() + } + } + }) + binding.rv.layoutManager = LinearLayoutManager(context) + mAdapter = BookmarkAdapter() + RvConfigUtils.init(mAdapter) + mAdapter.setEnableLoadMore(true) + mAdapter.setOnLoadMoreListener({ getPageList() }, binding.rv) + mAdapter.setOnItemClickListener { _, _, position -> + mAdapter.getItem(position)?.let { item -> + UrlOpenUtils.with(item.link).open(context) + } + } + binding.rv.adapter = mAdapter + setEmptyAndErrorClick(binding.msv) { + toLoading(binding.msv) + offset = 0 + getPageList() + } + } + + override fun loadData() { + toLoading(binding.msv) + } + + override fun onVisible(isFirstVisible: Boolean) { + super.onVisible(isFirstVisible) + offset = 0 + getPageList() + } + + override fun onInvisible() { + super.onInvisible() + } + + override fun isRegisterEventBus(): Boolean = true + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onReadLaterEvent(event: ReadLaterEvent) { + if (!isAdded) return + offset = 0 + presenter.getList(0, perPageCount) + } + + private fun getPageList() { + presenter.getList(offset, perPageCount) + } + + override fun getBookmarkListSuccess(list: List) { + if (offset == 0) { + mAdapter.setNewData(list) + if (list.isEmpty()) { + toEmpty(binding.msv, true) + } else { + toContent(binding.msv) + } + } else { + mAdapter.addData(list) + mAdapter.loadMoreComplete() + } + offset = mAdapter.data.size + if (list.size < perPageCount) { + mAdapter.loadMoreEnd() + } + } + + override fun getBookmarkListFailed() { + if (offset == 0) { + toError(binding.msv) + } else { + mAdapter.loadMoreFail() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/fragment/KnowledgeNavigationFragment.java b/app/src/main/java/per/goweii/wanandroid/module/main/fragment/KnowledgeNavigationFragment.java index e2ee60d7..a78ae1fb 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/fragment/KnowledgeNavigationFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/fragment/KnowledgeNavigationFragment.java @@ -1,30 +1,34 @@ package per.goweii.wanandroid.module.main.fragment; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.viewpager.widget.ViewPager; import butterknife.BindView; import per.goweii.actionbarex.ActionBarEx; import per.goweii.basic.core.adapter.FixedFragmentPagerAdapter; import per.goweii.basic.core.base.BaseFragment; +import per.goweii.basic.core.base.BasePresenter; import per.goweii.basic.core.mvp.MvpPresenter; import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.wanandroid.R; import per.goweii.wanandroid.common.Config; -import per.goweii.wanandroid.common.ScrollTop; +import per.goweii.wanandroid.databinding.FragmentKnowledgeNavigationBinding; import per.goweii.wanandroid.module.knowledge.fragment.KnowledgeFragment; import per.goweii.wanandroid.module.navigation.fragment.NaviFragment; import per.goweii.wanandroid.utils.MagicIndicatorUtils; +import per.goweii.wanandroid.utils.RvScrollTopUtils; /** * @author CuiZhen * @date 2019/5/19 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class KnowledgeNavigationFragment extends BaseFragment implements ScrollTop { +public class KnowledgeNavigationFragment extends BaseFragment implements RvScrollTopUtils.ScrollTop { @BindView(R.id.ab) ActionBarEx ab; @@ -39,27 +43,25 @@ public static KnowledgeNavigationFragment create() { return new KnowledgeNavigationFragment(); } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_knowledge_navigation; + protected FragmentKnowledgeNavigationBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentKnowledgeNavigationBinding.inflate(inflater, container, false); } @Nullable @Override - protected MvpPresenter initPresenter() { + protected BasePresenter initPresenter() { return null; } @Override protected void initView() { mAdapter = new FixedFragmentPagerAdapter(getChildFragmentManager()); - mAdapter.setTitles("体系", "导航"); - mAdapter.setFragmentList( - KnowledgeFragment.create(), - NaviFragment.create() - ); + mAdapter.setTitles(getString(R.string.navigation), getString(R.string.architecture)); + mAdapter.setFragmentList(NaviFragment.create(), KnowledgeFragment.create()); vp.setAdapter(mAdapter); - MagicIndicatorUtils.commonNavigator(ab.getView(R.id.mi), vp, mAdapter, new SimpleCallback(){ + MagicIndicatorUtils.commonNavigator(ab.getView(R.id.mi), vp, mAdapter, new SimpleCallback() { @Override public void onResult(Integer data) { notifyScrollTop(data); @@ -80,8 +82,8 @@ public void onVisible(boolean isFirstVisible) { public void scrollTop() { if (isAdded() && !isDetached()) { Fragment fragment = mAdapter.getItem(vp.getCurrentItem()); - if (fragment instanceof ScrollTop) { - ScrollTop scrollTop = (ScrollTop) fragment; + if (fragment instanceof RvScrollTopUtils.ScrollTop) { + RvScrollTopUtils.ScrollTop scrollTop = (RvScrollTopUtils.ScrollTop) fragment; scrollTop.scrollTop(); } } @@ -91,8 +93,8 @@ private void notifyScrollTop(int pos) { long currClickTime = System.currentTimeMillis(); if (lastClickPos == pos && currClickTime - lastClickTime <= Config.SCROLL_TOP_DOUBLE_CLICK_DELAY) { Fragment fragment = mAdapter.getItem(vp.getCurrentItem()); - if (fragment instanceof ScrollTop) { - ScrollTop scrollTop = (ScrollTop) fragment; + if (fragment instanceof RvScrollTopUtils.ScrollTop) { + RvScrollTopUtils.ScrollTop scrollTop = (RvScrollTopUtils.ScrollTop) fragment; scrollTop.scrollTop(); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/fragment/MainFragment.java b/app/src/main/java/per/goweii/wanandroid/module/main/fragment/MainFragment.java index 54cf89b6..9034eaca 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/fragment/MainFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/fragment/MainFragment.java @@ -1,65 +1,83 @@ package per.goweii.wanandroid.module.main.fragment; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.content.ContextCompat; -import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; import android.view.View; -import android.widget.ImageView; +import android.view.ViewGroup; import android.widget.LinearLayout; -import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.OnApplyWindowInsetsListener; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.viewpager.widget.ViewPager; + +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.util.ArrayList; +import java.util.List; import butterknife.BindView; -import butterknife.OnClick; -import per.goweii.basic.core.adapter.FixedFragmentPagerAdapter; +import per.goweii.basic.core.adapter.TabFragmentPagerAdapter; import per.goweii.basic.core.base.BaseFragment; import per.goweii.basic.core.base.BasePresenter; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.common.Config; -import per.goweii.wanandroid.common.ScrollTop; +import per.goweii.wanandroid.databinding.FragmentMainBinding; +import per.goweii.wanandroid.event.CloseSecondFloorEvent; +import per.goweii.wanandroid.event.MessageCountEvent; +import per.goweii.wanandroid.event.SettingChangeEvent; import per.goweii.wanandroid.module.home.fragment.HomeFragment; +import per.goweii.wanandroid.module.main.adapter.MainTabAdapter; +import per.goweii.wanandroid.module.main.model.TabEntity; import per.goweii.wanandroid.module.mine.fragment.MineFragment; -import per.goweii.wanandroid.module.project.fragment.ProjectFragment; -import per.goweii.wanandroid.module.wxarticle.fragment.WxFragment; +import per.goweii.wanandroid.module.question.fragment.QuestionFragment; +import per.goweii.wanandroid.utils.SettingUtils; -public class MainFragment extends BaseFragment implements ViewPager.OnPageChangeListener { +public class MainFragment extends BaseFragment { @BindView(R.id.vp_tab) ViewPager vp_tab; - @BindView(R.id.ll_bb) - LinearLayout ll_bb; - @BindView(R.id.iv_bb_home) - ImageView iv_bb_home; - @BindView(R.id.tv_bb_home) - TextView tv_bb_home; - @BindView(R.id.iv_bb_knowledge) - ImageView iv_bb_knowledge; - @BindView(R.id.tv_bb_knowledge) - TextView tv_bb_knowledge; - @BindView(R.id.iv_bb_wechat) - ImageView iv_bb_wechat; - @BindView(R.id.tv_bb_wechat) - TextView tv_bb_wechat; - @BindView(R.id.iv_bb_project) - ImageView iv_bb_project; - @BindView(R.id.tv_bb_project) - TextView tv_bb_project; - @BindView(R.id.iv_bb_mine) - ImageView iv_bb_mine; - @BindView(R.id.tv_bb_mine) - TextView tv_bb_mine; - - private FixedFragmentPagerAdapter mPagerAdapter; - private long lastClickTime = 0L; - private int lastClickPos = 0; + @BindView(R.id.ll_bottom_bar) + LinearLayout ll_bottom_bar; + //@BindView(R.id.bvef) + //BackdropVisualEffectView bvef; + + private TabFragmentPagerAdapter.Page mHomePage; + private TabFragmentPagerAdapter.Page mSquarePage; + private TabFragmentPagerAdapter.Page mNaviPage; + private TabFragmentPagerAdapter.Page mQAPage; + private TabFragmentPagerAdapter.Page mMinePage; + private TabFragmentPagerAdapter mTabFragmentPagerAdapter; public static MainFragment create() { return new MainFragment(); } + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMessageCountEvent(MessageCountEvent event) { + if (isDetached()) return; + mMinePage.getData().setMsgCount(event.getCount()); + mTabFragmentPagerAdapter.notifyPageDataChanged(); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onSettingChangeEvent(SettingChangeEvent event) { + if (isDetached()) return; + if (event.isShowQAChanged()) { + mTabFragmentPagerAdapter.setPages(createPages()); + } + } + @Override - protected int getLayoutRes() { - return R.layout.fragment_main; + protected boolean isRegisterEventBus() { + return true; + } + + @Nullable + @Override + protected FragmentMainBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentMainBinding.inflate(inflater, container, false); } @Nullable @@ -70,104 +88,91 @@ protected BasePresenter initPresenter() { @Override protected void initView() { - vp_tab.addOnPageChangeListener(this); - vp_tab.setOffscreenPageLimit(4); - mPagerAdapter = new FixedFragmentPagerAdapter(getChildFragmentManager()); - vp_tab.setAdapter(mPagerAdapter); - mPagerAdapter.setFragmentList( - HomeFragment.create(), - KnowledgeNavigationFragment.create(), - WxFragment.create(), - ProjectFragment.create(), - MineFragment.create() - ); - vp_tab.setCurrentItem(0); - onPageSelected(vp_tab.getCurrentItem()); - } + mTabFragmentPagerAdapter = new TabFragmentPagerAdapter<>(getChildFragmentManager(), vp_tab, ll_bottom_bar, R.layout.tab_item_main); + mTabFragmentPagerAdapter.setPages(createPages()); - @Override - protected void loadData() { - } + vp_tab.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } - @OnClick({ - R.id.ll_bb_home, R.id.ll_bb_knowledge, R.id.ll_bb_wechat, R.id.ll_bb_project, R.id.ll_bb_mine - }) - @Override - public void onClick(View v) { - super.onClick(v); + @Override + public void onPageSelected(int position) { + new CloseSecondFloorEvent().post(); + } + + @Override + public void onPageScrollStateChanged(int state) { + } + }); + //bvef.setShowDebugInfo(false); + //bvef.setOverlayColor(ResUtils.getThemeColor(bvef, R.attr.colorBottomBarOverlay)); + //bvef.setSimpleSize(8); + //bvef.setVisualEffect(new RSBlurEffect(getContext(), 8)); + + ViewCompat.setOnApplyWindowInsetsListener(binding.flBottomBar, new OnApplyWindowInsetsListener() { + @NonNull + @Override + public WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) { + final int b = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom; + binding.flBottomBar.setPadding(0, 0, 0, b); + return insets; + } + }); } - @Override - protected boolean onClick1(View v) { - switch (v.getId()) { - default: - return false; - case R.id.ll_bb_home: - vp_tab.setCurrentItem(0); - break; - case R.id.ll_bb_knowledge: - vp_tab.setCurrentItem(1); - break; - case R.id.ll_bb_wechat: - vp_tab.setCurrentItem(2); - break; - case R.id.ll_bb_project: - vp_tab.setCurrentItem(3); - break; - case R.id.ll_bb_mine: - vp_tab.setCurrentItem(4); - break; + @NonNull + private List> createPages() { + final List> pages = new ArrayList<>(); + + if (mHomePage == null) { + mHomePage = new TabFragmentPagerAdapter.Page<>(R.string.homepage, HomeFragment.create(), + new TabEntity(getString(R.string.homepage), R.drawable.ic_bottom_bar_home, -1), + new MainTabAdapter()); } - notifyScrollTop(vp_tab.getCurrentItem()); - return true; - } + pages.add(mHomePage); + + if (mSquarePage == null) { + mSquarePage = new TabFragmentPagerAdapter.Page<>(2, UserArticleFragment.create(), + new TabEntity(getString(R.string.square), R.drawable.ic_bottom_bar_square, -1), + new MainTabAdapter()); + } + pages.add(mSquarePage); - private void notifyScrollTop(int pos) { - long currClickTime = System.currentTimeMillis(); - if (lastClickPos == pos && currClickTime - lastClickTime <= Config.SCROLL_TOP_DOUBLE_CLICK_DELAY) { - Fragment fragment = mPagerAdapter.getItem(pos); - if (fragment instanceof ScrollTop) { - ScrollTop scrollTop = (ScrollTop) fragment; - scrollTop.scrollTop(); + if (SettingUtils.getInstance().isShowQA()) { + if (mQAPage == null) { + mQAPage = new TabFragmentPagerAdapter.Page<>(3, QuestionFragment.create(), + new TabEntity(getString(R.string.qa), R.drawable.ic_bottom_bar_ques, -1), + new MainTabAdapter()); } + pages.add(mQAPage); + } else { + mQAPage = null; } - lastClickPos = pos; - lastClickTime = currClickTime; - } - @Override - public void onPageScrolled(int i, float v, int i1) { + if (mNaviPage == null) { + mNaviPage = new TabFragmentPagerAdapter.Page<>(4, KnowledgeNavigationFragment.create(), + new TabEntity(getString(R.string.navigation), R.drawable.ic_bottom_bar_navi, -1), + new MainTabAdapter()); + } + pages.add(mNaviPage); + + if (mMinePage == null) { + mMinePage = new TabFragmentPagerAdapter.Page<>(5, MineFragment.create(), + new TabEntity(getString(R.string.mine), R.drawable.ic_bottom_bar_mine, -1), + new MainTabAdapter()); + } + pages.add(mMinePage); + + return pages; } @Override - public void onPageSelected(int i) { - iv_bb_home.setColorFilter(ContextCompat.getColor(getContext(), R.color.third)); - iv_bb_knowledge.setColorFilter(ContextCompat.getColor(getContext(), R.color.third)); - iv_bb_wechat.setColorFilter(ContextCompat.getColor(getContext(), R.color.third)); - iv_bb_project.setColorFilter(ContextCompat.getColor(getContext(), R.color.third)); - iv_bb_mine.setColorFilter(ContextCompat.getColor(getContext(), R.color.third)); - switch (i) { - default: - break; - case 0: - iv_bb_home.setColorFilter(ContextCompat.getColor(getContext(), R.color.main)); - break; - case 1: - iv_bb_knowledge.setColorFilter(ContextCompat.getColor(getContext(), R.color.main)); - break; - case 2: - iv_bb_wechat.setColorFilter(ContextCompat.getColor(getContext(), R.color.main)); - break; - case 3: - iv_bb_project.setColorFilter(ContextCompat.getColor(getContext(), R.color.main)); - break; - case 4: - iv_bb_mine.setColorFilter(ContextCompat.getColor(getContext(), R.color.main)); - break; - } + protected void loadData() { } @Override - public void onPageScrollStateChanged(int i) { + protected void onVisible(boolean isFirstVisible) { + super.onVisible(isFirstVisible); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/fragment/UserArticleFragment.java b/app/src/main/java/per/goweii/wanandroid/module/main/fragment/UserArticleFragment.java index 3f9cb8f7..5c57ad52 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/fragment/UserArticleFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/fragment/UserArticleFragment.java @@ -1,19 +1,28 @@ package per.goweii.wanandroid.module.main.fragment; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; +import com.chad.library.adapter.base.entity.MultiItemEntity; +import com.google.android.gms.ads.nativead.NativeAd; import com.kennyc.view.MultiStateView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import butterknife.BindView; import per.goweii.actionbarex.common.ActionBarCommon; @@ -21,32 +30,35 @@ import per.goweii.basic.core.base.BaseFragment; import per.goweii.basic.core.utils.SmartRefreshUtils; import per.goweii.basic.ui.toast.ToastMaker; +import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.basic.utils.listener.SimpleListener; +import per.goweii.wanandroid.utils.GoogleAdUnitIds; +import per.goweii.component.ad.NativeAdProvider; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.common.Config; +import per.goweii.wanandroid.databinding.FragmentUserArticleBinding; import per.goweii.wanandroid.event.ArticleShareEvent; import per.goweii.wanandroid.event.CollectionEvent; import per.goweii.wanandroid.event.LoginEvent; -import per.goweii.wanandroid.event.SettingChangeEvent; import per.goweii.wanandroid.module.main.activity.ShareArticleActivity; -import per.goweii.wanandroid.module.main.activity.WebActivity; import per.goweii.wanandroid.module.main.adapter.ArticleAdapter; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.ArticleListBean; +import per.goweii.wanandroid.module.main.model.NativeAdBean; import per.goweii.wanandroid.module.main.presenter.UserArticlePresenter; import per.goweii.wanandroid.module.main.view.UserArticleView; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; -import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.RvScrollTopUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; +import per.goweii.wanandroid.utils.cdkey.CDKeyUtils; import per.goweii.wanandroid.widget.CollectView; /** * @author CuiZhen * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class UserArticleFragment extends BaseFragment implements UserArticleView { +public class UserArticleFragment extends BaseFragment implements UserArticleView, RvScrollTopUtils.ScrollTop { private static final int PAGE_START = 0; @@ -64,6 +76,8 @@ public class UserArticleFragment extends BaseFragment impl private int currPage = PAGE_START; + private long lastClickTime = 0L; + public static UserArticleFragment create() { return new UserArticleFragment(); } @@ -73,17 +87,7 @@ public void onCollectionEvent(CollectionEvent event) { if (event.getArticleId() == -1) { return; } - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.getId() == event.getArticleId()) { - if (item.isCollect() != event.isCollect()) { - item.setCollect(event.isCollect()); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - break; - } - } + mAdapter.notifyCollectionEvent(event); } @Subscribe(threadMode = ThreadMode.MAIN) @@ -92,14 +96,7 @@ public void onLoginEvent(LoginEvent event) { currPage = PAGE_START; getProjectArticleList(true); } else { - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.isCollect()) { - item.setCollect(false); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - } + mAdapter.notifyAllUnCollect(); } } @@ -109,21 +106,15 @@ public void onArticleShareEvent(ArticleShareEvent event) { getProjectArticleList(true); } - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); - } - } - @Override protected boolean isRegisterEventBus() { return true; } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_user_article; + protected FragmentUserArticleBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentUserArticleBinding.inflate(inflater, container, false); } @Nullable @@ -134,6 +125,16 @@ protected UserArticlePresenter initPresenter() { @Override protected void initView() { + abc.getTitleTextView().setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + long currClickTime = System.currentTimeMillis(); + if (currClickTime - lastClickTime <= Config.SCROLL_TOP_DOUBLE_CLICK_DELAY) { + RvScrollTopUtils.smoothScrollTop(rv); + } + lastClickTime = currClickTime; + } + }); abc.setOnRightIconClickListener(new OnActionBarChildClickListener() { @Override public void onClick(View v) { @@ -151,7 +152,6 @@ public void onRefresh() { }); rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new ArticleAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); mAdapter.setEnableLoadMore(false); mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override @@ -162,18 +162,18 @@ public void onLoadMoreRequested() { mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - WebActivity.start(getContext(), item); + UrlOpenUtils.Companion.with(item).open(getContext()); } } }); mAdapter.setOnItemChildViewClickListener(new ArticleAdapter.OnItemChildViewClickListener() { @Override public void onCollectClick(BaseViewHolder helper, CollectView v, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - if (!v.isChecked()) { + if (v.isChecked()) { presenter.collect(item, v); } else { presenter.uncollect(item, v); @@ -197,6 +197,19 @@ protected void loadData() { MultiStateUtils.toLoading(msv); currPage = PAGE_START; presenter.getUserArticleListCache(currPage); + +// if (!CDKeyUtils.getInstance().isActive()) { +// new NativeAdProvider(requireActivity(), getViewLifecycleOwner(), GoogleAdUnitIds.ARTICLE_LIST_NATIVE_AD).load(new SimpleCallback() { +// @Override +// public void onResult(NativeAd data) { +// LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager(); +// if (layoutManager != null) { +// int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition(); +// mAdapter.addData(lastVisibleItemPosition + 1, new NativeAdBean(data)); +// } +// } +// }); +// } } @Override @@ -212,11 +225,37 @@ public void getProjectArticleList(boolean refresh) { presenter.getUserArticleList(currPage, refresh); } + @Override + public void scrollTop() { + if (isAdded() && !isDetached()) { + RvScrollTopUtils.smoothScrollTop(rv); + } + } + @Override public void getUserArticleListSuccess(int code, ArticleListBean data) { currPage = data.getCurPage() + PAGE_START; if (data.getCurPage() == 1) { - mAdapter.setNewData(data.getDatas()); + Map ads = new HashMap<>(1); + List oldList = mAdapter.getData(); + for (int i = 0; i < oldList.size(); i++) { + MultiItemEntity item = oldList.get(i); + if (item.getItemType() == ArticleAdapter.ITEM_TYPE_AD) { + ads.put(i, (NativeAdBean) item); + } + } + List newList = new ArrayList<>(data.getDatas()); + if (!ads.isEmpty()) { + for (Map.Entry entry : ads.entrySet()) { + int i = entry.getKey(); + if (i > 0 && i < newList.size()) { + newList.add(i, entry.getValue()); + } else { + newList.add(entry.getValue()); + } + } + } + mAdapter.setNewData(newList); mAdapter.setEnableLoadMore(true); if (data.getDatas() == null || data.getDatas().isEmpty()) { MultiStateUtils.toEmpty(msv); diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/AdvertBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/AdvertBean.java new file mode 100644 index 00000000..616a0f15 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/AdvertBean.java @@ -0,0 +1,109 @@ +package per.goweii.wanandroid.module.main.model; + +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.List; + +import per.goweii.rxhttp.request.base.BaseBean; +import per.goweii.wanandroid.module.home.model.ImageBean; + +public class AdvertBean extends BaseBean { + private String showMode; + /** + * [ + * 202001010000-202001020000, + * 202001010000-202001020000 + * ] + */ + @Nullable + private List showAtDate; + private int duration; + private String route; + private String image; + private List images; + + public String getShowMode() { + return showMode; + } + + public void setShowMode(String showMode) { + this.showMode = showMode; + } + + @Nullable + public List getShowAtDate() { + return showAtDate; + } + + public void setShowAtDate(@Nullable List showAtDate) { + this.showAtDate = showAtDate; + } + + public boolean shouldShowAtNow() { + if ((images == null || images.isEmpty()) && TextUtils.isEmpty(image)) { + return false; + } + List list = DateRangeEntity.parse(showAtDate); + if (list == null) { + return true; + } + for (DateRangeEntity entity : list) { + if (entity.containsNow()) { + return true; + } + } + return false; + } + + public int getDuration() { + return duration; + } + + public void setDuration(int duration) { + this.duration = duration; + } + + public String getImage() { + return image; + } + + public void setImage(String image) { + this.image = image; + } + + public List getImages() { + return images; + } + + public void setImages(List images) { + this.images = images; + } + + public String getRoute() { + return route; + } + + public void setRoute(String route) { + this.route = route; + } + + public enum ShowMode { + DO_NOT_SHOW, + ONCE_OF_DAY, + EVERY_TIME, + ; + + @NonNull + public static ShowMode from(String value) { + for (ShowMode showMode : values()) { + if (TextUtils.equals(value.toLowerCase(), showMode.name().toLowerCase())) { + return showMode; + } + } + return DO_NOT_SHOW; + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/ArticleBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/ArticleBean.java index 012a81a9..beb84811 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/model/ArticleBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/ArticleBean.java @@ -2,18 +2,22 @@ import android.text.TextUtils; +import com.chad.library.adapter.base.entity.MultiItemEntity; + import java.util.List; +import java.util.Objects; +import per.goweii.basic.utils.ResUtils; import per.goweii.rxhttp.request.base.BaseBean; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.module.main.adapter.ArticleAdapter; /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class ArticleBean extends BaseBean { +public class ArticleBean extends BaseBean implements MultiItemEntity { /** * apkLink : * author : 玉刚说 @@ -33,7 +37,7 @@ public class ArticleBean extends BaseBean { * publishTime : 1557072000000 * superChapterId : 408 * superChapterName : 公众号 - * tags : [{"name":"公众号","url":"/https/github.com/wxarticle/list/410/1"}] + * tags : [{"name":"WX","url":"/https/github.com/wxarticle/list/410/1"}] * title : 深扒 EventBus:register * type : 0 * userId : -1 @@ -69,6 +73,18 @@ public class ArticleBean extends BaseBean { private List tags; private int originId; + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + ArticleBean bean = (ArticleBean) o; + return chapterId == bean.chapterId && collect == bean.collect && courseId == bean.courseId && top == bean.top && fresh == bean.fresh && id == bean.id && publishTime == bean.publishTime && superChapterId == bean.superChapterId && type == bean.type && userId == bean.userId && visible == bean.visible && zan == bean.zan && originId == bean.originId && Objects.equals(apkLink, bean.apkLink) && Objects.equals(author, bean.author) && Objects.equals(shareUser, bean.shareUser) && Objects.equals(chapterName, bean.chapterName) && Objects.equals(desc, bean.desc) && Objects.equals(envelopePic, bean.envelopePic) && Objects.equals(link, bean.link) && Objects.equals(niceDate, bean.niceDate) && Objects.equals(origin, bean.origin) && Objects.equals(prefix, bean.prefix) && Objects.equals(projectLink, bean.projectLink) && Objects.equals(superChapterName, bean.superChapterName) && Objects.equals(title, bean.title) && Objects.equals(tags, bean.tags); + } + + @Override + public int hashCode() { + return Objects.hash(apkLink, author, shareUser, chapterId, chapterName, collect, courseId, desc, envelopePic, top, fresh, id, link, niceDate, origin, prefix, projectLink, publishTime, superChapterId, superChapterName, title, type, userId, visible, zan, tags, originId); + } + public String getApkLink() { return apkLink; } @@ -84,7 +100,7 @@ public String getAuthor() { if (!TextUtils.isEmpty(shareUser)) { return shareUser; } - return "匿名"; + return ResUtils.getString(R.string.anonymous); } public String getShareUser() { @@ -291,6 +307,11 @@ public void setTags(List tags) { this.tags = tags; } + @Override + public int getItemType() { + return ArticleAdapter.ITEM_TYPE_ARTICLE; + } + public static class TagsBean extends BaseBean { /** * name : 公众号 diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/ArticleListBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/ArticleListBean.java index f13d1f3e..2636ffdc 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/model/ArticleListBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/ArticleListBean.java @@ -1,88 +1,9 @@ package per.goweii.wanandroid.module.main.model; -import java.util.List; - -import per.goweii.rxhttp.request.base.BaseBean; - /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class ArticleListBean extends BaseBean { - /** - * curPage : 2 - * datas : [{"apkLink":"","author":"玉刚说","chapterId":410,"chapterName":"玉刚说","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8367,"link":"https://mp.weixin.qq.com/s/uI7Fej1_qSJOJnzQ6offpw","niceDate":"2019-05-06","origin":"","prefix":"","projectLink":"","publishTime":1557072000000,"superChapterId":408,"superChapterName":"公众号","tags":[{"name":"公众号","url":"/https/github.com/wxarticle/list/410/1"}],"title":"深扒 EventBus:register","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"鸿洋","chapterId":408,"chapterName":"鸿洋","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8371,"link":"https://mp.weixin.qq.com/s/8ceAAAt8FEL0Se-8--7CfQ","niceDate":"2019-05-06","origin":"","prefix":"","projectLink":"","publishTime":1557072000000,"superChapterId":408,"superChapterName":"公众号","tags":[{"name":"公众号","url":"/https/github.com/wxarticle/list/408/1"}],"title":"我在一个群分享Android 好像被我分享得没人说话了... 2期","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"三好码农","chapterId":77,"chapterName":"响应式编程","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8359,"link":"https://juejin.im/post/5b66eeaa6fb9a04fd93e5369","niceDate":"2019-05-05","origin":"","prefix":"","projectLink":"","publishTime":1557063177000,"superChapterId":184,"superChapterName":"热门专题","tags":[],"title":"RxJava2源码解读之 Map、FlatMap","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"玉刚说","chapterId":410,"chapterName":"玉刚说","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8366,"link":"https://mp.weixin.qq.com/s/MRv1aMJD8XtTEP0keoqerA","niceDate":"2019-05-05","origin":"","prefix":"","projectLink":"","publishTime":1556985600000,"superChapterId":408,"superChapterName":"公众号","tags":[{"name":"公众号","url":"/https/github.com/wxarticle/list/410/1"}],"title":"从零开始实现一个 mini-Retrofit 框架","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"code小生","chapterId":414,"chapterName":"code小生","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8368,"link":"https://mp.weixin.qq.com/s/_FeFjeCkpPeiPq1I6VoRFw","niceDate":"2019-05-05","origin":"","prefix":"","projectLink":"","publishTime":1556985600000,"superChapterId":408,"superChapterName":"公众号","tags":[{"name":"公众号","url":"/https/github.com/wxarticle/list/414/1"}],"title":"Android 中的红点提示怎么统一实现","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"鸿洋","chapterId":408,"chapterName":"鸿洋","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8370,"link":"https://mp.weixin.qq.com/s/0EprsJ7sXKmphghMsU3aGw","niceDate":"2019-05-05","origin":"","prefix":"","projectLink":"","publishTime":1556985600000,"superChapterId":408,"superChapterName":"公众号","tags":[{"name":"公众号","url":"/https/github.com/wxarticle/list/408/1"}],"title":"微信自研APM利器Matrix 卡顿分析工具之Trace Canary","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"郭霖","chapterId":409,"chapterName":"郭霖","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8373,"link":"https://mp.weixin.qq.com/s/5CeZ6NHF6dm3qN6RgzaGDQ","niceDate":"2019-05-05","origin":"","prefix":"","projectLink":"","publishTime":1556985600000,"superChapterId":408,"superChapterName":"公众号","tags":[{"name":"公众号","url":"/https/github.com/wxarticle/list/409/1"}],"title":"程序媛说源码:AsyncTask在子线程创建与调用的那些事儿","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"承香墨影","chapterId":411,"chapterName":"承香墨影","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8375,"link":"https://mp.weixin.qq.com/s/JndDaQTkmbo05ciT1OVW1w","niceDate":"2019-05-05","origin":"","prefix":"","projectLink":"","publishTime":1556985600000,"superChapterId":408,"superChapterName":"公众号","tags":[{"name":"公众号","url":"/https/github.com/wxarticle/list/411/1"}],"title":"""转 Int,{} 转 List,还有什么奇葩的 JSON 要容错?| 实战","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"飞吧小蚊子","chapterId":443,"chapterName":"Android 10.0","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8358,"link":"https://www.jianshu.com/p/77f319ea53aa","niceDate":"2019-05-04","origin":"","prefix":"","projectLink":"","publishTime":1556976828000,"superChapterId":183,"superChapterName":"5.+高新技术","tags":[],"title":"Android Q适配(1)-------图标篇","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"蒙伟","chapterId":31,"chapterName":"Dialog","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8357,"link":"https://www.jianshu.com/p/5f8e74726eee","niceDate":"2019-05-04","origin":"","prefix":"","projectLink":"","publishTime":1556976695000,"superChapterId":30,"superChapterName":"用户交互","tags":[],"title":"Android带进场退场动画的dialog对话框","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"TeaOf","chapterId":99,"chapterName":"具体案例","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8356,"link":"https://www.jianshu.com/p/f144d6645877","niceDate":"2019-05-04","origin":"","prefix":"","projectLink":"","publishTime":1556976665000,"superChapterId":94,"superChapterName":"自定义控件","tags":[],"title":"仿写一个QQ空间图片预览Dialog","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"sollian","chapterId":100,"chapterName":"RecyclerView","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8355,"link":"https://www.jianshu.com/p/4f66c2c71d8c","niceDate":"2019-05-04","origin":"","prefix":"","projectLink":"","publishTime":1556976626000,"superChapterId":183,"superChapterName":"5.+高新技术","tags":[],"title":"RecyclerView#Adapter使用中的两个陷阱","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"Peakmain","chapterId":78,"chapterName":"性能优化","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8354,"link":"https://www.jianshu.com/p/6d5cddd56d94","niceDate":"2019-05-04","origin":"","prefix":"","projectLink":"","publishTime":1556976553000,"superChapterId":184,"superChapterName":"热门专题","tags":[],"title":"新闻类App (MVP + RxJava + Retrofit+Dagger+ARouter)性能优化之启动优化","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"猛猛的小盆友","chapterId":99,"chapterName":"具体案例","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8353,"link":"https://www.jianshu.com/p/ec96396a470b","niceDate":"2019-05-04","origin":"","prefix":"","projectLink":"","publishTime":1556976514000,"superChapterId":94,"superChapterName":"自定义控件","tags":[],"title":"灵魂画师,Android绘制流程——Android高级UI","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"Mr_villain","chapterId":332,"chapterName":"嵌套滑动","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8352,"link":"https://www.jianshu.com/p/20efb9f65494","niceDate":"2019-05-04","origin":"","prefix":"","projectLink":"","publishTime":1556976465000,"superChapterId":183,"superChapterName":"5.+高新技术","tags":[],"title":"Android嵌套滑动机制实战演练","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"流水潺湲","chapterId":89,"chapterName":"app缓存相关","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8351,"link":"https://www.jianshu.com/p/41b98118decc","niceDate":"2019-05-04","origin":"","prefix":"","projectLink":"","publishTime":1556976402000,"superChapterId":89,"superChapterName":"数据存储","tags":[],"title":"Android缓存机制——一般存储实现","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"都有米 ","chapterId":134,"chapterName":"SurfaceView","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8350,"link":"https://www.jianshu.com/p/05a8f7e1dd3d","niceDate":"2019-05-04","origin":"","prefix":"","projectLink":"","publishTime":1556976373000,"superChapterId":94,"superChapterName":"自定义控件","tags":[],"title":"为啥从SurfaceView中获取不到图片?","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"若丨寒","chapterId":304,"chapterName":"基础源码","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8349,"link":"https://www.jianshu.com/p/69dad4eee143","niceDate":"2019-05-04","origin":"","prefix":"","projectLink":"","publishTime":1556976352000,"superChapterId":245,"superChapterName":"Java深入","tags":[],"title":"Java:优雅地处理异常真是一门学问啊!","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"未见哥哥","chapterId":78,"chapterName":"性能优化","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8348,"link":"https://www.jianshu.com/p/adeeee995bc5","niceDate":"2019-05-04","origin":"","prefix":"","projectLink":"","publishTime":1556976324000,"superChapterId":184,"superChapterName":"热门专题","tags":[],"title":"「性能优化4.0」运行期间检测不合理的图片","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"Keven","chapterId":249,"chapterName":"干货资源","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8347,"link":"https://juejin.im/post/5cc56099e51d456e8a12f01f","niceDate":"2019-05-04","origin":"","prefix":"","projectLink":"","publishTime":1556975595000,"superChapterId":249,"superChapterName":"干货资源","tags":[],"title":"GitHub 上优质项目整理","type":0,"userId":-1,"visible":1,"zan":0}] - * offset : 20 - * over : false - * pageCount : 323 - * size : 20 - * total : 6456 - */ - - private int curPage; - private int offset; - private boolean over; - private int pageCount; - private int size; - private int total; - private List datas; - - public int getCurPage() { - return curPage; - } - - public void setCurPage(int curPage) { - this.curPage = curPage; - } - - public int getOffset() { - return offset; - } - - public void setOffset(int offset) { - this.offset = offset; - } - - public boolean isOver() { - return over; - } - - public void setOver(boolean over) { - this.over = over; - } - - public int getPageCount() { - return pageCount; - } - - public void setPageCount(int pageCount) { - this.pageCount = pageCount; - } - - public int getSize() { - return size; - } - - public void setSize(int size) { - this.size = size; - } - - public int getTotal() { - return total; - } - - public void setTotal(int total) { - this.total = total; - } - - public List getDatas() { - return datas; - } - - public void setDatas(List datas) { - this.datas = datas; - } +public class ArticleListBean extends ListBean { } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/BetaUserBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/BetaUserBean.java new file mode 100644 index 00000000..94a0d0e8 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/BetaUserBean.java @@ -0,0 +1,24 @@ +package per.goweii.wanandroid.module.main.model; + +import per.goweii.rxhttp.request.base.BaseBean; + +public class BetaUserBean extends BaseBean { + private int userId; + private String userName; + + public int getUserId() { + return userId; + } + + public void setUserId(int userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/ChapterBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/ChapterBean.java index ad49479e..2bae91ff 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/model/ChapterBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/ChapterBean.java @@ -7,8 +7,6 @@ /** * @author CuiZhen * @date 2019/5/8 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ChapterBean extends BaseBean { diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/CoinInfoBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/CoinInfoBean.java index ce8ec632..b1bf856b 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/model/CoinInfoBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/CoinInfoBean.java @@ -5,8 +5,6 @@ /** * @author CuiZhen * @date 2019/10/3 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CoinInfoBean extends BaseBean { @@ -21,6 +19,7 @@ public class CoinInfoBean extends BaseBean { private int rank; private int userId; private String username; + public boolean anim; public int getCoinCount() { return coinCount; diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/CollectArticleEntity.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/CollectArticleEntity.java index 3691ef78..9ccea665 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/model/CollectArticleEntity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/CollectArticleEntity.java @@ -7,8 +7,6 @@ /** * @author CuiZhen * @date 2019/11/2 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CollectArticleEntity extends BaseEntity { diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/CollectionLinkBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/CollectionLinkBean.java index 8e5b3fc7..856c9c02 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/model/CollectionLinkBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/CollectionLinkBean.java @@ -5,8 +5,6 @@ /** * @author CuiZhen * @date 2019/5/20 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CollectionLinkBean extends BaseBean { diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/ConfigBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/ConfigBean.java index cd2bd97b..37ab8523 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/model/ConfigBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/ConfigBean.java @@ -1,32 +1,63 @@ package per.goweii.wanandroid.module.main.model; +import androidx.annotation.Nullable; + +import java.util.List; + import per.goweii.rxhttp.request.base.BaseBean; /** * @author CuiZhen * @date 2019/9/28 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ConfigBean extends BaseBean { - /** - * dailyMsg : - * actionBarBgColor : - * actionBarBgImageUrl : - */ - - private String dailyMsg; + @Nullable + private List enableAtDate; + private boolean grayFilter; + private String theme; private String homeTitle; private String actionBarBgColor; private String actionBarBgImageUrl; + private String secondFloorBgImageUrl; + private float secondFloorBgImageBlurPercent; + + public void setEnableAtDate(@Nullable List enableAtDate) { + this.enableAtDate = enableAtDate; + } + + @Nullable + public List getEnableAtDate() { + return enableAtDate; + } + + public boolean isEnableAtNow() { + List list = DateRangeEntity.parse(enableAtDate); + if (list == null) { + return true; + } + for (DateRangeEntity entity : list) { + if (entity.containsNow()) { + return true; + } + } + return false; + } + + public boolean isGrayFilter() { + return grayFilter; + } - public String getDailyMsg() { - return dailyMsg; + public void setGrayFilter(boolean grayFilter) { + this.grayFilter = grayFilter; } - public void setDailyMsg(String dailyMsg) { - this.dailyMsg = dailyMsg; + public String getTheme() { + return theme; + } + + public void setTheme(String theme) { + this.theme = theme; } public String getHomeTitle() { @@ -52,4 +83,20 @@ public String getActionBarBgImageUrl() { public void setActionBarBgImageUrl(String actionBarBgImageUrl) { this.actionBarBgImageUrl = actionBarBgImageUrl; } + + public String getSecondFloorBgImageUrl() { + return secondFloorBgImageUrl; + } + + public void setSecondFloorBgImageUrl(String secondFloorBgImageUrl) { + this.secondFloorBgImageUrl = secondFloorBgImageUrl; + } + + public float getSecondFloorBgImageBlurPercent() { + return secondFloorBgImageBlurPercent; + } + + public void setSecondFloorBgImageBlurPercent(float secondFloorBgImageBlurPercent) { + this.secondFloorBgImageBlurPercent = secondFloorBgImageBlurPercent; + } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/DateRangeEntity.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/DateRangeEntity.java new file mode 100644 index 00000000..9cdd1178 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/DateRangeEntity.java @@ -0,0 +1,82 @@ +package per.goweii.wanandroid.module.main.model; + +import android.annotation.SuppressLint; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import per.goweii.basic.core.base.BaseEntity; + +public class DateRangeEntity extends BaseEntity { + private final Date from; + private final Date to; + + public DateRangeEntity(@NonNull Date from, @NonNull Date to) { + if (from.before(to)) { + this.from = from; + this.to = to; + } else { + this.from = to; + this.to = from; + } + } + + public boolean containsNow() { + return contains(new Date(System.currentTimeMillis())); + } + + public boolean contains(@NonNull Date date) { + if (date.before(from)) { + return false; + } + if (date.after(to)) { + return false; + } + return true; + } + + /** + * 202001010000 + * or + * 202001010000-202001020000 + */ + @SuppressLint("SimpleDateFormat") + @Nullable + public static DateRangeEntity parse(String dateRange) { + try { + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmm"); + int index = dateRange.indexOf("-"); + if (index == -1) { + Date d = sdf.parse(dateRange); + return new DateRangeEntity(d, d); + } else { + Date from = sdf.parse(dateRange.substring(0, index)); + Date to = sdf.parse(dateRange.substring(index + 1)); + return new DateRangeEntity(from, to); + } + } catch (ParseException ignore) { + return null; + } + } + + @Nullable + public static List parse(@Nullable List dateRanges) { + if (dateRanges == null) { + return null; + } + List dateRangeList = new ArrayList<>(dateRanges.size()); + for (String dateRange : dateRanges) { + DateRangeEntity entity = DateRangeEntity.parse(dateRange); + if (entity != null) { + dateRangeList.add(entity); + } + } + return dateRangeList; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/JinrishiciBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/JinrishiciBean.java index 8fc1fecd..a49058fe 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/model/JinrishiciBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/JinrishiciBean.java @@ -7,8 +7,6 @@ /** * @author CuiZhen * @date 2019/11/10 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class JinrishiciBean extends BaseBean { diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/ListBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/ListBean.java new file mode 100644 index 00000000..f6d07687 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/ListBean.java @@ -0,0 +1,86 @@ +package per.goweii.wanandroid.module.main.model; + +import java.util.List; + +import per.goweii.rxhttp.request.base.BaseBean; + +/** + * @author CuiZhen + * @date 2019/5/12 + * GitHub: https://github.com/goweii + */ +public class ListBean extends BaseBean { + /** + * curPage : 2 + * datas : [] + * offset : 20 + * over : false + * pageCount : 323 + * size : 20 + * total : 6456 + */ + + private int curPage; + private int offset; + private boolean over; + private int pageCount; + private int size; + private int total; + private List datas; + + public int getCurPage() { + return curPage; + } + + public void setCurPage(int curPage) { + this.curPage = curPage; + } + + public int getOffset() { + return offset; + } + + public void setOffset(int offset) { + this.offset = offset; + } + + public boolean isOver() { + return over; + } + + public void setOver(boolean over) { + this.over = over; + } + + public int getPageCount() { + return pageCount; + } + + public void setPageCount(int pageCount) { + this.pageCount = pageCount; + } + + public int getSize() { + return size; + } + + public void setSize(int size) { + this.size = size; + } + + public int getTotal() { + return total; + } + + public void setTotal(int total) { + this.total = total; + } + + public List getDatas() { + return datas; + } + + public void setDatas(List datas) { + this.datas = datas; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/MainRequest.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/MainRequest.java index 4adf0e51..ec155856 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/model/MainRequest.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/MainRequest.java @@ -1,59 +1,126 @@ package per.goweii.wanandroid.module.main.model; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; -import android.text.TextUtils; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; + +import java.util.List; import io.reactivex.disposables.Disposable; +import per.goweii.basic.utils.ResUtils; import per.goweii.rxhttp.core.RxLife; import per.goweii.rxhttp.request.base.BaseBean; +import per.goweii.rxhttp.request.exception.ExceptionHandle; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.common.WanApp; import per.goweii.wanandroid.http.BaseRequest; import per.goweii.wanandroid.http.RequestCallback; import per.goweii.wanandroid.http.RequestListener; import per.goweii.wanandroid.http.WanApi; import per.goweii.wanandroid.http.WanCache; +import per.goweii.wanandroid.utils.UserUtils; /** * @author CuiZhen * @date 2019/5/16 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class MainRequest extends BaseRequest { - public static Disposable collect(int id, @NonNull RequestListener listener) { - return request(WanApi.api().collect(id), listener); + public static Disposable collectArticle(int id, @NonNull RequestListener listener) { + return request(WanApi.api().collectArticle(id), listener); } - public static Disposable collect(String title, String author, String link, @NonNull RequestListener listener) { - return request(WanApi.api().collect(title, author, link), listener); + public static Disposable collectArticle(String title, String author, String link, @NonNull RequestListener listener) { + return request(WanApi.api().collectArticle(title, author, link), listener); } - public static Disposable collect(String title, String link, @NonNull RequestListener listener) { - return request(WanApi.api().collect(title, link), listener); + public static Disposable collectLink(String title, String link, @NonNull RequestListener listener) { + return request(WanApi.api().collectLink(title, link), listener); } - public static Disposable uncollect(int id, @NonNull RequestListener listener) { - return request(WanApi.api().uncollect(id), listener); + public static Disposable uncollectArticle(int id, @NonNull RequestListener listener) { + return request(WanApi.api().uncollectArticle(id), listener); } public static Disposable uncollectLink(int id, @NonNull RequestListener listener) { return request(WanApi.api().uncollectLink(id), listener); } - public static Disposable uncollect(int id, int originId, @NonNull RequestListener listener) { - return request(WanApi.api().uncollect(id, originId), listener); + public static Disposable uncollectArticle(int id, int originId, @NonNull RequestListener listener) { + return request(WanApi.api().uncollectArticle(id, originId), listener); } public static void update(RxLife rxLife, @NonNull RequestListener listener) { rxLife.add(request(WanApi.api().update(), listener)); } + public static void betaUpdate(RxLife rxLife, @NonNull RequestListener listener) { + rxLife.add(request(WanApi.api().betaUsers(), new RequestListener>() { + @Override + public void onStart() { + listener.onStart(); + } + + @Override + public void onSuccess(int code, List data) { + boolean isBetaUser = false; + if (UserUtils.getInstance().isLogin()) { + for (BetaUserBean betaUserBean : data) { + if (betaUserBean.getUserId() == UserUtils.getInstance().getWanId()) { + isBetaUser = true; + break; + } + } + } + if (isBetaUser) { + rxLife.add(request(WanApi.api().betaUpdate(), new RequestCallback() { + @Override + public void onSuccess(int code, UpdateBean data) { + listener.onSuccess(code, data); + listener.onFinish(); + } + + @Override + public void onFailed(int code, String msg) { + listener.onFailed(code, msg); + listener.onFinish(); + } + })); + } else { + listener.onFailed(WanApi.ApiCode.ERROR, ResUtils.getString(R.string.not_the_beta_user)); + listener.onFinish(); + } + } + + @Override + public void onFailed(int code, String msg) { + listener.onFailed(code, msg); + listener.onFinish(); + } + + @Override + public void onError(ExceptionHandle handle) { + listener.onError(handle); + } + + @Override + public void onFinish() { + } + })); + } + public static void getConfig(RxLife rxLife, @NonNull RequestListener listener) { rxLife.add(request(WanApi.api().getConfig(), listener)); } + public static void getAdvert(RxLife rxLife, @NonNull RequestListener listener) { + rxLife.add(request(WanApi.api().getAdvert(), listener)); + } + + public static void getRecommend(RxLife rxLife, @NonNull RequestListener listener) { + rxLife.add(request(WanApi.api().getRecommend(), listener)); + } + public static void getUserArticleListCache(@IntRange(from = 0) int page, @NonNull RequestListener listener) { cacheBean(WanCache.CacheKey.USER_ARTICLE_LIST(page), ArticleListBean.class, @@ -91,35 +158,21 @@ public static Disposable shareArticle(String title, String link, @NonNull Reques } public static void getJinrishici(RxLife rxLife, @NonNull RequestListener listener) { - getJinrishiciToken(rxLife, new RequestCallback() { - @Override - public void onSuccess(int code, String data) { - request(WanApi.api().getJinrishici(data), listener); - } - - @Override - public void onFailed(int code, String msg) { - listener.onFailed(code, msg); - } - }); - } - - private static void getJinrishiciToken(RxLife rxLife, @NonNull RequestListener listener) { - cacheBean(WanCache.CacheKey.JINRISHICI_TOKEN, String.class, new RequestCallback() { - @Override - public void onSuccess(int code, String data) { - if (TextUtils.isEmpty(data)) { - rxLife.add(request(WanApi.api().getJinrishiciToken(), listener)); - } else { - listener.onSuccess(code, data); - } - } - - @Override - public void onFailed(int code, String msg) { - rxLife.add(request(WanApi.api().getJinrishiciToken(), listener)); - } - }); + cacheOrNetBean(rxLife, + WanApi.api().getJinrishiciToken(), + WanCache.CacheKey.JINRISHICI_TOKEN, + String.class, + new RequestCallback() { + @Override + public void onSuccess(int code, String data) { + rxLife.add(request(WanApi.api().getJinrishici(data), listener)); + } + + @Override + public void onFailed(int code, String msg) { + listener.onFailed(code, msg); + } + }); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/NativeAdBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/NativeAdBean.java new file mode 100644 index 00000000..412557c7 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/NativeAdBean.java @@ -0,0 +1,27 @@ +package per.goweii.wanandroid.module.main.model; + +import com.chad.library.adapter.base.entity.MultiItemEntity; +import com.google.android.gms.ads.nativead.NativeAd; + +import per.goweii.wanandroid.module.main.adapter.ArticleAdapter; + +public class NativeAdBean implements MultiItemEntity { + private NativeAd nativeAd; + + public NativeAdBean(NativeAd nativeAd) { + this.nativeAd = nativeAd; + } + + public void setNativeAd(NativeAd nativeAd) { + this.nativeAd = nativeAd; + } + + public NativeAd getNativeAd() { + return nativeAd; + } + + @Override + public int getItemType() { + return ArticleAdapter.ITEM_TYPE_AD; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/RecommendBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/RecommendBean.java new file mode 100644 index 00000000..15d2a4d1 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/RecommendBean.java @@ -0,0 +1,104 @@ +package per.goweii.wanandroid.module.main.model; + +import androidx.annotation.Nullable; + +import java.util.List; + +import per.goweii.rxhttp.request.base.BaseBean; + +public class RecommendBean extends BaseBean { + @Nullable + private List bannerList; + @Nullable + private ArticleFooter articleFooter; + + @Nullable + public List getBannerList() { + return bannerList; + } + + public void setBannerList(@Nullable List bannerList) { + this.bannerList = bannerList; + } + + @Nullable + public ArticleFooter getArticleFooter() { + return articleFooter; + } + + public void setArticleFooter(@Nullable ArticleFooter articleFooter) { + this.articleFooter = articleFooter; + } + + public static class BannerBean { + @Nullable + private String title; + @Nullable + private String url; + @Nullable + private String route; + + @Nullable + public String getTitle() { + return title; + } + + public void setTitle(@Nullable String title) { + this.title = title; + } + + @Nullable + public String getUrl() { + return url; + } + + public void setUrl(@Nullable String url) { + this.url = url; + } + + @Nullable + public String getRoute() { + return route; + } + + public void setRoute(@Nullable String route) { + this.route = route; + } + } + + public static class ArticleFooter { + @Nullable + private String title; + @Nullable + private String url; + @Nullable + private String route; + + @Nullable + public String getTitle() { + return title; + } + + public void setTitle(@Nullable String title) { + this.title = title; + } + + @Nullable + public String getUrl() { + return url; + } + + public void setUrl(@Nullable String url) { + this.url = url; + } + + @Nullable + public String getRoute() { + return route; + } + + public void setRoute(@Nullable String route) { + this.route = route; + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/TabEntity.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/TabEntity.java new file mode 100644 index 00000000..2125204e --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/TabEntity.java @@ -0,0 +1,33 @@ +package per.goweii.wanandroid.module.main.model; + +/** + * @author CuiZhen + * @date 2020/3/22 + */ +public class TabEntity { + private String tabName; + private int tabIcon; + private int msgCount; + + public TabEntity(String tabName, int tabIcon, int msgCount) { + this.tabName = tabName; + this.tabIcon = tabIcon; + this.msgCount = msgCount; + } + + public String getTabName() { + return tabName; + } + + public int getTabIcon() { + return tabIcon; + } + + public int getMsgCount() { + return msgCount; + } + + public void setMsgCount(int msgCount) { + this.msgCount = msgCount; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/UpdateBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/UpdateBean.java index 3743f0ab..e0d08d80 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/model/UpdateBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/UpdateBean.java @@ -5,25 +5,16 @@ /** * @author CuiZhen * @date 2019/5/19 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class UpdateBean extends BaseBean { - /** - * url : - * url_backup : - * version_name : 1.0.0 - * version_code : 0 - * force : true - * desc : - * time : 2019-05-19 - */ private String url; private String url_backup; private String version_name; private int version_code; + private String last_force_version_name; + private int last_force_version_code; private boolean force; private String desc; private String time; @@ -60,6 +51,22 @@ public void setVersion_code(int version_code) { this.version_code = version_code; } + public String getLast_force_version_name() { + return last_force_version_name; + } + + public void setLast_force_version_name(String last_force_version_name) { + this.last_force_version_name = last_force_version_name; + } + + public int getLast_force_version_code() { + return last_force_version_code; + } + + public void setLast_force_version_code(int last_force_version_code) { + this.last_force_version_code = last_force_version_code; + } + public boolean isForce() { return force; } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/UsefulWebBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/UsefulWebBean.java index 2138f600..053660e3 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/model/UsefulWebBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/UsefulWebBean.java @@ -5,15 +5,13 @@ /** * @author CuiZhen * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class UsefulWebBean extends BaseBean { /** * icon : * id : 17 - * link : http://www.wanandroid.com/article/list/0?cid=176 + * link : http://wanandroid.com/article/list/0?cid=176 * name : 国内大牛博客集合 * order : 1 * visible : 1 diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/UserPageBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/UserPageBean.java index ad66dbd6..52e65408 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/model/UserPageBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/UserPageBean.java @@ -5,14 +5,12 @@ /** * @author CuiZhen * @date 2019/10/3 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class UserPageBean extends BaseBean { /** * coinInfo : {"coinCount":1285,"rank":6,"userId":2,"username":"x**oyang"} - * shareArticles : {"curPage":1,"datas":[{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

从2019年10月1日更新记录,在这里也备份一份,有问题可以直接回复我。<\/p>","envelopePic":"","fresh":false,"id":9470,"link":"https://wanandroid.com/wenda/show/9470","niceDate":"1天前","niceShareDate":"1天前","origin":"","prefix":"","projectLink":"","publishTime":1569990724000,"selfVisible":0,"shareDate":1569990436000,"shareUser":"","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"玩Android更新记录 [>2019-10-02]","type":1,"userId":2,"visible":1,"zan":0},{"apkLink":"","audit":1,"author":"","chapterId":494,"chapterName":"广场","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":9410,"link":"https://juejin.im/post/5d8b34cff265da5b88209e7b","niceDate":"2019-09-28","niceShareDate":"2019-09-28","origin":"","prefix":"","projectLink":"","publishTime":1569662568000,"selfVisible":0,"shareDate":1569662568000,"shareUser":"鸿洋","superChapterId":494,"superChapterName":"广场Tab","tags":[],"title":"滴滴开源库Booster:模块功能作用分析说明","type":0,"userId":2,"visible":0,"zan":0},{"apkLink":"","audit":1,"author":"","chapterId":494,"chapterName":"广场","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":9409,"link":"https://www.jianshu.com/p/79b30238b994","niceDate":"2019-09-28","niceShareDate":"2019-09-28","origin":"","prefix":"","projectLink":"","publishTime":1569662550000,"selfVisible":0,"shareDate":1569662550000,"shareUser":"鸿洋","superChapterId":494,"superChapterName":"广场Tab","tags":[],"title":"Android常见安全风险及规避措施","type":0,"userId":2,"visible":0,"zan":0},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

就当是问卷啦,带大家一起发现一些好用的插件,欢迎分享出来哈。<\/p>\r\n

    \r\n
  1. 好用的 chrome 插件;<\/li>\r\n
  2. 好用的 AS 插件;<\/li>\r\n
  3. ...其他<\/li>\r\n<\/ol>\r\n

    如果有链接,最好能附上链接哈~<\/p>","envelopePic":"","fresh":false,"id":9286,"link":"https://www.wanandroid.com/wenda/show/9286","niceDate":"2019-09-25","niceShareDate":"2019-09-23","origin":"","prefix":"","projectLink":"","publishTime":1569419925000,"selfVisible":0,"shareDate":1569171588000,"shareUser":"","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 好用到你离不开的插件有哪些?","type":0,"userId":2,"visible":0,"zan":18},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

    这是一个很常见的需求,那么到底有多少种方案,每种各有什么特点?<\/p>","envelopePic":"","fresh":false,"id":9233,"link":"https://www.wanandroid.com/wenda/show/9233","niceDate":"2019-09-23","niceShareDate":"2019-09-17","origin":"","prefix":"","projectLink":"","publishTime":1569171675000,"selfVisible":0,"shareDate":1568731287000,"shareUser":"","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 屏蔽连续点击的方案有哪些?","type":0,"userId":2,"visible":1,"zan":20},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

    wanandroid有很多开源app,最近发现通过app查看文章时,经常自动打开淘宝、京东等第三方 app。<\/p>\r\n

      \r\n
    1. h5页面怎么做到的可以让我们\u201c自动打开淘宝\u201d? <\/li>\r\n
    2. 如果认为\u201c自动打开淘宝\u201d是一个非常严重的体验问题,那么从技术角度思考该如何解决?<\/li>\r\n
    3. 除了h5 页本身写了跳转代码,还有哪些情况下会导致\u201c自动打开淘宝\u201d这样的现象产生?<\/li>\r\n<\/ol>","envelopePic":"","fresh":false,"id":9177,"link":"https://www.wanandroid.com/wenda/show/9177","niceDate":"2019-09-17","niceShareDate":"2019-09-15","origin":"","prefix":"","projectLink":"","publishTime":1568731303000,"selfVisible":0,"shareDate":1568561270000,"shareUser":"","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 webview 加载的页面好像被劫持了?","type":0,"userId":2,"visible":1,"zan":6},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

      描述下Gradle 构建过程中的核心任务,以及我们能做哪些事干涉整个过程,例如:<\/p>\r\n

      第一类: 通过配置影响 task,例如:混淆,BuildConfig 等;
      第二类:我们可以add一些 listener,插入一些 task 等;<\/p>\r\n

      对于第一类,回答我们可以配置什么,有何作用;<\/p>\r\n

      对于第二类,回答我们可以怎么干,有何使用场景;<\/p>","envelopePic":"","fresh":false,"id":9113,"link":"https://www.wanandroid.com/wenda/show/9113","niceDate":"2019-09-15","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1568561458000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 Gradle 构建过程中,我们可以做哪些事?","type":0,"userId":2,"visible":1,"zan":7},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

      干涉指的是:<\/p>\r\n

        \r\n
      1. 有些可以通过配置,改变task执行输出产物,例如BuildConfig,混淆,签名等;<\/li>\r\n
      2. 有些可以在构建流程中插入一些自己的Task,做一些事情;<\/li>\r\n<\/ol>\r\n

        可以分别列举一些~<\/p>","envelopePic":"","fresh":false,"id":9112,"link":"https://wanandroid.com/wenda/show/9112","niceDate":"2019-09-15","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1568561355000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 gradle项目构建,需要经历哪些核心task,整个构建流程哪些我们可以干涉?","type":0,"userId":2,"visible":0,"zan":0},{"apkLink":"","audit":1,"author":"鸿洋","chapterId":360,"chapterName":"小编发布","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":9152,"link":"https://www.wanandroid.com/blog/show/2676","niceDate":"2019-09-15","niceShareDate":"2019-09-14","origin":"","prefix":"","projectLink":"","publishTime":1568561333000,"selfVisible":0,"shareDate":1568448789000,"shareUser":"鸿洋","superChapterId":298,"superChapterName":"原创文章","tags":[],"title":"我们支持大家自己投递文章啦!","type":1,"userId":2,"visible":1,"zan":0},{"apkLink":"","audit":1,"author":"","chapterId":494,"chapterName":"广场","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":9153,"link":"https://juejin.im/post/5d7a169af265da03ad147e64","niceDate":"2019-09-14","niceShareDate":"2019-09-14","origin":"","prefix":"","projectLink":"","publishTime":1568448869000,"selfVisible":0,"shareDate":1568448869000,"shareUser":"鸿洋","superChapterId":494,"superChapterName":"广场Tab","tags":[],"title":"Android 另一种更换字体的方式","type":0,"userId":2,"visible":0,"zan":0},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

        <\/p>\r\n

        打开一个 apk,忽然发现res 下没有我们熟知的 xxhdpi,xxxhdpi,替之的是:<\/p>\r\n

        drawable-xxhdpi-v4
        drawable-xxxhdpi-v4 <\/p>\r\n

        为什么会这样呢?<\/p>\r\n

        截图 Apk 点这里<\/a><\/p>","envelopePic":"","fresh":false,"id":9096,"link":"https://www.wanandroid.com/wenda/show/9096","niceDate":"2019-09-11","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1568132666000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 我的资源去哪了???","type":0,"userId":2,"visible":0,"zan":8},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

        上次我们问了每日一问 关于 R.java 的生成规则,你知道多少?\r\n<\/a>,但是一个有用的知识点还是没弄清楚。<\/p>\r\n

        对于不同的 module 下,同名但值不同的资源,最终:<\/p>\r\n

          \r\n
        1. 会被覆盖成其中一个资源吗?<\/li>\r\n
        2. 如果会,那么覆盖的规则是,随机吗?<\/li>\r\n<\/ol>","envelopePic":"","fresh":false,"id":9088,"link":"https://www.wanandroid.com/wenda/show/9088","niceDate":"2019-09-09","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1567959553000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 项目中同名资源,会不会覆盖,规则是怎么样的?","type":0,"userId":2,"visible":1,"zan":24},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

          作为一名 Java 开发者,这两个方法应该早已烂熟于心了。<\/p>\r\n

          今天我想问的是:<\/p>\r\n

            \r\n
          1. 两个对象,equals 相同,hashcode 一定相同吗?<\/li>\r\n
          2. hashcode 相同,equals 一定相同吗?<\/li>\r\n
          3. 二者一篇在什么时候配合?如何配合?在哪些源码中可以看到类似的配合?<\/li>\r\n<\/ol>","envelopePic":"","fresh":false,"id":9078,"link":"https://www.wanandroid.com/wenda/show/9078","niceDate":"2019-09-04","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1567611191000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 equals vs hashcode ?","type":0,"userId":2,"visible":0,"zan":17},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

            一般情况,每个 app 会依赖多个 module,一般 module 里面的资源,例如 res中有个 love。<\/p>\r\n

            对应的在 module中会有个:<\/p>\r\n

            com.module.R.res.love\r\n<\/code><\/pre>

            app 中也可以使用这个资源,但是包名为:<\/p>\r\n

            com.app.R.res.love\r\n<\/code><\/pre>

            两者虽然对应一个资源,但是id 值很大概率上是不同的。<\/p>\r\n

            那么最终打包是如何处理的呢?<\/p>","envelopePic":"","fresh":false,"id":9067,"link":"https://www.wanandroid.com/wenda/show/9067","niceDate":"2019-09-01","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1567351014000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 关于 R.java 的生成规则,你知道多少?","type":0,"userId":2,"visible":1,"zan":15},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

              \r\n
            1. 为什么效果比Serializable高?<\/li>\r\n
            2. 为了效率损失了什么?<\/li>\r\n
            3. 一个对象可以序列化的关键,你认为是?<\/li>\r\n<\/ol>","envelopePic":"","fresh":false,"id":9002,"link":"https://www.wanandroid.com/wenda/show/9002","niceDate":"2019-08-26","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1566748970000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 Parcelable 为什么效率高于 Serializable ?","type":0,"userId":2,"visible":1,"zan":31},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

              就找个细节考察下 Gradle 相关知识吧。<\/p>\r\n

              每个项目中,gradle 都会帮助我们生成一个 BuildConfig,那么:<\/p>\r\n

                \r\n
              1. 这个类有何用处?<\/li>\r\n
              2. 是在项目的编译期间,那个环节、如何生成的?<\/li>\r\n<\/ol>\r\n

                欢迎了解多少说多少,不了解学了过来总结一下。<\/p>","envelopePic":"","fresh":false,"id":8985,"link":"https://www.wanandroid.com/wenda/show/8985","niceDate":"2019-08-20","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1566314853000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 今天聊一下Gradle 相关,BuildConfig这个类是如何生成的?","type":0,"userId":2,"visible":1,"zan":15},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                很多时候大家在剖析butterknife源码的时候,更多的是讲解其中的apt等,在library中使用buttterknife的时候,会使用R2.id.xxx<\/p>\r\n

                class ExampleActivity extends Activity {\r\n  @BindView(R2.id.user) EditText username;\r\n  @BindView(R2.id.pass) EditText password;\r\n...\r\n}\r\n<\/code><\/pre>

                而非R.id.xxx.<\/p>\r\n

                所以今天提问是:<\/p>\r\n

                  \r\n
                1. 简述butterknife 原理;<\/li>\r\n
                2. apt 大致开发流程;<\/li>\r\n
                3. 以及上面描述为何使用 R2.id.xxx ,怎么做到的。<\/li>\r\n<\/ol>\r\n

                  任意回答其中一点都可以。<\/p>","envelopePic":"","fresh":false,"id":8926,"link":"https://www.wanandroid.com/wenda/show/8926","niceDate":"2019-08-18","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1566135745000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 butterknife 中的黑科技","type":0,"userId":2,"visible":0,"zan":5},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                  都说程序员应该对位运算了如指掌,那么Android中有哪些地方用到了呢?<\/p>\r\n

                  可否列举一些场景。<\/p>","envelopePic":"","fresh":false,"id":8913,"link":"https://wanandroid.com/wenda/show/8913","niceDate":"2019-08-13","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1565704592000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 Android 有哪些 位运算 的例子很值得借鉴?","type":0,"userId":2,"visible":1,"zan":4},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                    \r\n
                  1. 你有任何想问的问题<\/li>\r\n
                  2. 任何方向<\/li>\r\n
                  3. 关键词<\/li>\r\n<\/ol>\r\n

                    都可以直接留言提出。<\/p>","envelopePic":"","fresh":false,"id":8857,"link":"https://www.wanandroid.com/wenda/show/8857","niceDate":"2019-08-12","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1565575082000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 问答征集","type":0,"userId":2,"visible":0,"zan":10},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                    记得曾经有位同学做贴纸应用时,有RT 的需求。<\/p>\r\n

                    默认事件分发为逆序,遍历子 View 为 (childCount ~ 0 ],有哪些方式可以修改这一策略,比如修改遍历方式为[0,childCount)?<\/p>\r\n

                    3/3<\/p>","envelopePic":"","fresh":false,"id":8852,"link":"https://www.wanandroid.com/wenda/show/8852","niceDate":"2019-08-11","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1565529502000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 事件分发机制大家应该都熟记于心,默认事件分发是逆序的,有哪些方法可以修改分发顺序?","type":0,"userId":2,"visible":1,"zan":12}],"offset":0,"over":false,"pageCount":3,"size":20,"total":57} + * shareArticles : {"curPage":1,"datas":[{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"descdescdescdescdesc","envelopePic":"","fresh":false,"id":9470,"link":"https://wanandroid.com/wenda/show/9470","niceDate":"1天前","niceShareDate":"1天前","origin":"","prefix":"","projectLink":"","publishTime":1569990724000,"selfVisible":0,"shareDate":1569990436000,"shareUser":"","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"玩Android更新记录 [>2019-10-02]","type":1,"userId":2,"visible":1,"zan":0},{"apkLink":"","audit":1,"author":"","chapterId":494,"chapterName":"广场","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":9410,"link":"https://juejin.im/post/5d8b34cff265da5b88209e7b","niceDate":"2019-09-28","niceShareDate":"2019-09-28","origin":"","prefix":"","projectLink":"","publishTime":1569662568000,"selfVisible":0,"shareDate":1569662568000,"shareUser":"鸿洋","superChapterId":494,"superChapterName":"广场Tab","tags":[],"title":"滴滴开源库Booster:模块功能作用分析说明","type":0,"userId":2,"visible":0,"zan":0},{"apkLink":"","audit":1,"author":"","chapterId":494,"chapterName":"广场","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":9409,"link":"https://www.jianshu.com/p/79b30238b994","niceDate":"2019-09-28","niceShareDate":"2019-09-28","origin":"","prefix":"","projectLink":"","publishTime":1569662550000,"selfVisible":0,"shareDate":1569662550000,"shareUser":"鸿洋","superChapterId":494,"superChapterName":"广场Tab","tags":[],"title":"Android常见安全风险及规避措施","type":0,"userId":2,"visible":0,"zan":0},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                    就当是问卷啦,带大家一起发现一些好用的插件,欢迎分享出来哈。<\/p>\r\n

                      \r\n
                    1. 好用的 chrome 插件;<\/li>\r\n
                    2. 好用的 AS 插件;<\/li>\r\n
                    3. ...其他<\/li>\r\n<\/ol>\r\n

                      如果有链接,最好能附上链接哈~<\/p>","envelopePic":"","fresh":false,"id":9286,"link":"https://wanandroid.com/wenda/show/9286","niceDate":"2019-09-25","niceShareDate":"2019-09-23","origin":"","prefix":"","projectLink":"","publishTime":1569419925000,"selfVisible":0,"shareDate":1569171588000,"shareUser":"","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 好用到你离不开的插件有哪些?","type":0,"userId":2,"visible":0,"zan":18},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                      这是一个很常见的需求,那么到底有多少种方案,每种各有什么特点?<\/p>","envelopePic":"","fresh":false,"id":9233,"link":"https://wanandroid.com/wenda/show/9233","niceDate":"2019-09-23","niceShareDate":"2019-09-17","origin":"","prefix":"","projectLink":"","publishTime":1569171675000,"selfVisible":0,"shareDate":1568731287000,"shareUser":"","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 屏蔽连续点击的方案有哪些?","type":0,"userId":2,"visible":1,"zan":20},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                      wanandroid有很多开源app,最近发现通过app查看文章时,经常自动打开淘宝、京东等第三方 app。<\/p>\r\n

                        \r\n
                      1. h5页面怎么做到的可以让我们\u201c自动打开淘宝\u201d? <\/li>\r\n
                      2. 如果认为\u201c自动打开淘宝\u201d是一个非常严重的体验问题,那么从技术角度思考该如何解决?<\/li>\r\n
                      3. 除了h5 页本身写了跳转代码,还有哪些情况下会导致\u201c自动打开淘宝\u201d这样的现象产生?<\/li>\r\n<\/ol>","envelopePic":"","fresh":false,"id":9177,"link":"https://wanandroid.com/wenda/show/9177","niceDate":"2019-09-17","niceShareDate":"2019-09-15","origin":"","prefix":"","projectLink":"","publishTime":1568731303000,"selfVisible":0,"shareDate":1568561270000,"shareUser":"","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 webview 加载的页面好像被劫持了?","type":0,"userId":2,"visible":1,"zan":6},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                        描述下Gradle 构建过程中的核心任务,以及我们能做哪些事干涉整个过程,例如:<\/p>\r\n

                        第一类: 通过配置影响 task,例如:混淆,BuildConfig 等;
                        第二类:我们可以add一些 listener,插入一些 task 等;<\/p>\r\n

                        对于第一类,回答我们可以配置什么,有何作用;<\/p>\r\n

                        对于第二类,回答我们可以怎么干,有何使用场景;<\/p>","envelopePic":"","fresh":false,"id":9113,"link":"https://wanandroid.com/wenda/show/9113","niceDate":"2019-09-15","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1568561458000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 Gradle 构建过程中,我们可以做哪些事?","type":0,"userId":2,"visible":1,"zan":7},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                        干涉指的是:<\/p>\r\n

                          \r\n
                        1. 有些可以通过配置,改变task执行输出产物,例如BuildConfig,混淆,签名等;<\/li>\r\n
                        2. 有些可以在构建流程中插入一些自己的Task,做一些事情;<\/li>\r\n<\/ol>\r\n

                          可以分别列举一些~<\/p>","envelopePic":"","fresh":false,"id":9112,"link":"https://wanandroid.com/wenda/show/9112","niceDate":"2019-09-15","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1568561355000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 gradle项目构建,需要经历哪些核心task,整个构建流程哪些我们可以干涉?","type":0,"userId":2,"visible":0,"zan":0},{"apkLink":"","audit":1,"author":"鸿洋","chapterId":360,"chapterName":"小编发布","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":9152,"link":"https://wanandroid.com/blog/show/2676","niceDate":"2019-09-15","niceShareDate":"2019-09-14","origin":"","prefix":"","projectLink":"","publishTime":1568561333000,"selfVisible":0,"shareDate":1568448789000,"shareUser":"鸿洋","superChapterId":298,"superChapterName":"原创文章","tags":[],"title":"我们支持大家自己投递文章啦!","type":1,"userId":2,"visible":1,"zan":0},{"apkLink":"","audit":1,"author":"","chapterId":494,"chapterName":"广场","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":9153,"link":"https://juejin.im/post/5d7a169af265da03ad147e64","niceDate":"2019-09-14","niceShareDate":"2019-09-14","origin":"","prefix":"","projectLink":"","publishTime":1568448869000,"selfVisible":0,"shareDate":1568448869000,"shareUser":"鸿洋","superChapterId":494,"superChapterName":"广场Tab","tags":[],"title":"Android 另一种更换字体的方式","type":0,"userId":2,"visible":0,"zan":0},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                          <\/p>\r\n

                          打开一个 apk,忽然发现res 下没有我们熟知的 xxhdpi,xxxhdpi,替之的是:<\/p>\r\n

                          drawable-xxhdpi-v4
                          drawable-xxxhdpi-v4 <\/p>\r\n

                          为什么会这样呢?<\/p>\r\n

                          截图 Apk 点这里<\/a><\/p>","envelopePic":"","fresh":false,"id":9096,"link":"https://wanandroid.com/wenda/show/9096","niceDate":"2019-09-11","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1568132666000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 我的资源去哪了???","type":0,"userId":2,"visible":0,"zan":8},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                          上次我们问了每日一问 关于 R.java 的生成规则,你知道多少?\r\n<\/a>,但是一个有用的知识点还是没弄清楚。<\/p>\r\n

                          对于不同的 module 下,同名但值不同的资源,最终:<\/p>\r\n

                            \r\n
                          1. 会被覆盖成其中一个资源吗?<\/li>\r\n
                          2. 如果会,那么覆盖的规则是,随机吗?<\/li>\r\n<\/ol>","envelopePic":"","fresh":false,"id":9088,"link":"https://wanandroid.com/wenda/show/9088","niceDate":"2019-09-09","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1567959553000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 项目中同名资源,会不会覆盖,规则是怎么样的?","type":0,"userId":2,"visible":1,"zan":24},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                            作为一名 Java 开发者,这两个方法应该早已烂熟于心了。<\/p>\r\n

                            今天我想问的是:<\/p>\r\n

                              \r\n
                            1. 两个对象,equals 相同,hashcode 一定相同吗?<\/li>\r\n
                            2. hashcode 相同,equals 一定相同吗?<\/li>\r\n
                            3. 二者一篇在什么时候配合?如何配合?在哪些源码中可以看到类似的配合?<\/li>\r\n<\/ol>","envelopePic":"","fresh":false,"id":9078,"link":"https://wanandroid.com/wenda/show/9078","niceDate":"2019-09-04","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1567611191000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 equals vs hashcode ?","type":0,"userId":2,"visible":0,"zan":17},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                              一般情况,每个 app 会依赖多个 module,一般 module 里面的资源,例如 res中有个 love。<\/p>\r\n

                              对应的在 module中会有个:<\/p>\r\n

                              com.module.R.res.love\r\n<\/code><\/pre>

                              app 中也可以使用这个资源,但是包名为:<\/p>\r\n

                              com.app.R.res.love\r\n<\/code><\/pre>

                              两者虽然对应一个资源,但是id 值很大概率上是不同的。<\/p>\r\n

                              那么最终打包是如何处理的呢?<\/p>","envelopePic":"","fresh":false,"id":9067,"link":"https://wanandroid.com/wenda/show/9067","niceDate":"2019-09-01","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1567351014000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 关于 R.java 的生成规则,你知道多少?","type":0,"userId":2,"visible":1,"zan":15},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                                \r\n
                              1. 为什么效果比Serializable高?<\/li>\r\n
                              2. 为了效率损失了什么?<\/li>\r\n
                              3. 一个对象可以序列化的关键,你认为是?<\/li>\r\n<\/ol>","envelopePic":"","fresh":false,"id":9002,"link":"https://wanandroid.com/wenda/show/9002","niceDate":"2019-08-26","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1566748970000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 Parcelable 为什么效率高于 Serializable ?","type":0,"userId":2,"visible":1,"zan":31},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                                就找个细节考察下 Gradle 相关知识吧。<\/p>\r\n

                                每个项目中,gradle 都会帮助我们生成一个 BuildConfig,那么:<\/p>\r\n

                                  \r\n
                                1. 这个类有何用处?<\/li>\r\n
                                2. 是在项目的编译期间,那个环节、如何生成的?<\/li>\r\n<\/ol>\r\n

                                  欢迎了解多少说多少,不了解学了过来总结一下。<\/p>","envelopePic":"","fresh":false,"id":8985,"link":"https://wanandroid.com/wenda/show/8985","niceDate":"2019-08-20","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1566314853000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 今天聊一下Gradle 相关,BuildConfig这个类是如何生成的?","type":0,"userId":2,"visible":1,"zan":15},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                                  很多时候大家在剖析butterknife源码的时候,更多的是讲解其中的apt等,在library中使用buttterknife的时候,会使用R2.id.xxx<\/p>\r\n

                                  class ExampleActivity extends Activity {\r\n  @BindView(R2.id.user) EditText username;\r\n  @BindView(R2.id.pass) EditText password;\r\n...\r\n}\r\n<\/code><\/pre>

                                  而非R.id.xxx.<\/p>\r\n

                                  所以今天提问是:<\/p>\r\n

                                    \r\n
                                  1. 简述butterknife 原理;<\/li>\r\n
                                  2. apt 大致开发流程;<\/li>\r\n
                                  3. 以及上面描述为何使用 R2.id.xxx ,怎么做到的。<\/li>\r\n<\/ol>\r\n

                                    任意回答其中一点都可以。<\/p>","envelopePic":"","fresh":false,"id":8926,"link":"https://wanandroid.com/wenda/show/8926","niceDate":"2019-08-18","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1566135745000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 butterknife 中的黑科技","type":0,"userId":2,"visible":0,"zan":5},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                                    都说程序员应该对位运算了如指掌,那么Android中有哪些地方用到了呢?<\/p>\r\n

                                    可否列举一些场景。<\/p>","envelopePic":"","fresh":false,"id":8913,"link":"https://wanandroid.com/wenda/show/8913","niceDate":"2019-08-13","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1565704592000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 Android 有哪些 位运算 的例子很值得借鉴?","type":0,"userId":2,"visible":1,"zan":4},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                                      \r\n
                                    1. 你有任何想问的问题<\/li>\r\n
                                    2. 任何方向<\/li>\r\n
                                    3. 关键词<\/li>\r\n<\/ol>\r\n

                                      都可以直接留言提出。<\/p>","envelopePic":"","fresh":false,"id":8857,"link":"https://wanandroid.com/wenda/show/8857","niceDate":"2019-08-12","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1565575082000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 问答征集","type":0,"userId":2,"visible":0,"zan":10},{"apkLink":"","audit":1,"author":"xiaoyang","chapterId":440,"chapterName":"官方","collect":false,"courseId":13,"desc":"

                                      记得曾经有位同学做贴纸应用时,有RT 的需求。<\/p>\r\n

                                      默认事件分发为逆序,遍历子 View 为 (childCount ~ 0 ],有哪些方式可以修改这一策略,比如修改遍历方式为[0,childCount)?<\/p>\r\n

                                      3/3<\/p>","envelopePic":"","fresh":false,"id":8852,"link":"https://wanandroid.com/wenda/show/8852","niceDate":"2019-08-11","niceShareDate":"未知时间","origin":"","prefix":"","projectLink":"","publishTime":1565529502000,"selfVisible":0,"shareDate":null,"shareUser":"鸿洋","superChapterId":440,"superChapterName":"问答","tags":[{"name":"问答","url":"/https/github.com/article/list/0?cid=440"}],"title":"每日一问 事件分发机制大家应该都熟记于心,默认事件分发是逆序的,有哪些方法可以修改分发顺序?","type":0,"userId":2,"visible":1,"zan":12}],"offset":0,"over":false,"pageCount":3,"size":20,"total":57} */ private CoinInfoBean coinInfo; diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/model/WebArticleUrlRegexBean.java b/app/src/main/java/per/goweii/wanandroid/module/main/model/WebArticleUrlRegexBean.java new file mode 100644 index 00000000..1c2716e2 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/model/WebArticleUrlRegexBean.java @@ -0,0 +1,38 @@ +package per.goweii.wanandroid.module.main.model; + +import per.goweii.rxhttp.request.base.BaseBean; + +/** + * @author CuiZhen + * @date 2019/5/19 + * GitHub: https://github.com/goweii + */ +public class WebArticleUrlRegexBean extends BaseBean { + private String name; + private String host; + private String regex; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public String getRegex() { + return regex; + } + + public void setRegex(String regex) { + this.regex = regex; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/presenter/ArticlePresenter.kt b/app/src/main/java/per/goweii/wanandroid/module/main/presenter/ArticlePresenter.kt new file mode 100644 index 00000000..ed777237 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/presenter/ArticlePresenter.kt @@ -0,0 +1,238 @@ +package per.goweii.wanandroid.module.main.presenter + +import android.text.TextUtils +import per.goweii.basic.core.base.BasePresenter +import per.goweii.rxhttp.request.base.BaseBean +import per.goweii.rxhttp.request.exception.ExceptionHandle +import per.goweii.wanandroid.db.executor.ReadLaterExecutor +import per.goweii.wanandroid.db.executor.ReadRecordExecutor +import per.goweii.wanandroid.event.CollectionEvent +import per.goweii.wanandroid.event.ReadRecordAddedEvent +import per.goweii.wanandroid.event.ReadRecordUpdateEvent +import per.goweii.wanandroid.http.RequestListener +import per.goweii.wanandroid.module.main.model.CollectionLinkBean +import per.goweii.wanandroid.module.main.model.MainRequest +import per.goweii.wanandroid.module.main.view.ArticleView + +/** + * @author CuiZhen + * @date 2020/2/20 + */ +class ArticlePresenter : BasePresenter() { + var articleUrl: String = "" + var articleTitle: String = "" + var articleId: Int = -1 + var collectId: Int = -1 + var readLater: Boolean = false + var collected: Boolean = false + var userName: String = "" + var userId: Int = -1 + + private var mReadLaterExecutor: ReadLaterExecutor? = null + private var mReadRecordExecutor: ReadRecordExecutor? = null + + override fun attach(baseView: ArticleView) { + super.attach(baseView) + mReadLaterExecutor = ReadLaterExecutor() + mReadRecordExecutor = ReadRecordExecutor() + } + + override fun detach() { + mReadLaterExecutor?.destroy() + mReadRecordExecutor?.destroy() + super.detach() + } + + fun collect() { + if (articleId > 0) { + collectArticle() + } else { + collectLink() + } + } + + private fun collectArticle() { + addToRxLife(MainRequest.collectArticle(articleId, object : RequestListener { + override fun onStart() {} + + override fun onSuccess(code: Int, data: BaseBean?) { + collected = true + if (isAttach) { + baseView.collectSuccess() + } + CollectionEvent.postCollectWithArticleId(articleId) + } + + override fun onFailed(code: Int, msg: String) { + if (isAttach) { + baseView.collectFailed(msg) + } + } + + override fun onError(handle: ExceptionHandle) {} + override fun onFinish() {} + })) + } + + private fun collectLink() { + addToRxLife(MainRequest.collectLink(articleTitle, articleUrl, object : RequestListener { + override fun onStart() {} + + override fun onSuccess(code: Int, data: CollectionLinkBean?) { + collected = true + collectId = data?.id ?: -1 + if (isAttach) { + baseView.collectSuccess() + } + if (data?.id != null) { + CollectionEvent.postCollectWithCollectId(data.id) + } + } + + override fun onFailed(code: Int, msg: String) { + if (isAttach) { + baseView.collectFailed(msg) + } + } + + override fun onError(handle: ExceptionHandle) {} + override fun onFinish() {} + })) + } + + fun uncollect() { + if (articleId > 0) { + uncollectArticle() + } else { + uncollectLink() + } + } + + fun uncollectArticle() { + addToRxLife(MainRequest.uncollectArticle(articleId, object : RequestListener { + override fun onStart() {} + + override fun onSuccess(code: Int, data: BaseBean?) { + collected = false + collectId = -1 + if (isAttach) { + baseView.uncollectSuccess() + } + CollectionEvent.postUnCollectWithArticleId(articleId) + } + + override fun onFailed(code: Int, msg: String) { + if (isAttach) { + baseView.uncollectFailed(msg) + } + } + + override fun onError(handle: ExceptionHandle) {} + override fun onFinish() {} + })) + } + + fun uncollectLink() { + addToRxLife(MainRequest.uncollectLink(articleId, object : RequestListener { + override fun onStart() {} + + override fun onSuccess(code: Int, data: BaseBean?) { + collected = false + if (isAttach) { + baseView.uncollectSuccess() + } + CollectionEvent.postUnCollectWithArticleId(articleId) + } + + override fun onFailed(code: Int, msg: String) { + if (isAttach) { + baseView.uncollectFailed(msg) + } + } + + override fun onError(handle: ExceptionHandle) {} + override fun onFinish() {} + })) + } + + fun addReadRecord(link: String, title: String, percent: Float) { + mReadRecordExecutor ?: return + if (TextUtils.isEmpty(link)) return + if (TextUtils.isEmpty(title)) return + if (TextUtils.equals(link, title)) return + mReadRecordExecutor?.add(link, title, percent, { + if (it.time == it.lastTime) { + ReadRecordAddedEvent(it).post() + } else { + val readRecordUpdateEvent = ReadRecordUpdateEvent(it.link) + readRecordUpdateEvent.time = it.lastTime + readRecordUpdateEvent.post() + } + }, { }) + } + + fun updateReadRecordTitle(link: String, title: String?) { + mReadRecordExecutor ?: return + if (TextUtils.isEmpty(link)) return + if (TextUtils.isEmpty(title)) return + if (TextUtils.equals(link, title)) return + mReadRecordExecutor?.updateTitle(link, title!!, { + val readRecordUpdateEvent = ReadRecordUpdateEvent(it.link) + readRecordUpdateEvent.title = it.title + readRecordUpdateEvent.post() + }, { }) + } + + fun updateReadRecordPercent(link: String, percent: Float) { + mReadRecordExecutor ?: return + if (TextUtils.isEmpty(link)) return + val lastTime = System.currentTimeMillis() + mReadRecordExecutor?.updatePercent(link, percent, lastTime, { + val readRecordUpdateEvent = ReadRecordUpdateEvent(it.link) + readRecordUpdateEvent.time = it.lastTime + readRecordUpdateEvent.percent = it.percent + readRecordUpdateEvent.post() + }, {}) + } + + fun addReadLater() { + mReadLaterExecutor?.add(articleUrl, articleTitle, { + readLater = true + if (isAttach) { + baseView.addReadLaterSuccess() + } + }, { + if (isAttach) { + baseView.addReadLaterFailed() + } + }) + } + + fun removeReadLater() { + mReadLaterExecutor?.remove(articleUrl, { + readLater = false + if (isAttach) { + baseView.removeReadLaterSuccess() + } + }, { + if (isAttach) { + baseView.removeReadLaterFailed() + } + }) + } + + fun isReadLater(callback: (Boolean) -> Unit = {}) { + mReadLaterExecutor?.findByLink(articleUrl, { + if (it.isNotEmpty()) { + readLater = true + callback.invoke(true) + } else { + readLater = false + callback.invoke(false) + } + }, { + readLater = false + callback.invoke(false) + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/presenter/MainPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/main/presenter/MainPresenter.java index fad10933..536926d9 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/presenter/MainPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/presenter/MainPresenter.java @@ -1,25 +1,44 @@ package per.goweii.wanandroid.module.main.presenter; +import java.util.List; + import per.goweii.basic.core.base.BasePresenter; +import per.goweii.basic.utils.listener.SimpleCallback; +import per.goweii.basic.utils.listener.SimpleListener; +import per.goweii.rxhttp.request.exception.ExceptionHandle; +import per.goweii.wanandroid.db.executor.ReadLaterExecutor; +import per.goweii.wanandroid.db.model.ReadLaterModel; import per.goweii.wanandroid.http.RequestCallback; +import per.goweii.wanandroid.http.RequestListener; +import per.goweii.wanandroid.module.main.model.AdvertBean; +import per.goweii.wanandroid.module.main.model.ConfigBean; import per.goweii.wanandroid.module.main.model.MainRequest; import per.goweii.wanandroid.module.main.model.UpdateBean; import per.goweii.wanandroid.module.main.view.MainView; -import per.goweii.wanandroid.utils.UpdateUtils; +import per.goweii.wanandroid.utils.ConfigUtils; /** * @author CuiZhen * @date 2019/5/19 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class MainPresenter extends BasePresenter { - public void update(){ - if (UpdateUtils.newInstance().isTodayChecked()) { - return; - } + private ReadLaterExecutor mReadLaterExecutor = null; + + @Override + public void attach(MainView baseView) { + super.attach(baseView); + mReadLaterExecutor = new ReadLaterExecutor(); + } + + @Override + public void detach() { + if (mReadLaterExecutor != null) mReadLaterExecutor.destroy(); + super.detach(); + } + + public void update() { MainRequest.update(getRxLife(), new RequestCallback() { @Override public void onSuccess(int code, UpdateBean data) { @@ -30,7 +49,107 @@ public void onSuccess(int code, UpdateBean data) { @Override public void onFailed(int code, String msg) { + if (isAttach()) { + getBaseView().updateFailed(code, msg); + } + } + }); + } + + public void betaUpdate() { + MainRequest.betaUpdate(getRxLife(), new RequestCallback() { + @Override + public void onSuccess(int code, UpdateBean data) { + if (isAttach()) { + getBaseView().betaUpdateSuccess(code, data); + } + } + + @Override + public void onFailed(int code, String msg) { + if (isAttach()) { + getBaseView().betaUpdateFailed(code, msg); + } + } + }); + } + + public void getConfig() { + MainRequest.getConfig(getRxLife(), new RequestListener() { + @Override + public void onStart() { + } + + @Override + public void onSuccess(int code, ConfigBean data) { + if (isAttach()) { + getBaseView().getConfigSuccess(data); + } + int oldTheme = ConfigUtils.getInstance().getTheme(); + ConfigUtils.getInstance().setConfig(data); + int newTheme = ConfigUtils.getInstance().getTheme(); + if (oldTheme != newTheme) { + if (isAttach()) { + getBaseView().newThemeFounded(); + } + } + } + @Override + public void onFailed(int code, String msg) { + if (isAttach()) { + getBaseView().getConfigFailed(code, msg); + } + } + + @Override + public void onError(ExceptionHandle handle) { + } + + @Override + public void onFinish() { + } + }); + } + + public void getAdvert() { + MainRequest.getAdvert(getRxLife(), new RequestCallback() { + @Override + public void onSuccess(int code, AdvertBean data) { + if (isAttach()) { + getBaseView().getAdvertSuccess(code, data); + } + } + + @Override + public void onFailed(int code, String msg) { + if (isAttach()) { + getBaseView().getAdvertFailed(code, msg); + } + } + }); + } + + public void getReadLaterArticle() { + mReadLaterExecutor.findLately(1, new SimpleCallback>() { + @Override + public void onResult(List data) { + if (!data.isEmpty()) { + if (isAttach()) { + getBaseView().getReadLaterArticleSuccess(data.get(0)); + } + } else { + if (isAttach()) { + getBaseView().getReadLaterArticleFailed(); + } + } + } + }, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + getBaseView().getReadLaterArticleFailed(); + } } }); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/presenter/ScanPresenter.kt b/app/src/main/java/per/goweii/wanandroid/module/main/presenter/ScanPresenter.kt new file mode 100644 index 00000000..64c40f12 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/presenter/ScanPresenter.kt @@ -0,0 +1,50 @@ +package per.goweii.wanandroid.module.main.presenter + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.BitmapFactory +import android.net.Uri +import per.goweii.basic.core.base.BasePresenter +import per.goweii.basic.utils.bitmap.BitmapUtils +import per.goweii.wanandroid.module.main.view.ScanView +import kotlin.math.min + +/** + * @author CuiZhen + * @date 2020/2/26 + */ +class ScanPresenter : BasePresenter() { + + fun getBitmapFromPath(path: String): Bitmap? { + val uri = BitmapUtils.getImageContentUri(context, path) + uri ?: return null + val bm = getBitmapFromUri(context, uri) + bm ?: return null + return bm + } + + private fun getBitmapFromUri(context: Context, uri: Uri): Bitmap? { + try { + val parcelFileDescriptor = context.contentResolver.openFileDescriptor(uri, "r") + parcelFileDescriptor ?: return null + val fileDescriptor = parcelFileDescriptor.fileDescriptor + val newOpts = BitmapFactory.Options() + newOpts.inJustDecodeBounds = true + BitmapFactory.decodeFileDescriptor(fileDescriptor, null, newOpts)// 此时返回bm为空 + val w = newOpts.outWidth.toFloat() + val h = newOpts.outHeight.toFloat() + val s = 720f + var be = (min(w, h) / s).toInt() + if (be < 1) be = 1 + newOpts.inSampleSize = be + newOpts.inJustDecodeBounds = false + val image = BitmapFactory.decodeFileDescriptor(fileDescriptor, null, newOpts) + parcelFileDescriptor.close() + return image + } catch (e: Exception) { + e.printStackTrace() + } + return null + } + +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/presenter/ShareArticlePresenter.java b/app/src/main/java/per/goweii/wanandroid/module/main/presenter/ShareArticlePresenter.java index 72dbfb66..ff10e9dc 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/presenter/ShareArticlePresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/presenter/ShareArticlePresenter.java @@ -11,8 +11,6 @@ /** * @author CuiZhen * @date 2019/10/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ShareArticlePresenter extends BasePresenter { diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/presenter/UserArticlePresenter.java b/app/src/main/java/per/goweii/wanandroid/module/main/presenter/UserArticlePresenter.java index 0f821881..554670bd 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/presenter/UserArticlePresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/presenter/UserArticlePresenter.java @@ -14,8 +14,6 @@ /** * @author CuiZhen * @date 2019/10/3 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class UserArticlePresenter extends BasePresenter { @@ -78,7 +76,7 @@ public void onFinish() { } public void collect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.collect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.collectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } @@ -110,7 +108,7 @@ public void onFinish() { } public void uncollect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.uncollect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.uncollectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/presenter/WebPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/main/presenter/WebPresenter.java index 9fcca3ca..992871b9 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/presenter/WebPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/presenter/WebPresenter.java @@ -1,57 +1,114 @@ package per.goweii.wanandroid.module.main.presenter; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.RelativeLayout; -import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import per.goweii.basic.core.base.BasePresenter; -import per.goweii.basic.core.glide.GlideHelper; import per.goweii.basic.ui.toast.ToastMaker; -import per.goweii.basic.utils.bitmap.BitmapUtils; import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.rxhttp.request.base.BaseBean; import per.goweii.rxhttp.request.exception.ExceptionHandle; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.db.executor.ReadLaterExecutor; +import per.goweii.wanandroid.db.executor.ReadRecordExecutor; +import per.goweii.wanandroid.db.model.ReadLaterModel; +import per.goweii.wanandroid.db.model.ReadRecordModel; import per.goweii.wanandroid.event.CollectionEvent; +import per.goweii.wanandroid.event.ReadRecordAddedEvent; +import per.goweii.wanandroid.event.ReadRecordUpdateEvent; import per.goweii.wanandroid.http.RequestListener; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.CollectArticleEntity; import per.goweii.wanandroid.module.main.model.CollectionLinkBean; import per.goweii.wanandroid.module.main.model.MainRequest; import per.goweii.wanandroid.module.main.view.WebView; -import per.goweii.wanandroid.utils.UserUtils; /** * @author CuiZhen * @date 2019/5/20 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WebPresenter extends BasePresenter { + private List mCollectedList = new ArrayList<>(1); + private List mReadLaterList = new ArrayList<>(1); + + private ReadLaterExecutor mReadLaterExecutor = null; + private ReadRecordExecutor mReadRecordExecutor = null; + + @Override + public void attach(WebView baseView) { + super.attach(baseView); + mReadLaterExecutor = new ReadLaterExecutor(); + mReadRecordExecutor = new ReadRecordExecutor(); + } + + @Override + public void detach() { + if (mReadLaterExecutor != null) mReadLaterExecutor.destroy(); + if (mReadRecordExecutor != null) mReadRecordExecutor.destroy(); + super.detach(); + } + + public void addReadLater(ReadLaterModel model) { + if (findReadLater(model.getLink()) != null) return; + mReadLaterList.add(model); + } + + public void removeReadLater(String url) { + Iterator iterator = mReadLaterList.iterator(); + while (iterator.hasNext()) { + if (TextUtils.equals(iterator.next().getLink(), url)) { + iterator.remove(); + } + } + } + + public void addCollected(CollectArticleEntity entity) { + if (findCollected(entity.getUrl()) != null) return; + mCollectedList.add(entity); + } + + public ReadLaterModel findReadLater(String url) { + ReadLaterModel readLaterModel = null; + for (ReadLaterModel model : mReadLaterList) { + if (TextUtils.equals(model.getLink(), url)) { + readLaterModel = model; + break; + } + } + return readLaterModel; + } + + public CollectArticleEntity findCollected(String url) { + CollectArticleEntity collectArticleEntity = null; + for (CollectArticleEntity entity : mCollectedList) { + if (TextUtils.equals(entity.getUrl(), url)) { + collectArticleEntity = entity; + break; + } + } + return collectArticleEntity; + } public void collect(CollectArticleEntity entity) { if (entity.isCollect()) { + addCollected(entity); if (isAttach()) { getBaseView().collectSuccess(entity); } return; } if (entity.getArticleId() > 0) { - collect(entity.getArticleId(), entity); + collectArticle(entity.getArticleId(), entity); } else { if (TextUtils.isEmpty(entity.getAuthor())) { - collect(entity.getTitle(), entity.getUrl(), entity); + collectLink(entity.getTitle(), entity.getUrl(), entity); } else { - collect(entity.getTitle(), entity.getAuthor(), entity.getUrl(), entity); + collectArticle(entity.getTitle(), entity.getAuthor(), entity.getUrl(), entity); } } } @@ -64,14 +121,14 @@ public void uncollect(CollectArticleEntity entity) { return; } if (entity.getArticleId() > 0) { - uncollect(entity.getArticleId(), entity); + uncollectArticle(entity.getArticleId(), entity); } else { uncollectLink(entity.getCollectId(), entity); } } - private void collect(int id, final CollectArticleEntity entity) { - addToRxLife(MainRequest.collect(id, new RequestListener() { + private void collectArticle(int id, final CollectArticleEntity entity) { + addToRxLife(MainRequest.collectArticle(id, new RequestListener() { @Override public void onStart() { showLoadingBar(); @@ -81,6 +138,7 @@ public void onStart() { public void onSuccess(int code, BaseBean data) { CollectionEvent.postCollectWithArticleId(id); entity.setCollect(true); + addCollected(entity); if (isAttach()) { getBaseView().collectSuccess(entity); } @@ -104,8 +162,8 @@ public void onFinish() { })); } - private void uncollect(final int id, final CollectArticleEntity entity) { - addToRxLife(MainRequest.uncollect(id, new RequestListener() { + private void uncollectArticle(final int id, final CollectArticleEntity entity) { + addToRxLife(MainRequest.uncollectArticle(id, new RequestListener() { @Override public void onStart() { } @@ -136,8 +194,8 @@ public void onFinish() { })); } - private void collect(String title, String author, String link, CollectArticleEntity entity) { - addToRxLife(MainRequest.collect(title, author, link, new RequestListener() { + private void collectArticle(String title, String author, String link, CollectArticleEntity entity) { + addToRxLife(MainRequest.collectArticle(title, author, link, new RequestListener() { @Override public void onStart() { showLoadingBar(); @@ -146,7 +204,9 @@ public void onStart() { @Override public void onSuccess(int code, ArticleBean data) { CollectionEvent.postCollectWithCollectId(data.getId()); + entity.setArticleId(data.getId()); entity.setCollect(true); + addCollected(entity); if (isAttach()) { getBaseView().collectSuccess(entity); } @@ -170,8 +230,8 @@ public void onFinish() { })); } - private void collect(String title, String link, CollectArticleEntity entity) { - addToRxLife(MainRequest.collect(title, link, new RequestListener() { + private void collectLink(String title, String link, CollectArticleEntity entity) { + addToRxLife(MainRequest.collectLink(title, link, new RequestListener() { @Override public void onStart() { showLoadingBar(); @@ -180,7 +240,9 @@ public void onStart() { @Override public void onSuccess(int code, CollectionLinkBean data) { CollectionEvent.postCollectWithCollectId(data.getId()); + entity.setCollectId(data.getId()); entity.setCollect(true); + addCollected(entity); if (isAttach()) { getBaseView().collectSuccess(entity); } @@ -236,60 +298,117 @@ public void onFinish() { })); } - public void saveGallery(Bitmap bitmap, String name) { - if (null != BitmapUtils.saveGallery(bitmap, name)) { - ToastMaker.showShort("以保存到相册"); - } else { - ToastMaker.showShort("保存失败"); - } - bitmap.recycle(); + public void addReadLater(String link, String title) { + if (mReadLaterExecutor == null) return; + mReadLaterExecutor.add(link, title, new SimpleCallback() { + @Override + public void onResult(ReadLaterModel data) { + addReadLater(data); + ToastMaker.showShort(getContext().getString(R.string.added_to_my_bookmarks)); + } + }, new SimpleListener() { + @Override + public void onResult() { + ToastMaker.showShort(getContext().getString(R.string.failed_to_add_my_bookmarks)); + } + }); } - public void createQrcodeImage(Bitmap qrcode, String title, SimpleCallback callback) { - SimpleCallback inflateCallback = new SimpleCallback() { - @Override - public void onResult(View view) { - int w = View.MeasureSpec.makeMeasureSpec(qrcode.getWidth(), View.MeasureSpec.EXACTLY); - int h = View.MeasureSpec.makeMeasureSpec(10000, View.MeasureSpec.AT_MOST); - view.measure(w, h); - view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight()); - Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.RGB_565); - Canvas canvas = new Canvas(bitmap); - view.draw(canvas); - qrcode.recycle(); - callback.onResult(bitmap); - } - }; - View view = LayoutInflater.from(getContext()).inflate(R.layout.layout_web_share_qrcode, null); - ImageView piv_qrcode = view.findViewById(R.id.piv_qrcode); - piv_qrcode.setImageBitmap(qrcode); - TextView tv_title = view.findViewById(R.id.tv_title); - tv_title.setText(title); - RelativeLayout rl_user_info = view.findViewById(R.id.rl_user_info); - if (UserUtils.getInstance().isLogin()) { - rl_user_info.setVisibility(View.VISIBLE); - TextView tv_user_name = view.findViewById(R.id.tv_user_name); - ImageView civ_user_icon = view.findViewById(R.id.civ_user_icon); - tv_user_name.setText(UserUtils.getInstance().getLoginBean().getUsername() + - "(" + UserUtils.getInstance().getLoginBean().getId() + ")"); - GlideHelper.with(getContext()) - .load(UserUtils.getInstance().getLoginBean().getIcon()) - .get(new SimpleCallback() { - @Override - public void onResult(Bitmap data) { - civ_user_icon.setImageBitmap(data); - inflateCallback.onResult(view); - } - }, new SimpleListener() { - @Override - public void onResult() { - civ_user_icon.setImageDrawable(new ColorDrawable(Color.parseColor("#22000000"))); - inflateCallback.onResult(view); - } - }); - } else { - rl_user_info.setVisibility(View.GONE); - inflateCallback.onResult(view); + public void deleteReadLater(String link) { + if (mReadLaterExecutor == null) return; + mReadLaterExecutor.remove(link, new SimpleListener() { + @Override + public void onResult() { + removeReadLater(link); + ToastMaker.showShort(getContext().getString(R.string.moved_out_of_my_bookmarks)); + } + }, new SimpleListener() { + @Override + public void onResult() { + ToastMaker.showShort(getContext().getString(R.string.failed_to_move_out_of_my_bookmarks)); + } + }); + } + + public void isAddedReadLater(String link) { + if (mReadLaterExecutor == null) return; + mReadLaterExecutor.findByLink(link, new SimpleCallback>() { + @Override + public void onResult(List data) { + if (!data.isEmpty()) { + ReadLaterModel readLaterModel = data.get(0); + addReadLater(readLaterModel); + if (isAttach()) { + getBaseView().isAddedReadLaterSuccess(readLaterModel); + } + } + } + }, new SimpleListener() { + @Override + public void onResult() { + } + }); + } + + public void addReadRecord(String link, String title, float percent) { + if (mReadRecordExecutor == null) return; + if (TextUtils.isEmpty(link)) return; + if (TextUtils.isEmpty(title)) { + title = link; } + mReadRecordExecutor.add(link, title, percent, new SimpleCallback() { + @Override + public void onResult(ReadRecordModel readRecordModel) { + if (readRecordModel.getTime() == readRecordModel.getLastTime()) { + new ReadRecordAddedEvent(readRecordModel).post(); + } else { + ReadRecordUpdateEvent readRecordUpdateEvent = new ReadRecordUpdateEvent(readRecordModel.getLink()); + readRecordUpdateEvent.setTime(readRecordModel.getLastTime()); + readRecordUpdateEvent.post(); + } + } + }, new SimpleListener() { + @Override + public void onResult() { + } + }); + } + + public void updateReadRecordTitle(String link, String title) { + if (mReadRecordExecutor == null) return; + if (TextUtils.isEmpty(link)) return; + if (TextUtils.isEmpty(title)) return; + if (TextUtils.equals(link, title)) return; + mReadRecordExecutor.updateTitle(link, title, new SimpleCallback() { + @Override + public void onResult(ReadRecordModel readRecordModel) { + ReadRecordUpdateEvent readRecordUpdateEvent = new ReadRecordUpdateEvent(readRecordModel.getLink()); + readRecordUpdateEvent.setTitle(readRecordModel.getTitle()); + readRecordUpdateEvent.post(); + } + }, new SimpleListener() { + @Override + public void onResult() { + } + }); + } + + public void updateReadRecordPercent(String link, float percent) { + if (mReadRecordExecutor == null) return; + if (TextUtils.isEmpty(link)) return; + long lastTime = System.currentTimeMillis(); + mReadRecordExecutor.updatePercent(link, percent, lastTime, new SimpleCallback() { + @Override + public void onResult(ReadRecordModel readRecordModel) { + ReadRecordUpdateEvent readRecordUpdateEvent = new ReadRecordUpdateEvent(readRecordModel.getLink()); + readRecordUpdateEvent.setTime(readRecordModel.getLastTime()); + readRecordUpdateEvent.setPercent(readRecordModel.getPercent()); + readRecordUpdateEvent.post(); + } + }, new SimpleListener() { + @Override + public void onResult() { + } + }); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/utils/BottomDrawerViewOutlineProvider.kt b/app/src/main/java/per/goweii/wanandroid/module/main/utils/BottomDrawerViewOutlineProvider.kt new file mode 100644 index 00000000..c989e679 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/utils/BottomDrawerViewOutlineProvider.kt @@ -0,0 +1,29 @@ +package per.goweii.wanandroid.module.main.utils + +import android.graphics.Outline +import android.view.View +import android.view.ViewOutlineProvider + +class BottomDrawerViewOutlineProvider( + private val radius: Float +) : ViewOutlineProvider() { + private var view: View? = null + private var faction: Float = 0F + + fun setToView(v: View) { + v.clipToOutline = true + v.outlineProvider = this + view = v + } + + fun updateFaction(f: Float) { + faction = f + view?.invalidateOutline() + } + + override fun getOutline(v: View, outline: Outline) { + view = v + val r = radius * faction + outline.setRoundRect(0, 0, v.width, v.height + (r + 0.5F).toInt(), r) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/utils/FloatIconTouchListener.kt b/app/src/main/java/per/goweii/wanandroid/module/main/utils/FloatIconTouchListener.kt new file mode 100644 index 00000000..b9cd6d99 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/utils/FloatIconTouchListener.kt @@ -0,0 +1,132 @@ +package per.goweii.wanandroid.module.main.utils + +import android.annotation.SuppressLint +import android.graphics.PointF +import android.graphics.Rect +import android.view.HapticFeedbackConstants +import android.view.MotionEvent +import android.view.View +import android.view.ViewConfiguration +import kotlin.math.abs + +/** + * @author CuiZhen + * @date 2020/4/19 + */ +@SuppressLint("ClickableViewAccessibility") +class FloatIconTouchListener( + private val icons: List, + private val onFloatTouchedListener: OnFloatTouchedListener +) : View.OnTouchListener, Runnable { + + data class Icon(val view: View, var touched: Boolean = false) + + private lateinit var icon: Icon + + private val longPressTimeout = ViewConfiguration.getLongPressTimeout() + private val touchSlop by lazy { ViewConfiguration.get(icon.view.context).scaledTouchSlop } + + private var touchTime = 0L + private var touchMoved = false + private val touchPoint = PointF() + private var currTouchIcon: Icon? = null + private var moveOutside = false + + private val location = intArrayOf(0, 0) + private val rect = Rect() + + override fun onTouch(v: View, e: MotionEvent): Boolean { + if (!this::icon.isInitialized) icon = Icon(v) + when (e.action) { + MotionEvent.ACTION_DOWN -> { + icon.touched = false + icons.forEach { it.touched = false } + touchPoint.x = e.rawX + touchPoint.y = e.rawY + touchTime = System.currentTimeMillis() + touchMoved = false + moveOutside = false + currTouchIcon = null + icon.view.postDelayed(this, longPressTimeout.toLong()) + } + MotionEvent.ACTION_MOVE -> { + if (abs(e.rawX - touchPoint.x) > touchSlop || abs(e.rawY - touchPoint.y) > touchSlop) { + touchMoved = true + icon.view.removeCallbacks(this) + } + rect.set(0, 0, icon.view.width, icon.view.height) + icon.touched = rect.contains(e.x.toInt(), e.y.toInt()) + icons.forEach { + it.view.getLocationOnScreen(location) + rect.set(location[0], location[1], location[0] + it.view.width, location[1] + it.view.height) + if (rect.contains(e.rawX.toInt(), e.rawY.toInt())) { + if (!it.touched) { + it.touched = true + } + } else { + it.touched = false + } + } + var touchedCount = 0 + if (icon.touched) touchedCount++ + icons.forEach { + if (it.touched) touchedCount++ + } + if (touchedCount != 1) { + if (currTouchIcon != null) { + currTouchIcon = null + onFloatTouchedListener.onTouched(null) + } + } else { + if (icon.touched) { + if (moveOutside && currTouchIcon != icon) { + currTouchIcon = icon + icon.view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY) + onFloatTouchedListener.onTouched(icon.view) + } + } else { + moveOutside = true + var touchedIcon: Icon? = null + icons.forEach { + if (it.touched) { + touchedIcon = it + return@forEach + } + } + if (currTouchIcon != touchedIcon) { + currTouchIcon = touchedIcon + touchedIcon!!.view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY) + onFloatTouchedListener.onTouched(touchedIcon!!.view) + } + } + } + } + MotionEvent.ACTION_UP -> { + icon.view.removeCallbacks(this) + if (!touchMoved) { + val upTime = System.currentTimeMillis() + if (upTime - touchTime < longPressTimeout) { + icon.view.performClick() + } + } else { + currTouchIcon?.let { + if (it == icon) { + it.view.performClick() + } else { + it.view.performClick() + } + } + } + } + } + return true + } + + override fun run() { + icon.view.performLongClick() + } + + interface OnFloatTouchedListener { + fun onTouched(v: View?) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/view/ArticleView.kt b/app/src/main/java/per/goweii/wanandroid/module/main/view/ArticleView.kt new file mode 100644 index 00000000..b384b854 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/view/ArticleView.kt @@ -0,0 +1,18 @@ +package per.goweii.wanandroid.module.main.view + +import per.goweii.basic.core.base.BaseView + +/** + * @author CuiZhen + * @date 2020/2/20 + */ +interface ArticleView : BaseView { + fun collectSuccess() + fun collectFailed(msg: String) + fun uncollectSuccess() + fun uncollectFailed(msg: String) + fun addReadLaterSuccess() + fun addReadLaterFailed() + fun removeReadLaterSuccess() + fun removeReadLaterFailed() +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/view/MainView.java b/app/src/main/java/per/goweii/wanandroid/module/main/view/MainView.java index e124f374..15e6c43c 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/view/MainView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/view/MainView.java @@ -1,15 +1,37 @@ package per.goweii.wanandroid.module.main.view; import per.goweii.basic.core.base.BaseView; +import per.goweii.wanandroid.db.model.ReadLaterModel; +import per.goweii.wanandroid.module.main.model.AdvertBean; +import per.goweii.wanandroid.module.main.model.ConfigBean; import per.goweii.wanandroid.module.main.model.UpdateBean; /** * @author CuiZhen * @date 2019/5/19 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface MainView extends BaseView { void updateSuccess(int code, UpdateBean data); + + void updateFailed(int code, String msg); + + void betaUpdateSuccess(int code, UpdateBean data); + + void betaUpdateFailed(int code, String msg); + + void getConfigSuccess(ConfigBean configBean); + + void getConfigFailed(int code, String msg); + + void newThemeFounded(); + + void getAdvertSuccess(int code, AdvertBean advertBean); + + void getAdvertFailed(int code, String msg); + + void getReadLaterArticleSuccess(ReadLaterModel readLaterModel); + + void getReadLaterArticleFailed(); + } diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/view/ScanView.kt b/app/src/main/java/per/goweii/wanandroid/module/main/view/ScanView.kt new file mode 100644 index 00000000..2069c8c1 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/main/view/ScanView.kt @@ -0,0 +1,10 @@ +package per.goweii.wanandroid.module.main.view + +import per.goweii.basic.core.base.BaseView + +/** + * @author CuiZhen + * @date 2020/2/26 + */ +interface ScanView : BaseView { +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/view/ShareArticleView.java b/app/src/main/java/per/goweii/wanandroid/module/main/view/ShareArticleView.java index 36fc8b85..1aba69f5 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/view/ShareArticleView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/view/ShareArticleView.java @@ -6,8 +6,6 @@ /** * @author CuiZhen * @date 2019/10/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface ShareArticleView extends BaseView { diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/view/UserArticleView.java b/app/src/main/java/per/goweii/wanandroid/module/main/view/UserArticleView.java index ba1f13dc..114523c2 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/view/UserArticleView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/view/UserArticleView.java @@ -6,8 +6,6 @@ /** * @author CuiZhen * @date 2019/10/3 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface UserArticleView extends BaseView { diff --git a/app/src/main/java/per/goweii/wanandroid/module/main/view/WebView.java b/app/src/main/java/per/goweii/wanandroid/module/main/view/WebView.java index fd0aad8d..dcd9fb72 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/main/view/WebView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/main/view/WebView.java @@ -1,20 +1,22 @@ package per.goweii.wanandroid.module.main.view; import per.goweii.basic.core.base.BaseView; +import per.goweii.wanandroid.db.model.ReadLaterModel; import per.goweii.wanandroid.module.main.model.CollectArticleEntity; /** * @author CuiZhen * @date 2019/5/20 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface WebView extends BaseView { void collectSuccess(CollectArticleEntity entity); + void collectFailed(String msg); void uncollectSuccess(CollectArticleEntity entity); void uncollectFailed(String msg); + + void isAddedReadLaterSuccess(ReadLaterModel data); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/AboutActivity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/AboutActivity.java index 9d0e2d55..b9e76e1d 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/AboutActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/AboutActivity.java @@ -1,28 +1,65 @@ package per.goweii.wanandroid.module.mine.activity; +import android.Manifest; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; +import android.graphics.Bitmap; +import android.view.LayoutInflater; import android.view.View; +import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.OnApplyWindowInsetsListener; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + import butterknife.BindView; import butterknife.OnClick; +import kotlin.Unit; +import kotlin.jvm.functions.Function1; +import per.goweii.actionbarex.common.ActionBarCommon; +import per.goweii.actionbarex.common.OnActionBarChildClickListener; +import per.goweii.anypermission.RequestListener; +import per.goweii.anypermission.RuntimeRequester; import per.goweii.basic.core.base.BaseActivity; -import per.goweii.basic.core.mvp.MvpPresenter; +import per.goweii.basic.core.permission.PermissionUtils; +import per.goweii.basic.ui.dialog.TipDialog; +import per.goweii.basic.ui.dialog.UpdateDialog; +import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.AppInfoUtils; +import per.goweii.basic.utils.ResUtils; +import per.goweii.codex.encoder.CodeEncoder; +import per.goweii.codex.processor.zxing.ZXingEncodeQRCodeProcessor; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.module.main.activity.WebActivity; +import per.goweii.wanandroid.databinding.ActivityAboutBinding; +import per.goweii.wanandroid.module.main.dialog.CardShareDialog; +import per.goweii.wanandroid.module.main.dialog.DownloadDialog; +import per.goweii.wanandroid.module.main.model.UpdateBean; +import per.goweii.wanandroid.module.mine.presenter.AboutPresenter; +import per.goweii.wanandroid.module.mine.view.AboutView; +import per.goweii.wanandroid.utils.UpdateUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; +import per.goweii.wanandroid.widget.LogoAnimView; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class AboutActivity extends BaseActivity { +public class AboutActivity extends BaseActivity implements AboutView { + private static final int UPDATE_TYPE_REFRESH = 1; + private static final int UPDATE_TYPE_NOTICE = 2; + private static final int UPDATE_TYPE_SHARE = 3; + + private static final int REQ_CODE_PERMISSION = 1; + @BindView(R.id.abc) + ActionBarCommon abc; + @BindView(R.id.tv_update) + TextView tv_update; @BindView(R.id.tv_version_name) TextView tv_version_name; @BindView(R.id.tv_web) @@ -31,35 +68,63 @@ public class AboutActivity extends BaseActivity { TextView tv_about; @BindView(R.id.tv_github) TextView tv_github; + @BindView(R.id.lav) + LogoAnimView lav; + + private RuntimeRequester mRuntimeRequester; - public static void start(Context context){ + public static void start(Context context) { Intent intent = new Intent(context, AboutActivity.class); context.startActivity(intent); } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_about; + protected ActivityAboutBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityAboutBinding.inflate(inflater); } @Nullable @Override - protected MvpPresenter initPresenter() { - return null; + protected AboutPresenter initPresenter() { + return new AboutPresenter(); } @Override protected void initView() { - tv_version_name.setText(String.format("V%s(%d)", - AppInfoUtils.getVersionName(), AppInfoUtils.getVersionCode())); + abc.setOnRightIconClickListener(new OnActionBarChildClickListener() { + @Override + public void onClick(View v) { + presenter.update(UPDATE_TYPE_SHARE); + } + }); + + ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), new OnApplyWindowInsetsListener() { + @NonNull + @Override + public WindowInsetsCompat onApplyWindowInsets(@NonNull View v, @NonNull WindowInsetsCompat insets) { + final int b = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom; + binding.getRoot().setPadding(0, 0, 0, b); + return insets; + } + }); } @Override protected void loadData() { + tv_version_name.setText(String.format("%s(%d)", + AppInfoUtils.getVersionName(), AppInfoUtils.getVersionCode())); + presenter.update(UPDATE_TYPE_REFRESH); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + lav.randomBlink(); } @OnClick({ - R.id.ll_web, R.id.ll_about, R.id.ll_github + R.id.ll_update, R.id.ll_web, R.id.ll_about, R.id.ll_github, R.id.ll_beta }) @Override public void onClick(View v) { @@ -69,17 +134,208 @@ public void onClick(View v) { @Override protected void onClick2(View v) { switch (v.getId()) { - default: + case R.id.ll_update: + presenter.update(UPDATE_TYPE_NOTICE); break; case R.id.ll_web: - WebActivity.start(getContext(), tv_web.getText().toString(), "https://www.wanandroid.com"); + UrlOpenUtils.Companion + .with("https://wanandroid.com") + .title(tv_web.getText().toString()) + .open(getContext()); break; case R.id.ll_about: - WebActivity.start(getContext(), tv_about.getText().toString(), "https://www.wanandroid.com/about"); + UrlOpenUtils.Companion + .with("https://wanandroid.com/about") + .title(tv_about.getText().toString()) + .open(getContext()); break; case R.id.ll_github: - WebActivity.start(getContext(), tv_github.getText().toString(), "https://github.com/goweii/WanAndroid"); + UrlOpenUtils.Companion + .with("https://github.com/goweii/WanAndroid") + .title(tv_github.getText().toString()) + .open(getContext()); + break; + case R.id.ll_beta: + String msg = new StringBuilder() + .append(getString(R.string.apply_for_a_closed_beta_msg)) + .toString(); + TipDialog.with(this) + .title(getString(R.string.apply_for_a_closed_beta)) + .message(msg) + .msgCenter(false) + .yesText(getString(R.string.i_know)) + .singleYesBtn() + .show(); + break; + default: + break; + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (mRuntimeRequester != null) { + mRuntimeRequester.onActivityResult(requestCode); + } + } + + @Override + public void updateSuccess(int code, UpdateBean data, int updateType) { + if (UpdateUtils.getInstance().isNewest(data)) { + tv_update.setTextColor(ResUtils.getThemeColor(getContext(), R.attr.colorTextMain)); + tv_update.setText(getString(R.string.discover_the_new_version_tips, data.getVersion_name())); + switch (updateType) { + case UPDATE_TYPE_REFRESH: + break; + case UPDATE_TYPE_NOTICE: + showUpdateDialog(data, false); + break; + case UPDATE_TYPE_SHARE: + showShareDialog(data); + break; + default: + break; + } + } else { + switch (updateType) { + case UPDATE_TYPE_REFRESH: + presenter.betaUpdate(updateType); + break; + case UPDATE_TYPE_NOTICE: + presenter.betaUpdate(updateType); + break; + case UPDATE_TYPE_SHARE: + showShareDialog(data); + break; + default: + break; + } + } + } + + @Override + public void updateFailed(int code, String msg, int updateType) { + tv_update.setTextColor(ResUtils.getThemeColor(getContext(), R.attr.colorTextThird)); + tv_update.setText(getString(R.string.this_is_the_latest_version)); + } + + @Override + public void betaUpdateSuccess(int code, UpdateBean data, int updateType) { + if (UpdateUtils.getInstance().isNewest(data)) { + tv_update.setTextColor(ResUtils.getThemeColor(getContext(), R.attr.colorTextAccent)); + tv_update.setText(getString(R.string.discover_the_closed_beta_version_tips, data.getVersion_name())); + switch (updateType) { + case UPDATE_TYPE_REFRESH: + break; + case UPDATE_TYPE_NOTICE: + showUpdateDialog(data, true); + break; + case UPDATE_TYPE_SHARE: + break; + default: + break; + } + } else { + tv_update.setTextColor(ResUtils.getThemeColor(getContext(), R.attr.colorTextThird)); + tv_update.setText(getString(R.string.this_is_the_latest_version)); + switch (updateType) { + case UPDATE_TYPE_REFRESH: + break; + case UPDATE_TYPE_NOTICE: + ToastMaker.showShort(getString(R.string.this_is_the_latest_version)); + break; + case UPDATE_TYPE_SHARE: + break; + default: + break; + } + } + } + + @Override + public void betaUpdateFailed(int code, String msg, int updateType) { + tv_update.setTextColor(ResUtils.getThemeColor(getContext(), R.attr.colorTextThird)); + tv_update.setText(getString(R.string.this_is_the_latest_version)); + switch (updateType) { + case UPDATE_TYPE_REFRESH: + break; + case UPDATE_TYPE_NOTICE: + ToastMaker.showShort(getString(R.string.this_is_the_latest_version)); + break; + case UPDATE_TYPE_SHARE: + break; + default: break; } } + + private void showUpdateDialog(@NonNull UpdateBean data, boolean isBeta) { + UpdateDialog.with(getContext()) + .setTest(isBeta) + .setUrl(data.getUrl()) + .setUrlBackup(data.getUrl_backup()) + .setVersionCode(data.getVersion_code()) + .setVersionName(data.getVersion_name()) + .setForce(UpdateUtils.getInstance().shouldForceUpdate(data)) + .setDescription(data.getDesc()) + .setTime(data.getTime()) + .setOnUpdateListener(new UpdateDialog.OnUpdateListener() { + @Override + public void onDownload(String url, String urlBackup, boolean isForce) { + download(url, urlBackup, isForce); + } + + @Override + public void onIgnore(String versionName, int versionCode) { + if (isBeta) { + UpdateUtils.getInstance().ignoreBeta(versionName, versionCode); + } else { + UpdateUtils.getInstance().ignore(versionCode); + } + } + }) + .show(); + } + + private void download(final String url, final String urlBackup, final boolean isForce) { + mRuntimeRequester = PermissionUtils.request(new RequestListener() { + @Override + public void onSuccess() { + DownloadDialog.with(getActivity(), isForce, url, urlBackup, null); + } + + @Override + public void onFailed() { + } + }, getContext(), REQ_CODE_PERMISSION, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE); + } + + private void showShareDialog(@NonNull UpdateBean data) { + new CardShareDialog(this, R.layout.layout_app_qrcode_share, new Function1() { + @SuppressLint("DefaultLocale") + @Override + public Unit invoke(View view) { + TextView tv_name = view.findViewById(R.id.layout_app_qrcode_share_tv_name); + TextView tv_version = view.findViewById(R.id.layout_app_qrcode_share_tv_version); + ImageView iv_qrcode = view.findViewById(R.id.layout_app_qrcode_share_iv_qrcode); + tv_name.setText(AppInfoUtils.getAppName()); + tv_version.setText(getString(R.string.app_version_name_code, data.getVersion_name(), data.getVersion_code())); + new CodeEncoder(new ZXingEncodeQRCodeProcessor()) + .encode(data.getUrl(), new Function1() { + @Override + public Unit invoke(Bitmap bitmap) { + iv_qrcode.setImageBitmap(bitmap); + return null; + } + }, new Function1() { + @Override + public Unit invoke(Exception e) { + return null; + } + }); + return null; + } + }).show(); + } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/AboutMeActivity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/AboutMeActivity.java index 316ec61b..cd5ddeb5 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/AboutMeActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/AboutMeActivity.java @@ -8,8 +8,9 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.support.annotation.Nullable; +import android.net.Uri; import android.text.TextUtils; +import android.view.LayoutInflater; import android.view.View; import android.view.animation.DecelerateInterpolator; import android.widget.ImageView; @@ -17,6 +18,9 @@ import android.widget.RelativeLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.daimajia.swipe.SwipeLayout; import java.util.ArrayList; @@ -24,33 +28,40 @@ import butterknife.BindView; import butterknife.OnClick; -import butterknife.OnLongClick; +import per.goweii.actionbarex.common.ActionBarCommon; +import per.goweii.actionbarex.common.OnActionBarChildClickListener; import per.goweii.anypermission.RequestListener; import per.goweii.anypermission.RuntimeRequester; import per.goweii.basic.core.base.BaseActivity; import per.goweii.basic.core.glide.GlideHelper; import per.goweii.basic.core.permission.PermissionUtils; import per.goweii.basic.ui.toast.ToastMaker; +import per.goweii.basic.utils.CopyUtils; +import per.goweii.basic.utils.LogUtils; +import per.goweii.basic.utils.RandomUtils; import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.percentimageview.percentimageview.PercentImageView; +import per.goweii.wanandroid.BuildConfig; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.module.main.activity.WebActivity; +import per.goweii.wanandroid.databinding.ActivityAboutMeBinding; import per.goweii.wanandroid.module.mine.model.AboutMeBean; import per.goweii.wanandroid.module.mine.presenter.AboutMePresenter; import per.goweii.wanandroid.module.mine.view.AboutMeView; import per.goweii.wanandroid.utils.ImageLoader; +import per.goweii.wanandroid.utils.UrlOpenUtils; +import per.goweii.wanandroid.utils.router.Router; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class AboutMeActivity extends BaseActivity implements AboutMeView { +public class AboutMeActivity extends BaseActivity implements AboutMeView { private static final int REQUEST_CODE_PERMISSION = 1; + @BindView(R.id.abc) + ActionBarCommon abc; @BindView(R.id.sl) SwipeLayout sl; @BindView(R.id.iv_blur) @@ -81,8 +92,8 @@ public class AboutMeActivity extends BaseActivity implements A RelativeLayout rl_info; @BindView(R.id.rl_reward) RelativeLayout rl_reward; - @BindView(R.id.piv_qq_qrcode) - PercentImageView piv_qq_qrcode; + @BindView(R.id.piv_zfb_qrcode) + PercentImageView piv_zfb_qrcode; @BindView(R.id.piv_wx_qrcode) PercentImageView piv_wx_qrcode; @@ -93,9 +104,10 @@ public static void start(Context context) { context.startActivity(intent); } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_about_me; + protected ActivityAboutMeBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityAboutMeBinding.inflate(inflater); } @Nullable @@ -106,6 +118,18 @@ protected AboutMePresenter initPresenter() { @Override protected void initView() { + abc.setOnRightTextClickListener(new OnActionBarChildClickListener() { + @Override + public void onClick(View v) { + StringBuilder s = new StringBuilder(); + String content = getString(R.string.your_friend_orders_you_a_coffee_for_two); + String key = String.format(BuildConfig.WANPWD_FORMAT, BuildConfig.WANPWD_TYPE_ABOUTME, RandomUtils.randomLetter(10)); + s.append(String.format(content, key)); + LogUtils.d("UserPageActivity", s); + CopyUtils.copyText(s.toString()); + ToastMaker.showShort(getString(R.string.copied)); + } + }); changeVisible(View.INVISIBLE, civ_icon, tv_name, tv_sign); changeVisible(View.GONE, ll_github, ll_jianshu, ll_qq, ll_qq_group); } @@ -113,10 +137,24 @@ protected void initView() { @Override protected void loadData() { presenter.getAboutMe(); + + Uri uri = Router.getUriFrom(getIntent()); + if (uri != null) { + boolean secondFloor = uri.getBooleanQueryParameter("second_floor", false); + if (secondFloor) { + sl.post(new Runnable() { + @Override + public void run() { + sl.open(true, true, SwipeLayout.DragEdge.Bottom); + } + }); + } + } } @OnClick({ - R.id.ll_github, R.id.ll_jianshu, R.id.ll_qq, R.id.ll_qq_group + R.id.ll_github, R.id.ll_jianshu, R.id.ll_qq, R.id.ll_qq_group, + R.id.piv_zfb_qrcode, R.id.piv_wx_qrcode }) @Override public void onClick(View v) { @@ -129,10 +167,16 @@ protected void onClick2(View v) { default: break; case R.id.ll_github: - WebActivity.start(getContext(), tv_name.getText().toString(), tv_github.getText().toString()); + UrlOpenUtils.Companion + .with(tv_github.getText().toString()) + .title(tv_name.getText().toString()) + .open(getContext()); break; case R.id.ll_jianshu: - WebActivity.start(getContext(), tv_name.getText().toString(), tv_jianshu.getText().toString()); + UrlOpenUtils.Companion + .with(tv_jianshu.getText().toString()) + .title(tv_name.getText().toString()) + .open(getContext()); break; case R.id.ll_qq: presenter.openQQChat(); @@ -140,19 +184,11 @@ protected void onClick2(View v) { case R.id.ll_qq_group: presenter.openQQGroup(); break; - } - } - - @OnLongClick({R.id.piv_qq_qrcode, R.id.piv_wx_qrcode}) - public boolean onLongClick(View v) { - switch (v.getId()) { - default: - break; - case R.id.piv_qq_qrcode: + case R.id.piv_zfb_qrcode: mRuntimeRequester = PermissionUtils.request(new RequestListener() { @Override public void onSuccess() { - presenter.saveQQQrcode(); + presenter.saveZFBQrcode(); } @Override @@ -173,7 +209,6 @@ public void onFailed() { }, getContext(), REQUEST_CODE_PERMISSION, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE); break; } - return true; } @Override @@ -252,11 +287,12 @@ public void onAnimationRepeat(Animator animation) { @Override public void getAboutMeSuccess(int code, AboutMeBean data) { - ImageLoader.image(piv_qq_qrcode, data.getQq_qrcode()); + ImageLoader.image(piv_zfb_qrcode, data.getZfb_qrcode()); ImageLoader.image(piv_wx_qrcode, data.getWx_qrcode()); GlideHelper.with(getContext()) + .asBitmap() .load(data.getIcon()) - .get(new SimpleCallback() { + .getBitmap(new SimpleCallback() { @Override public void onResult(Bitmap bitmap) { ImageLoader.userIcon(civ_icon, data.getIcon()); diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/AiSettingActivity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/AiSettingActivity.java new file mode 100644 index 00000000..e82172a6 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/AiSettingActivity.java @@ -0,0 +1,215 @@ +package per.goweii.wanandroid.module.mine.activity; + +import android.content.Context; +import android.content.Intent; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.CompoundButton; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.SwitchCompat; + +import java.util.ArrayList; +import java.util.List; + +import butterknife.BindView; +import butterknife.OnClick; +import per.goweii.basic.core.base.BaseActivity; +import per.goweii.basic.core.base.BasePresenter; +import per.goweii.basic.ui.dialog.ListDialog; +import per.goweii.basic.utils.listener.SimpleCallback; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.ActivityAiSettingBinding; +import per.goweii.wanandroid.module.mine.dialog.InfoEditDialog; +import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.ai.AiModel; +import per.goweii.wanandroid.utils.ai.AiProvider; + +public class AiSettingActivity extends BaseActivity { + @BindView(R.id.sc_enable_ai) + SwitchCompat sc_enable_ai; + @BindView(R.id.tv_ai_provider) + TextView tv_ai_provider; + @BindView(R.id.tv_ai_api_key) + TextView tv_ai_api_key; + @BindView(R.id.tv_ai_model) + TextView tv_ai_model; + + private boolean mAiEnabled; + private String mAiProvider; + private String mAiApiKey; + private String mAiModel; + + public static void start(Context context) { + Intent intent = new Intent(context, AiSettingActivity.class); + context.startActivity(intent); + } + + @Nullable + @Override + protected ActivityAiSettingBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityAiSettingBinding.inflate(inflater); + } + + @Nullable + @Override + protected BasePresenter initPresenter() { + return null; + } + + @Override + protected void initView() { + mAiEnabled = SettingUtils.getInstance().isAiEnabled(); + mAiProvider = SettingUtils.getInstance().getAiProvider(); + mAiApiKey = SettingUtils.getInstance().getAiApiKey(); + mAiModel = SettingUtils.getInstance().getAiModel(); + + AiProvider aiProvider = AiProvider.findProvider(mAiProvider); + if (aiProvider == null) { + aiProvider = AiProvider.getProviders().get(0); + } + AiModel aiModel = aiProvider.findModel(mAiModel); + if (aiModel == null) { + aiModel = aiProvider.getModels().get(0); + } + + sc_enable_ai.setChecked(mAiEnabled); + tv_ai_provider.setText(aiProvider.getName()); + tv_ai_api_key.setText("***"); + tv_ai_model.setText(aiModel.getName()); + + sc_enable_ai.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton v, boolean isChecked) { + SettingUtils.getInstance().setAiEnabled(isChecked); + } + }); + } + + @Override + protected void loadData() { + } + + @Override + protected void onStop() { + super.onStop(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + } + + + @OnClick({ + R.id.ll_ai_provider, R.id.ll_ai_api_key, R.id.ll_ai_model, + }) + @Override + public void onClick(View v) { + super.onClick(v); + } + + @Override + protected void onClick2(View v) { + switch (v.getId()) { + case R.id.ll_ai_provider: { + final List providers = AiProvider.getProviders(); + final List nameList = new ArrayList<>(providers.size()); + for (AiProvider aiProvider : providers) { + nameList.add(aiProvider.getName()); + } + final String aiProviderId = SettingUtils.getInstance().getAiProvider(); + int selectedPos = -1; + for (int i = 0; i < providers.size(); i++) { + if (providers.get(i).getId().equals(aiProviderId)) { + selectedPos = i; + } + } + ListDialog.with(getContext()) + .cancelable(true) + .title(getString(R.string.ai_provider)) + .datas(nameList) + .currSelectPos(selectedPos) + .onItemSelectedListener(new ListDialog.OnItemSelectedListener() { + @Override + public void onSelect(String data, int pos) { + AiProvider aiProvider = providers.get(pos); + if (aiProvider.getId().equals(aiProviderId)) { + return; + } + mAiProvider = aiProvider.getId(); + SettingUtils.getInstance().setAiProvider(aiProvider.getId()); + tv_ai_provider.setText(aiProvider.getName()); + AiModel aiModel = aiProvider.getModels().get(0); + mAiModel = aiModel.getId(); + tv_ai_model.setText(aiModel.getName()); + } + }) + .show(); + } + break; + case R.id.ll_ai_model: { + final String aiProviderId = SettingUtils.getInstance().getAiProvider(); + final String aiModelId = SettingUtils.getInstance().getAiModel(); + final AiProvider provider = AiProvider.findProvider(aiProviderId); + if (provider == null) { + return; + } + final List models = provider.getModels(); + final List nameList = new ArrayList<>(models.size()); + for (AiModel aiModel : models) { + nameList.add(aiModel.getName()); + } + int selectedPos = -1; + for (int i = 0; i < models.size(); i++) { + if (models.get(i).getId().equals(aiModelId)) { + selectedPos = i; + } + } + ListDialog.with(getContext()) + .cancelable(true) + .title(getString(R.string.ai_model)) + .datas(nameList) + .currSelectPos(selectedPos) + .onItemSelectedListener(new ListDialog.OnItemSelectedListener() { + @Override + public void onSelect(String data, int pos) { + AiModel aiModel = models.get(pos); + if (aiModel.getId().equals(aiModelId)) { + return; + } + mAiModel = aiModel.getId(); + SettingUtils.getInstance().setAiModel(aiModel.getId()); + tv_ai_model.setText(aiModel.getName()); + } + }) + .show(); + } + break; + case R.id.ll_ai_api_key: { + final String apiKey = SettingUtils.getInstance().getAiApiKey(); + InfoEditDialog.with(getContext()) + .title(getString(R.string.ai_api_key)) + .text(apiKey) + .lines(3) + .show(new SimpleCallback() { + @Override + public void onResult(String data) { + if (TextUtils.equals(data, apiKey)) { + return; + } + mAiApiKey = data; + SettingUtils.getInstance().setAiApiKey(data); + } + }); + } + break; + default: + break; + } + } + +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/CoinActivity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/CoinActivity.java index 7aa6055b..d8205688 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/CoinActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/CoinActivity.java @@ -2,45 +2,46 @@ import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.kennyc.view.MultiStateView; import butterknife.BindView; -import per.goweii.actionbarex.common.ActionBarCommon; -import per.goweii.actionbarex.common.OnActionBarChildClickListener; +import per.goweii.actionbarex.common.ActionBarSuper; import per.goweii.basic.core.base.BaseActivity; import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.AnimatorUtils; +import per.goweii.basic.utils.listener.OnClickListener2; import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.ActivityCoinBinding; import per.goweii.wanandroid.module.main.dialog.WebDialog; import per.goweii.wanandroid.module.mine.adapter.CoinRecordAdapter; import per.goweii.wanandroid.module.mine.model.CoinRecordBean; import per.goweii.wanandroid.module.mine.presenter.CoinPresenter; import per.goweii.wanandroid.module.mine.view.CoinView; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; -import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.RvConfigUtils; /** * @author CuiZhen * @date 2019/8/31 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class CoinActivity extends BaseActivity implements CoinView { +public class CoinActivity extends BaseActivity implements CoinView { private static final int PAGE_START = 1; @BindView(R.id.abc) - ActionBarCommon abc; + ActionBarSuper abc; @BindView(R.id.tv_coin) TextView tv_coin; @BindView(R.id.msv) @@ -56,9 +57,10 @@ public static void start(Context context) { context.startActivity(intent); } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_coin; + protected ActivityCoinBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityCoinBinding.inflate(inflater); } @Nullable @@ -69,16 +71,22 @@ protected CoinPresenter initPresenter() { @Override protected void initView() { - abc.setOnRightIconClickListener(new OnActionBarChildClickListener() { + abc.getRightActionView(0).setOnClickListener(new OnClickListener2() { + @Override + public void onClick2(View v) { + WebDialog.create(getContext(), "https://wanandroid.com/blog/show/2653").show(); + } + }); + abc.getRightActionView(1).setOnClickListener(new OnClickListener2() { @Override - public void onClick(View v) { - WebDialog.create(getContext(), "https://www.wanandroid.com/blog/show/2653").show(); + public void onClick2(View v) { + CoinRankActivity.start(getContext()); } }); tv_coin.setText("0"); rv.setLayoutManager(new LinearLayoutManager(getContext())); mCoinRecordAdapter = new CoinRecordAdapter(); - RvAnimUtils.setAnim(mCoinRecordAdapter, SettingUtils.getInstance().getRvAnim()); + RvConfigUtils.init(mCoinRecordAdapter); mCoinRecordAdapter.setEnableLoadMore(false); mCoinRecordAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/CoinRankActivity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/CoinRankActivity.java index 33f5ef29..43f5df85 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/CoinRankActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/CoinRankActivity.java @@ -2,11 +2,14 @@ import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.kennyc.view.MultiStateView; @@ -16,6 +19,7 @@ import per.goweii.basic.core.base.BaseActivity; import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.ActivityCoinRankBinding; import per.goweii.wanandroid.module.home.activity.UserPageActivity; import per.goweii.wanandroid.module.main.model.CoinInfoBean; import per.goweii.wanandroid.module.mine.adapter.CoinRankAdapter; @@ -23,17 +27,14 @@ import per.goweii.wanandroid.module.mine.presenter.CoinRankPresenter; import per.goweii.wanandroid.module.mine.view.CoinRankView; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; -import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.RvConfigUtils; /** * @author CuiZhen * @date 2019/8/31 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class CoinRankActivity extends BaseActivity implements CoinRankView { +public class CoinRankActivity extends BaseActivity implements CoinRankView { private static final int PAGE_START = 1; @@ -52,9 +53,10 @@ public static void start(Context context) { context.startActivity(intent); } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_coin_rank; + protected ActivityCoinRankBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityCoinRankBinding.inflate(inflater); } @Nullable @@ -73,7 +75,7 @@ public void onClick(View v) { }); rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new CoinRankAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); + RvConfigUtils.init(mAdapter); mAdapter.setEnableLoadMore(false); mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/CollectionActivity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/CollectionActivity.java index 44f7d2cc..18366aaf 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/CollectionActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/CollectionActivity.java @@ -2,33 +2,36 @@ import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.viewpager.widget.ViewPager; + import butterknife.BindView; import per.goweii.actionbarex.ActionBarEx; import per.goweii.basic.core.adapter.FixedFragmentPagerAdapter; import per.goweii.basic.core.base.BaseActivity; +import per.goweii.basic.core.base.BasePresenter; import per.goweii.basic.core.mvp.MvpPresenter; import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.wanandroid.R; import per.goweii.wanandroid.common.Config; -import per.goweii.wanandroid.common.ScrollTop; +import per.goweii.wanandroid.databinding.ActivityCollectionBinding; import per.goweii.wanandroid.module.mine.fragment.CollectionArticleFragment; import per.goweii.wanandroid.module.mine.fragment.CollectionLinkFragment; import per.goweii.wanandroid.utils.MagicIndicatorUtils; +import per.goweii.wanandroid.utils.RvScrollTopUtils; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class CollectionActivity extends BaseActivity { +public class CollectionActivity extends BaseActivity { @BindView(R.id.ab) ActionBarEx ab; @@ -44,14 +47,15 @@ public static void start(Context context) { context.startActivity(intent); } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_collection; + protected ActivityCollectionBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityCollectionBinding.inflate(inflater); } @Nullable @Override - protected MvpPresenter initPresenter() { + protected BasePresenter initPresenter() { return null; } @@ -66,7 +70,7 @@ public void onClick(View v) { } }); mAdapter = new FixedFragmentPagerAdapter(getSupportFragmentManager()); - mAdapter.setTitles("文章", "网址"); + mAdapter.setTitles(getString(R.string.article), getString(R.string.website)); mAdapter.setFragmentList( CollectionArticleFragment.create(), CollectionLinkFragment.create() @@ -88,8 +92,8 @@ private void notifyScrollTop(int pos) { long currClickTime = System.currentTimeMillis(); if (lastClickPos == pos && currClickTime - lastClickTime <= Config.SCROLL_TOP_DOUBLE_CLICK_DELAY) { Fragment fragment = mAdapter.getItem(pos); - if (fragment instanceof ScrollTop) { - ScrollTop scrollTop = (ScrollTop) fragment; + if (fragment instanceof RvScrollTopUtils.ScrollTop) { + RvScrollTopUtils.ScrollTop scrollTop = (RvScrollTopUtils.ScrollTop) fragment; scrollTop.scrollTop(); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/HostInterruptActivity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/HostInterruptActivity.java index 0bb05a5e..8af7e257 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/HostInterruptActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/HostInterruptActivity.java @@ -2,33 +2,36 @@ import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; import android.view.View; import android.widget.ImageView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.viewpager.widget.ViewPager; + import butterknife.BindView; import per.goweii.actionbarex.ActionBarEx; import per.goweii.basic.core.adapter.FixedFragmentPagerAdapter; import per.goweii.basic.core.base.BaseActivity; +import per.goweii.basic.core.base.BasePresenter; import per.goweii.basic.core.mvp.MvpPresenter; import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.wanandroid.R; import per.goweii.wanandroid.common.Config; -import per.goweii.wanandroid.common.ScrollTop; +import per.goweii.wanandroid.databinding.ActivityHostInterruptBinding; import per.goweii.wanandroid.module.mine.fragment.HostBlackFragment; import per.goweii.wanandroid.module.mine.fragment.HostWhiteFragment; import per.goweii.wanandroid.utils.MagicIndicatorUtils; +import per.goweii.wanandroid.utils.RvScrollTopUtils; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class HostInterruptActivity extends BaseActivity { +public class HostInterruptActivity extends BaseActivity { @BindView(R.id.ab) ActionBarEx ab; @@ -44,14 +47,15 @@ public static void start(Context context) { context.startActivity(intent); } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_host_interrupt; + protected ActivityHostInterruptBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityHostInterruptBinding.inflate(inflater); } @Nullable @Override - protected MvpPresenter initPresenter() { + protected BasePresenter initPresenter() { return null; } @@ -66,7 +70,7 @@ public void onClick(View v) { } }); mAdapter = new FixedFragmentPagerAdapter(getSupportFragmentManager()); - mAdapter.setTitles("白名单", "黑名单"); + mAdapter.setTitles(getString(R.string.whitelist), getString(R.string.blacklist)); mAdapter.setFragmentList( HostWhiteFragment.create(), HostBlackFragment.create() @@ -88,8 +92,8 @@ private void notifyScrollTop(int pos) { long currClickTime = System.currentTimeMillis(); if (lastClickPos == pos && currClickTime - lastClickTime <= Config.SCROLL_TOP_DOUBLE_CLICK_DELAY) { Fragment fragment = mAdapter.getItem(pos); - if (fragment instanceof ScrollTop) { - ScrollTop scrollTop = (ScrollTop) fragment; + if (fragment instanceof RvScrollTopUtils.ScrollTop) { + RvScrollTopUtils.ScrollTop scrollTop = (RvScrollTopUtils.ScrollTop) fragment; scrollTop.scrollTop(); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/LanguageActivity.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/LanguageActivity.kt new file mode 100644 index 00000000..8f2eb616 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/LanguageActivity.kt @@ -0,0 +1,58 @@ +package per.goweii.wanandroid.module.mine.activity + +import android.content.Context +import android.content.Intent +import android.view.LayoutInflater +import androidx.recyclerview.widget.LinearLayoutManager +import per.goweii.basic.core.base.BaseActivity +import per.goweii.basic.core.base.BasePresenter +import per.goweii.basic.l10n.L10nManager +import per.goweii.basic.utils.LogUtils +import per.goweii.wanandroid.utils.recreate_anim.RecreateAnimation +import per.goweii.wanandroid.databinding.ActivityLanguageBinding +import per.goweii.wanandroid.module.mine.adapter.LanguageAdapter +import java.util.Locale + +class LanguageActivity : BaseActivity, ActivityLanguageBinding>() { + companion object { + @JvmStatic + fun start(context: Context) { + val intent = Intent(context, LanguageActivity::class.java) + context.startActivity(intent) + } + } + + private var adapter: LanguageAdapter? = null + + override fun initViewBinding(inflater: LayoutInflater): ActivityLanguageBinding { + return ActivityLanguageBinding.inflate(inflater) + } + + override fun initPresenter(): BasePresenter<*>? { + return null + } + + override fun initView() { + val appLocale = L10nManager.getApplicationLocale() + val sysLocale = L10nManager.getSystemLocale(this) + adapter = LanguageAdapter(appLocale, sysLocale) + binding.rv.layoutManager = LinearLayoutManager(this) + binding.rv.adapter = adapter + + adapter?.setOnItemClickListener { _, _, position -> + val locale = adapter?.getItem(position) + LogUtils.e("LanguageActivity", "old decorView is ${window.decorView}") + RecreateAnimation( + activity = this, + trigger = { L10nManager.setApplicationLocales(locale) } + ).start() + } + } + + override fun loadData() { + val locales = arrayListOf() + locales.add(null) + locales.addAll(L10nManager.getSupportedLocales(this)) + adapter?.setNewData(locales) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/MessageActivity.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/MessageActivity.kt new file mode 100644 index 00000000..780b1440 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/MessageActivity.kt @@ -0,0 +1,62 @@ +package per.goweii.wanandroid.module.mine.activity + +import android.content.Context +import android.content.Intent +import android.view.LayoutInflater +import android.view.View +import butterknife.BindView +import per.goweii.actionbarex.ActionBarEx +import per.goweii.actionbarex.common.ActionIconView +import per.goweii.basic.core.adapter.FixedFragmentPagerAdapter +import per.goweii.basic.core.base.BaseActivity +import per.goweii.basic.core.base.BasePresenter +import per.goweii.basic.core.base.BaseView +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.ActivityMessageBinding +import per.goweii.wanandroid.module.mine.fragment.MessageReadedFragment +import per.goweii.wanandroid.module.mine.fragment.MessageUnreadFragment +import per.goweii.wanandroid.utils.MagicIndicatorUtils + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +class MessageActivity : BaseActivity, ActivityMessageBinding>() { + + @BindView(R.id.ab) + lateinit var ab: ActionBarEx + + companion object { + @JvmStatic + fun start(context: Context) { + val intent = Intent(context, MessageActivity::class.java) + context.startActivity(intent) + } + } + + override fun initViewBinding(inflater: LayoutInflater): ActivityMessageBinding { + return ActivityMessageBinding.inflate(inflater) + } + + override fun initPresenter(): BasePresenter? = null + + override fun initView() { + ab.getView(R.id.action_bar_fixed_magic_indicator_iv_back).apply { + visibility = View.VISIBLE + setOnClickListener { + finish() + } + } + val adapter = FixedFragmentPagerAdapter(supportFragmentManager) + adapter.setTitles(getString(R.string.new_messages),getString(R.string.historical_messages)) + adapter.setFragmentList( + MessageUnreadFragment.create(), + MessageReadedFragment.create() + ) + binding.vp.adapter = adapter + MagicIndicatorUtils.commonNavigator(ab.getView(R.id.mi), binding.vp, adapter, null) + } + + override fun loadData() {} +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/MineShareActivity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/MineShareActivity.java index 3c9f9f99..d028a2ae 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/MineShareActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/MineShareActivity.java @@ -2,20 +2,21 @@ import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.kennyc.view.MultiStateView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import java.util.List; - import butterknife.BindView; import per.goweii.actionbarex.common.ActionBarCommon; import per.goweii.actionbarex.common.OnActionBarChildClickListener; @@ -25,10 +26,10 @@ import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.wanandroid.R; import per.goweii.wanandroid.common.Config; +import per.goweii.wanandroid.databinding.ActivityMineShareBinding; import per.goweii.wanandroid.event.ArticleDeleteEvent; import per.goweii.wanandroid.event.ArticleShareEvent; import per.goweii.wanandroid.event.CollectionEvent; -import per.goweii.wanandroid.event.SettingChangeEvent; import per.goweii.wanandroid.module.main.activity.ShareArticleActivity; import per.goweii.wanandroid.module.main.adapter.ArticleAdapter; import per.goweii.wanandroid.module.main.model.ArticleBean; @@ -37,19 +38,15 @@ import per.goweii.wanandroid.module.mine.presenter.MineSharePresenter; import per.goweii.wanandroid.module.mine.view.MineShareView; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; import per.goweii.wanandroid.utils.RvScrollTopUtils; -import per.goweii.wanandroid.utils.SettingUtils; import per.goweii.wanandroid.widget.CollectView; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class MineShareActivity extends BaseActivity implements MineShareView { +public class MineShareActivity extends BaseActivity implements MineShareView { public static final int PAGE_START = 1; @@ -79,35 +76,7 @@ public void onCollectionEvent(CollectionEvent event) { if (isDestroyed()) { return; } - if (event.isCollect()) { - currPage = PAGE_START; - presenter.getMineShareArticleList(currPage, true); - } else { - if (event.getArticleId() != -1 || event.getCollectId() != -1) { - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (event.getArticleId() != -1) { - if (item.getOriginId() == event.getArticleId()) { - mAdapter.remove(i); - break; - } - } else if (event.getCollectId() != -1) { - if (item.getId() == event.getCollectId()) { - mAdapter.remove(i); - break; - } - } - } - } - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); - } + mAdapter.notifyCollectionEvent(event); } @Subscribe(threadMode = ThreadMode.MAIN) @@ -122,14 +91,16 @@ public void onArticleDeleteEvent(ArticleDeleteEvent event) { currPage = PAGE_START; presenter.getMineShareArticleList(currPage, true); } else { - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (event.getArticleId() == item.getId()) { - mAdapter.remove(i); - break; + mAdapter.forEach(new ArticleAdapter.ArticleForEach() { + @Override + public boolean forEach(int dataPos, int adapterPos, ArticleBean bean) { + if (event.getArticleId() == bean.getId()) { + mAdapter.remove(adapterPos); + return true; + } + return false; } - } + }); } } @@ -138,9 +109,10 @@ protected boolean isRegisterEventBus() { return true; } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_mine_share; + protected ActivityMineShareBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityMineShareBinding.inflate(inflater); } @Nullable @@ -178,7 +150,6 @@ public void onRefresh() { }); rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new MineShareArticleAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); mAdapter.setEnableLoadMore(false); mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override @@ -201,7 +172,7 @@ public void uncollect(ArticleBean item, CollectView v) { @Override public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) { mAdapter.closeAll(null); - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item == null) { return; } @@ -229,14 +200,14 @@ public void onResult() { protected void loadData() { MultiStateUtils.toLoading(msv); currPage = PAGE_START; - presenter.getMineShareArticleList(currPage, true); + presenter.getMineShareArticleList(currPage, false); } @Override public void getMineShareArticleListSuccess(int code, ArticleListBean data) { currPage = data.getCurPage() + PAGE_START; if (data.getCurPage() == 1) { - mAdapter.setNewData(data.getDatas()); + mAdapter.setArticleData(data.getDatas()); mAdapter.setEnableLoadMore(true); if (data.getDatas() == null || data.getDatas().isEmpty()) { MultiStateUtils.toEmpty(msv); diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/OpenActivity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/OpenActivity.java index 1021dbde..5080f827 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/OpenActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/OpenActivity.java @@ -2,36 +2,39 @@ import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.text.TextUtils; +import android.view.LayoutInflater; import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.kennyc.view.MultiStateView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; import java.util.ArrayList; import java.util.List; import butterknife.BindView; import per.goweii.basic.core.base.BaseActivity; +import per.goweii.basic.core.base.BasePresenter; import per.goweii.basic.core.mvp.MvpPresenter; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.module.main.activity.WebActivity; +import per.goweii.wanandroid.databinding.ActivityOpenBinding; import per.goweii.wanandroid.module.mine.model.OpenEntity; import per.goweii.wanandroid.utils.MultiStateUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class OpenActivity extends BaseActivity { +public class OpenActivity extends BaseActivity { @BindView(R.id.msv) MultiStateView msv; @@ -47,14 +50,15 @@ public static void start(Context context) { context.startActivity(intent); } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_open; + protected ActivityOpenBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityOpenBinding.inflate(inflater); } @Nullable @Override - protected MvpPresenter initPresenter() { + protected BasePresenter initPresenter() { return null; } @@ -78,7 +82,10 @@ protected void convert(BaseViewHolder helper, OpenEntity item) { public void onItemClick(BaseQuickAdapter adapter, View view, int position) { OpenEntity item = mAdapter.getItem(position); if (item != null) { - WebActivity.start(getContext(), item.getProject(), item.getLink()); + UrlOpenUtils.Companion + .with(item.getLink()) + .title(item.getProject()) + .open(getContext()); } } }); @@ -95,16 +102,14 @@ protected void loadData() { list.add(new OpenEntity("goweii/ActionBarEx", "高拓展高自定义性ActionBar,完美替代Android系统默认", "https://github.com/goweii/ActionBarEx")); list.add(new OpenEntity("goweii/AnyLayer", "用于替代Android自带Dialog和PopupWindow", "https://github.com/goweii/AnyLayer")); list.add(new OpenEntity("goweii/AnyDialog", "Android高定制性,高易用性Dialog。", "https://github.com/goweii/AnyDialog")); + list.add(new OpenEntity("goweii/VisualEffect", "Used to achieve visual effects in Android, such as Gaussian blur, mosaic, watermark, etc.", "https://github.com/goweii/VisualEffect")); list.add(new OpenEntity("goweii/RevealLayout", "揭示效果布局,可以指定2个子布局,以圆形揭示效果切换选中状态", "https://github.com/goweii/RevealLayout")); - // list.add(new OpenEntity("goweii/AnyView", "快速将一个布局封装成一个自定义View", "https://github.com/goweii/AnyView")); list.add(new OpenEntity("goweii/PercentImageView", "自由指定宽高比的ImageView", "https://github.com/goweii/PercentImageView")); list.add(new OpenEntity("goweii/Blurred", "Android高斯模糊库", "https://github.com/goweii/Blurred")); list.add(new OpenEntity("goweii/AnyPermission", "Android权限申请(运行时权限、未知应用安装权限、悬浮窗权限、显示通知和访问通知权限)", "https://github.com/goweii/AnyPermission")); list.add(new OpenEntity("goweii/KeyboardCompat", "Android软键盘兼容库,支持开启关闭监听和EditText获取焦点时布局上移", "https://github.com/goweii/KeyboardCompat")); - list.add(new OpenEntity("goweii/PictureSelector", "forked from LuckSiege/PictureSelector. Picture Selector Library for Android 多图片及音视频选择器", "https://github.com/goweii/PictureSelector")); list.add(new OpenEntity("JakeWharton/butterknife", "Bind Android views and callbacks to fields and methods.", "https://github.com/JakeWharton/butterknife")); list.add(new OpenEntity("greenrobot/EventBus", "Event bus for Android and Java that simplifies communication between Activities, Fragments, Threads, Services, etc. Less code, better quality.", "https://github.com/greenrobot/EventBus")); - // list.add(new OpenEntity("Justson/AgentWeb", "AgentWeb is a powerful library based on Android WebView.", "https://github.com/Justson/AgentWeb")); list.add(new OpenEntity("google/gson", "A Java serialization/deserialization library to convert Java Objects into JSON and back", "https://github.com/google/gson")); list.add(new OpenEntity("franmontiel/PersistentCookieJar", "A persistent CookieJar implementation for OkHttp 3 based on SharedPreferences.", "https://github.com/franmontiel/PersistentCookieJar")); list.add(new OpenEntity("bumptech/glide", "An image loading and caching library for Android focused on smooth scrolling", "https://github.com/bumptech/glide")); @@ -113,11 +118,10 @@ protected void loadData() { list.add(new OpenEntity("vinc3m1/RoundedImageView", "A fast ImageView that supports rounded corners, ovals, and circles.", "https://github.com/vinc3m1/RoundedImageView")); list.add(new OpenEntity("hdodenhof/CircleImageView", "A circular ImageView for Android", "https://github.com/hdodenhof/CircleImageView")); list.add(new OpenEntity("hackware1993/MagicIndicator", "A powerful, customizable and extensible ViewPager indicator framework. As the best alternative of ViewPagerIndicator, TabLayout and PagerSlidingTabStrip —— 强大、可定制、易扩展的 ViewPager 指示器框架。是ViewPagerIndicator、TabLayout、PagerSlidingTabStrip的最佳替代品。支持角标,更支持在非ViewPager场景下使用(使用hide()、show()切换Fragment或使用setVisibility切换FrameLayout里的View等)", "https://github.com/hackware1993/MagicIndicator")); - // list.add(new OpenEntity("chrisbanes/PhotoView", "Implementation of ImageView for Android that supports zooming, by various touch gestures.", "https://github.com/chrisbanes/PhotoView")); + list.add(new OpenEntity("chrisbanes/PhotoView", "Implementation of ImageView for Android that supports zooming, by various touch gestures.", "https://github.com/chrisbanes/PhotoView")); list.add(new OpenEntity("zhanghai/MaterialProgressBar", "Material Design ProgressBar with consistent appearance", "https://github.com/zhanghai/MaterialProgressBar")); list.add(new OpenEntity("google/flexbox-layout", "Flexbox for Android", "https://github.com/google/flexbox-layout")); list.add(new OpenEntity("youth5201314/banner", "Android广告图片轮播控件,支持无限循环和多种主题,可以灵活设置轮播样式、动画、轮播和切换时间、位置、图片加载框架等!", "https://github.com/youth5201314/banner")); - list.add(new OpenEntity("mmin18/RealtimeBlurView", "A realtime blurring overlay for Android (like iOS UIVisualEffectView)", "https://github.com/mmin18/RealtimeBlurView")); list.add(new OpenEntity("Kennyc1012/MultiStateView", "Android View that displays different content based on its state", "https://github.com/Kennyc1012/MultiStateView")); list.add(new OpenEntity("JakeWharton/DiskLruCache", "Java implementation of a Disk-based LRU cache which specifically targets Android compatibility.", "https://github.com/JakeWharton/DiskLruCache")); list.add(new OpenEntity("daimajia/AndroidSwipeLayout", "The Most Powerful Swipe Layout!", "https://github.com/daimajia/AndroidSwipeLayout")); diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/ReadLaterActivity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/ReadLaterActivity.java index cb38908f..de3398c2 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/ReadLaterActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/ReadLaterActivity.java @@ -2,15 +2,18 @@ import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.text.TextUtils; +import android.view.LayoutInflater; import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.kennyc.view.MultiStateView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -18,31 +21,36 @@ import java.util.List; import butterknife.BindView; +import per.goweii.actionbarex.common.ActionBarCommon; +import per.goweii.actionbarex.common.OnActionBarChildClickListener; import per.goweii.basic.core.base.BaseActivity; -import per.goweii.basic.core.mvp.MvpPresenter; import per.goweii.basic.core.utils.SmartRefreshUtils; +import per.goweii.basic.ui.dialog.TipDialog; import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.CopyUtils; import per.goweii.basic.utils.IntentUtils; +import per.goweii.basic.utils.listener.SimpleCallback; +import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.event.SettingChangeEvent; -import per.goweii.wanandroid.module.main.activity.WebActivity; +import per.goweii.wanandroid.databinding.ActivityReadLaterBinding; +import per.goweii.wanandroid.db.model.ReadLaterModel; +import per.goweii.wanandroid.event.ReadLaterEvent; import per.goweii.wanandroid.module.mine.adapter.ReadLaterAdapter; -import per.goweii.wanandroid.module.mine.model.ReadLaterEntity; +import per.goweii.wanandroid.module.mine.presenter.ReadLaterPresenter; +import per.goweii.wanandroid.module.mine.view.ReadLaterView; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RealmHelper; -import per.goweii.wanandroid.utils.RvAnimUtils; -import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.RvConfigUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class ReadLaterActivity extends BaseActivity { +public class ReadLaterActivity extends BaseActivity implements ReadLaterView { + @BindView(R.id.abc) + ActionBarCommon abc; @BindView(R.id.msv) MultiStateView msv; @BindView(R.id.srl) @@ -53,9 +61,8 @@ public class ReadLaterActivity extends BaseActivity { private SmartRefreshUtils mSmartRefreshUtils; private ReadLaterAdapter mAdapter; + private int offset = 0; private int perPageCount = 20; - private int currPage = 0; - private RealmHelper mRealmHelper; public static void start(Context context) { Intent intent = new Intent(context, ReadLaterActivity.class); @@ -63,13 +70,12 @@ public static void start(Context context) { } @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { + public void onReadLaterEvent(ReadLaterEvent event) { if (isDestroyed()) { return; } - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); - } + offset = 0; + presenter.getList(0, perPageCount); } @Override @@ -77,36 +83,50 @@ protected boolean isRegisterEventBus() { return true; } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_read_later; + protected ActivityReadLaterBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityReadLaterBinding.inflate(inflater); } @Nullable @Override - protected MvpPresenter initPresenter() { - return null; + protected ReadLaterPresenter initPresenter() { + return new ReadLaterPresenter(); } @Override protected void initView() { + abc.setOnRightTextClickListener(new OnActionBarChildClickListener() { + @Override + public void onClick(View v) { + TipDialog.with(getContext()) + .message(R.string.are_you_sure_you_want_to_delete_them_all) + .onYes(new SimpleCallback() { + @Override + public void onResult(Void data) { + presenter.removeAll(); + } + }) + .show(); + } + }); mSmartRefreshUtils = SmartRefreshUtils.with(srl); mSmartRefreshUtils.pureScrollMode(); mSmartRefreshUtils.setRefreshListener(new SmartRefreshUtils.RefreshListener() { @Override public void onRefresh() { - currPage = 0; + offset = 0; getPageList(); } }); rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new ReadLaterAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); + RvConfigUtils.init(mAdapter); mAdapter.setEnableLoadMore(false); mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override public void onLoadMoreRequested() { - currPage++; getPageList(); } }, rv); @@ -114,7 +134,7 @@ public void onLoadMoreRequested() { @Override public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) { mAdapter.closeAll(null); - ReadLaterEntity item = mAdapter.getItem(position); + ReadLaterModel item = mAdapter.getItem(position); if (item == null) { return; } @@ -122,15 +142,18 @@ public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) default: break; case R.id.rl_top: - WebActivity.start(getContext(), item.getTitle(), item.getLink()); + UrlOpenUtils.Companion + .with(item.getLink()) + .title(item.getTitle()) + .open(getContext()); break; case R.id.tv_copy: CopyUtils.copyText(item.getLink()); - ToastMaker.showShort("复制成功"); + ToastMaker.showShort(getString(R.string.copied)); break; case R.id.tv_open: if (TextUtils.isEmpty(item.getLink())) { - ToastMaker.showShort("链接为空"); + ToastMaker.showShort(getString(R.string.the_link_is_empty)); break; } if (getContext() != null) { @@ -138,52 +161,101 @@ public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) } break; case R.id.tv_delete: - mRealmHelper.delete(item.getLink()); - mAdapter.remove(position); - if (mAdapter.getData().isEmpty()) { - MultiStateUtils.toEmpty(msv); - } else { - MultiStateUtils.toContent(msv); + ReadLaterModel model = mAdapter.getItem(position); + if (model != null) { + presenter.remove(model.getLink()); } break; } } }); rv.setAdapter(mAdapter); - - mRealmHelper = RealmHelper.create(); + MultiStateUtils.setEmptyAndErrorClick(msv, new SimpleListener() { + @Override + public void onResult() { + MultiStateUtils.toLoading(msv); + offset = 0; + getPageList(); + } + }); } @Override protected void loadData() { MultiStateUtils.toLoading(msv); - currPage = 0; + offset = 0; getPageList(); } @Override protected void onDestroy() { - if (mRealmHelper != null) { - mRealmHelper.destroy(); - } super.onDestroy(); } public void getPageList() { - List list = mRealmHelper.get(currPage, perPageCount); - if (currPage == 0) { + presenter.getList(offset, perPageCount); + } + + @Override + public void getReadLaterListSuccess(List list) { + mSmartRefreshUtils.success(); + if (offset == 0) { mAdapter.setNewData(list); if (list.isEmpty()) { - MultiStateUtils.toEmpty(msv); + MultiStateUtils.toEmpty(msv, true); } else { MultiStateUtils.toContent(msv); } } else { mAdapter.addData(list); + mAdapter.loadMoreComplete(); } + offset = mAdapter.getData().size(); if (list.size() < perPageCount) { mAdapter.loadMoreEnd(); } - mSmartRefreshUtils.success(); + } + + @Override + public void getReadLaterListFailed() { + mSmartRefreshUtils.fail(); + if (offset == 0) { + MultiStateUtils.toError(msv); + } else { + mAdapter.loadMoreFail(); + } + } + + @Override + public void removeReadLaterSuccess(String link) { + List list = mAdapter.getData(); + for (int i = 0; i < list.size(); i++) { + ReadLaterModel model = list.get(i); + if (TextUtils.equals(model.getLink(), link)) { + mAdapter.remove(i); + break; + } + } + offset = mAdapter.getData().size(); + if (mAdapter.getData().isEmpty()) { + MultiStateUtils.toEmpty(msv, true); + } + } + + @Override + public void removeReadLaterFailed() { + ToastMaker.showShort(getString(R.string.deletion_failed)); + } + + @Override + public void removeAllReadLaterSuccess() { + mAdapter.setNewData(null); + offset = mAdapter.getData().size(); + MultiStateUtils.toEmpty(msv, true); + } + + @Override + public void removeAllReadLaterFailed() { + ToastMaker.showShort(getString(R.string.deletion_failed)); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/ReadRecordActivity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/ReadRecordActivity.java new file mode 100644 index 00000000..f5fbf079 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/ReadRecordActivity.java @@ -0,0 +1,296 @@ +package per.goweii.wanandroid.module.mine.activity; + +import android.content.Context; +import android.content.Intent; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.kennyc.view.MultiStateView; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; + +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.util.List; + +import butterknife.BindView; +import per.goweii.actionbarex.common.ActionBarCommon; +import per.goweii.actionbarex.common.OnActionBarChildClickListener; +import per.goweii.basic.core.base.BaseActivity; +import per.goweii.basic.core.utils.SmartRefreshUtils; +import per.goweii.basic.ui.dialog.TipDialog; +import per.goweii.basic.ui.toast.ToastMaker; +import per.goweii.basic.utils.CopyUtils; +import per.goweii.basic.utils.IntentUtils; +import per.goweii.basic.utils.listener.SimpleCallback; +import per.goweii.basic.utils.listener.SimpleListener; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.ActivityReadRecordBinding; +import per.goweii.wanandroid.db.model.ReadRecordModel; +import per.goweii.wanandroid.event.ReadRecordAddedEvent; +import per.goweii.wanandroid.event.ReadRecordUpdateEvent; +import per.goweii.wanandroid.module.mine.adapter.ReadRecordAdapter; +import per.goweii.wanandroid.module.mine.presenter.ReadRecordPresenter; +import per.goweii.wanandroid.module.mine.view.ReadRecordView; +import per.goweii.wanandroid.utils.MultiStateUtils; +import per.goweii.wanandroid.utils.RvConfigUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +public class ReadRecordActivity extends BaseActivity implements ReadRecordView { + + @BindView(R.id.abc) + ActionBarCommon abc; + @BindView(R.id.msv) + MultiStateView msv; + @BindView(R.id.srl) + SmartRefreshLayout srl; + @BindView(R.id.rv) + RecyclerView rv; + + private SmartRefreshUtils mSmartRefreshUtils; + private ReadRecordAdapter mAdapter; + + private int offset = 0; + private int perPageCount = 20; + + private boolean loading = false; + + public static void start(Context context) { + Intent intent = new Intent(context, ReadRecordActivity.class); + context.startActivity(intent); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onReadRecordEvent(ReadRecordAddedEvent event) { + if (isDestroyed()) { + return; + } + if (offset == 0 && loading) { + return; + } + offset = 0; + presenter.getList(offset, perPageCount); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onReadRecordEvent(ReadRecordUpdateEvent event) { + if (isDestroyed()) { + return; + } + if (offset == 0 && loading) { + return; + } + List data = mAdapter.getData(); + for (int i = 0; i < data.size(); i++) { + ReadRecordModel oldModel = data.get(i); + if (TextUtils.equals(oldModel.getLink(), event.getLink())) { + data.remove(i); + data.add(i, new ReadRecordModel( + oldModel.getLink(), + !TextUtils.isEmpty(event.getTitle()) ? event.getTitle() : oldModel.getTitle(), + oldModel.getTime(), + event.getTime() != null ? event.getTime() : oldModel.getLastTime(), + event.getPercent() != null ? event.getPercent() : oldModel.getPercent() + )); + mAdapter.notifyItemChanged(i); + return; + } + } + } + + @Override + protected boolean isRegisterEventBus() { + return true; + } + + @Nullable + @Override + protected ActivityReadRecordBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivityReadRecordBinding.inflate(inflater); + } + + @Nullable + @Override + protected ReadRecordPresenter initPresenter() { + return new ReadRecordPresenter(); + } + + @Override + protected void initView() { + abc.setOnRightTextClickListener(new OnActionBarChildClickListener() { + @Override + public void onClick(View v) { + TipDialog.with(getContext()) + .message(getString(R.string.are_you_sure_you_want_to_delete_them_all)) + .onYes(new SimpleCallback() { + @Override + public void onResult(Void data) { + presenter.removeAll(); + } + }) + .show(); + } + }); + mSmartRefreshUtils = SmartRefreshUtils.with(srl); + mSmartRefreshUtils.pureScrollMode(); + mSmartRefreshUtils.setRefreshListener(new SmartRefreshUtils.RefreshListener() { + @Override + public void onRefresh() { + offset = 0; + getPageList(); + } + }); + rv.setLayoutManager(new LinearLayoutManager(getContext())); + mAdapter = new ReadRecordAdapter(); + RvConfigUtils.init(mAdapter); + mAdapter.setEnableLoadMore(false); + mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { + @Override + public void onLoadMoreRequested() { + getPageList(); + } + }, rv); + mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() { + @Override + public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) { + mAdapter.closeAll(null); + ReadRecordModel item = mAdapter.getItem(position); + if (item == null) { + return; + } + switch (view.getId()) { + default: + break; + case R.id.rl_top: + UrlOpenUtils.Companion + .with(item.getLink()) + .title(item.getTitle()) + .open(getContext()); + break; + case R.id.tv_copy: + CopyUtils.copyText(item.getLink()); + ToastMaker.showShort(getString(R.string.copied)); + break; + case R.id.tv_open: + if (TextUtils.isEmpty(item.getLink())) { + ToastMaker.showShort(getString(R.string.the_link_is_empty)); + break; + } + if (getContext() != null) { + IntentUtils.openBrowser(getContext(), item.getLink()); + } + break; + case R.id.tv_delete: + ReadRecordModel model = mAdapter.getItem(position); + if (model != null) { + presenter.remove(model.getLink()); + } + break; + } + } + }); + rv.setAdapter(mAdapter); + MultiStateUtils.setEmptyAndErrorClick(msv, new SimpleListener() { + @Override + public void onResult() { + MultiStateUtils.toLoading(msv); + offset = 0; + getPageList(); + } + }); + } + + @Override + protected void loadData() { + MultiStateUtils.toLoading(msv); + offset = 0; + getPageList(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + } + + public void getPageList() { + loading = true; + presenter.getList(offset, perPageCount); + } + + @Override + public void getReadRecordListSuccess(List list) { + mSmartRefreshUtils.success(); + if (offset == 0) { + mAdapter.setNewData(list); + if (list.isEmpty()) { + MultiStateUtils.toEmpty(msv, true); + } else { + MultiStateUtils.toContent(msv); + } + } else { + mAdapter.addData(list); + mAdapter.loadMoreComplete(); + } + offset = mAdapter.getData().size(); + if (list.size() < perPageCount) { + mAdapter.loadMoreEnd(); + } + loading = false; + } + + @Override + public void getReadRecordListFailed() { + mSmartRefreshUtils.fail(); + if (offset == 0) { + MultiStateUtils.toError(msv); + } else { + mAdapter.loadMoreFail(); + } + loading = false; + } + + @Override + public void removeReadRecordSuccess(String link) { + List list = mAdapter.getData(); + for (int i = 0; i < list.size(); i++) { + ReadRecordModel model = list.get(i); + if (TextUtils.equals(model.getLink(), link)) { + mAdapter.remove(i); + break; + } + } + offset = mAdapter.getData().size(); + if (mAdapter.getData().isEmpty()) { + MultiStateUtils.toEmpty(msv, true); + } + } + + @Override + public void removeReadRecordFailed() { + ToastMaker.showShort(getString(R.string.deletion_failed)); + } + + @Override + public void removeAllReadRecordSuccess() { + mAdapter.setNewData(null); + offset = mAdapter.getData().size(); + MultiStateUtils.toEmpty(msv, true); + } + + @Override + public void removeAllReadRecordFailed() { + ToastMaker.showShort(getString(R.string.deletion_failed)); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/SettingActivity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/SettingActivity.java index 8ac7a25e..49178c6a 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/SettingActivity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/SettingActivity.java @@ -1,91 +1,85 @@ package per.goweii.wanandroid.module.mine.activity; -import android.Manifest; import android.content.Context; import android.content.Intent; -import android.support.annotation.Nullable; -import android.support.v7.widget.SwitchCompat; +import android.view.LayoutInflater; import android.view.View; import android.widget.CompoundButton; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatDelegate; +import androidx.appcompat.widget.SwitchCompat; +import androidx.core.os.LocaleListCompat; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + import butterknife.BindView; import butterknife.OnClick; import butterknife.OnLongClick; -import per.goweii.anypermission.RequestListener; -import per.goweii.anypermission.RuntimeRequester; import per.goweii.basic.core.base.BaseActivity; -import per.goweii.basic.core.permission.PermissionUtils; import per.goweii.basic.ui.dialog.ListDialog; import per.goweii.basic.ui.dialog.TipDialog; -import per.goweii.basic.ui.dialog.UpdateDialog; import per.goweii.basic.ui.toast.ToastMaker; -import per.goweii.basic.utils.AppInfoUtils; -import per.goweii.basic.utils.ResUtils; import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.rxhttp.request.base.BaseBean; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.common.WanApp; +import per.goweii.wanandroid.common.Constant; +import per.goweii.wanandroid.databinding.ActivitySettingBinding; import per.goweii.wanandroid.event.LoginEvent; import per.goweii.wanandroid.event.SettingChangeEvent; -import per.goweii.wanandroid.module.main.dialog.DownloadDialog; -import per.goweii.wanandroid.module.main.model.UpdateBean; import per.goweii.wanandroid.module.mine.presenter.SettingPresenter; import per.goweii.wanandroid.module.mine.view.SettingView; -import per.goweii.wanandroid.utils.HostInterceptUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; +import per.goweii.wanandroid.utils.DarkModeUtils; import per.goweii.wanandroid.utils.SettingUtils; -import per.goweii.wanandroid.utils.UpdateUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; import per.goweii.wanandroid.utils.UserUtils; +import per.goweii.wanandroid.utils.ai.AiModel; +import per.goweii.wanandroid.utils.ai.AiProvider; +import per.goweii.wanandroid.utils.recreate_anim.RecreateAnimation; +import per.goweii.wanandroid.utils.web.HostInterceptUtils; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class SettingActivity extends BaseActivity implements SettingView { - - private static final int REQ_CODE_PERMISSION = 1; - - @BindView(R.id.sc_dark_theme) - SwitchCompat sc_dark_theme; - @BindView(R.id.sc_show_read_later) - SwitchCompat sc_show_read_later; +public class SettingActivity extends BaseActivity implements SettingView { + @BindView(R.id.tv_theme_mode) + TextView tv_theme_mode; + @BindView(R.id.tv_show_read_later_notification_title) + TextView tv_show_read_later_notification_title; + @BindView(R.id.tv_show_read_later_notification_desc) + TextView tv_show_read_later_notification_desc; + @BindView(R.id.sc_show_read_later_notification) + SwitchCompat sc_show_read_later_notification; @BindView(R.id.sc_show_top) SwitchCompat sc_show_top; @BindView(R.id.sc_show_banner) SwitchCompat sc_show_banner; - @BindView(R.id.sc_hide_about_me) - SwitchCompat sc_hide_about_me; - @BindView(R.id.sc_hide_open) - SwitchCompat sc_hide_open; - @BindView(R.id.sc_web_swipeback_edge) - SwitchCompat sc_web_swipeback_edge; + @BindView(R.id.sc_show_qa) + SwitchCompat sc_show_qa; @BindView(R.id.tv_intercept_host) TextView tv_intercept_host; - @BindView(R.id.tv_rv_anim) - TextView tv_rv_anim; @BindView(R.id.tv_cache) TextView tv_cache; - @BindView(R.id.tv_has_update) - TextView tv_has_update; @BindView(R.id.tv_curr_version) TextView tv_curr_version; @BindView(R.id.ll_logout) LinearLayout ll_logout; + @BindView(R.id.tv_ai) + TextView tv_ai; - private RuntimeRequester mRuntimeRequester; - private UpdateUtils mUpdateUtils; - private boolean mDarkTheme; - private boolean mShowTop; + private boolean mShowQA; private boolean mShowBanner; - private boolean mShowReadLater; - private boolean mHideAboutMe; - private boolean mHideOpen; - private int mRvAnim; + private boolean mShowTop; + private boolean mShowReadLaterNotification; private int mUrlIntercept; public static void start(Context context) { @@ -93,9 +87,10 @@ public static void start(Context context) { context.startActivity(intent); } + @Nullable @Override - protected int getLayoutId() { - return R.layout.activity_setting; + protected ActivitySettingBinding initViewBinding(@NonNull LayoutInflater inflater) { + return ActivitySettingBinding.inflate(inflater); } @Nullable @@ -106,36 +101,22 @@ protected SettingPresenter initPresenter() { @Override protected void initView() { - tv_has_update.setText(""); - tv_curr_version.setText("当前版本" + AppInfoUtils.getVersionName()); - mDarkTheme = SettingUtils.getInstance().isDarkTheme(); - sc_dark_theme.setChecked(mDarkTheme); + updateLanguage(); + updateThemeModeUI(); + mShowQA = SettingUtils.getInstance().isShowQA(); + sc_show_qa.setChecked(mShowQA); mShowTop = SettingUtils.getInstance().isShowTop(); sc_show_top.setChecked(mShowTop); mShowBanner = SettingUtils.getInstance().isShowBanner(); sc_show_banner.setChecked(mShowBanner); - mShowReadLater = SettingUtils.getInstance().isShowReadLater(); - sc_show_read_later.setChecked(mShowReadLater); - mHideAboutMe = SettingUtils.getInstance().isHideAboutMe(); - sc_hide_about_me.setChecked(mHideAboutMe); - mHideOpen = SettingUtils.getInstance().isHideOpen(); - sc_hide_open.setChecked(mHideOpen); - sc_web_swipeback_edge.setChecked(SettingUtils.getInstance().isWebSwipeBackEdge()); - mRvAnim = SettingUtils.getInstance().getRvAnim(); - tv_rv_anim.setText(RvAnimUtils.getName(mRvAnim)); + mShowReadLaterNotification = SettingUtils.getInstance().isShowReadLaterNotification(); + sc_show_read_later_notification.setChecked(mShowReadLaterNotification); mUrlIntercept = SettingUtils.getInstance().getUrlInterceptType(); tv_intercept_host.setText(HostInterceptUtils.getName(mUrlIntercept)); - sc_dark_theme.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + sc_show_qa.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton v, boolean isChecked) { - SettingUtils.getInstance().setDarkTheme(isChecked); - WanApp.setDarkModeStatus(); - v.postDelayed(new Runnable() { - @Override - public void run() { - WanApp.recreate(); - } - }, 300); + SettingUtils.getInstance().setShowQA(isChecked); } }); sc_show_top.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @@ -150,28 +131,10 @@ public void onCheckedChanged(CompoundButton v, boolean isChecked) { SettingUtils.getInstance().setShowBanner(isChecked); } }); - sc_show_read_later.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton v, boolean isChecked) { - SettingUtils.getInstance().setShowReadLater(isChecked); - } - }); - sc_hide_about_me.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton v, boolean isChecked) { - SettingUtils.getInstance().setHideAboutMe(isChecked); - } - }); - sc_hide_open.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + sc_show_read_later_notification.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton v, boolean isChecked) { - SettingUtils.getInstance().setHideOpen(isChecked); - } - }); - sc_web_swipeback_edge.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(CompoundButton v, boolean isChecked) { - SettingUtils.getInstance().setWebSwipeBackEdge(isChecked); + SettingUtils.getInstance().setShowReadLaterNotification(isChecked); } }); if (UserUtils.getInstance().isLogin()) { @@ -181,12 +144,50 @@ public void onCheckedChanged(CompoundButton v, boolean isChecked) { } } + private void updateLanguage() { + LocaleListCompat applicationLocales = AppCompatDelegate.getApplicationLocales(); + if (!applicationLocales.isEmpty()) { + Locale locale = applicationLocales.get(0); + Objects.requireNonNull(locale); + String displayName = locale.getDisplayLanguage(); + binding.tvLanguage.setText(displayName); + } else { + binding.tvLanguage.setText(R.string.follow_the_system); + } + } + + private void updateThemeModeUI() { + final SettingUtils.ThemeMode themeMode = SettingUtils.getInstance().getThemeMode(); + switch (themeMode) { + case FOLLOW_SYSTEM: + tv_theme_mode.setText(getString(R.string.follow_the_system)); + break; + case LIGHT: + tv_theme_mode.setText(getString(R.string.light_mode)); + break; + case DARK: + tv_theme_mode.setText(getString(R.string.dark_mode)); + break; + } + } + @Override protected void loadData() { - presenter.update(false); presenter.getCacheSize(); } + @Override + protected void onResume() { + super.onResume(); + if (SettingUtils.getInstance().isAiEnabled()) { + AiProvider aiProvider = AiProvider.getProvider(SettingUtils.getInstance().getAiProvider()); + AiModel aiModel = aiProvider.getModel(SettingUtils.getInstance().getAiModel()); + tv_ai.setText(String.format("%s (%s)", aiModel.getName(), aiProvider.getName())); + } else { + tv_ai.setText(R.string.disabled); + } + } + @Override protected void onStop() { super.onStop(); @@ -199,23 +200,15 @@ protected void onDestroy() { } private void postSettingChangedEvent() { + boolean showQAChanged = mShowQA != SettingUtils.getInstance().isShowQA(); boolean showTopChanged = mShowTop != SettingUtils.getInstance().isShowTop(); boolean showBannerChanged = mShowBanner != SettingUtils.getInstance().isShowBanner(); - boolean showReadLaterChanged = mShowReadLater != SettingUtils.getInstance().isShowReadLater(); - boolean hideAboutMeChanged = mHideAboutMe != SettingUtils.getInstance().isHideAboutMe(); - boolean hideOpenChanged = mHideOpen != SettingUtils.getInstance().isHideOpen(); - boolean rvAnimChanged = mRvAnim != SettingUtils.getInstance().getRvAnim(); boolean urlInterceptChanged = mUrlIntercept != SettingUtils.getInstance().getUrlInterceptType(); - if (showReadLaterChanged || showTopChanged || showBannerChanged || - hideAboutMeChanged || hideOpenChanged || rvAnimChanged || - urlInterceptChanged) { + if (showQAChanged || showTopChanged || showBannerChanged || urlInterceptChanged) { SettingChangeEvent event = new SettingChangeEvent(); + event.setShowQAChanged(showQAChanged); event.setShowTopChanged(showTopChanged); event.setShowBannerChanged(showBannerChanged); - event.setShowReadLaterChanged(showReadLaterChanged); - event.setHideAboutMeChanged(hideAboutMeChanged); - event.setHideOpenChanged(hideOpenChanged); - event.setRvAnimChanged(rvAnimChanged); event.post(); } } @@ -234,8 +227,10 @@ public boolean onLongClick(View v) { @OnClick({ - R.id.rl_intercept_host, R.id.ll_rv_anim, R.id.ll_update, - R.id.ll_cache, R.id.ll_about, R.id.ll_logout + R.id.ll_language, + R.id.rl_intercept_host, R.id.ll_theme_mode, + R.id.ll_ai, R.id.ll_cache, R.id.ll_about, + R.id.ll_privacy_policy, R.id.ll_logout }) @Override public void onClick(View v) { @@ -245,51 +240,84 @@ public void onClick(View v) { @Override protected void onClick2(View v) { switch (v.getId()) { - default: + case R.id.ll_language: + LanguageActivity.start(getContext()); break; - case R.id.rl_intercept_host: + case R.id.ll_theme_mode: + final List themeModes = Arrays.asList(SettingUtils.ThemeMode.values()); + final List nameList = new ArrayList<>(themeModes.size()); + for (SettingUtils.ThemeMode themeMode : themeModes) { + switch (themeMode) { + case FOLLOW_SYSTEM: + nameList.add(getString(R.string.follow_the_system)); + break; + case LIGHT: + nameList.add(getString(R.string.light_mode)); + break; + case DARK: + nameList.add(getString(R.string.dark_mode)); + break; + } + } + final SettingUtils.ThemeMode selectedThemeMode = SettingUtils.getInstance().getThemeMode(); + final int selectedPos = themeModes.indexOf(selectedThemeMode); + ListDialog.with(getContext()) .cancelable(true) -// .title("网页拦截") - .datas(HostInterceptUtils.getName(HostInterceptUtils.TYPE_NOTHING), - HostInterceptUtils.getName(HostInterceptUtils.TYPE_ONLY_WHITE), - HostInterceptUtils.getName(HostInterceptUtils.TYPE_INTERCEPT_BLACK)) - .currSelectPos(SettingUtils.getInstance().getUrlInterceptType()) - .listener(new ListDialog.OnItemSelectedListener() { + .title(getString(R.string.theme_mode)) + .datas(nameList) + .currSelectPos(selectedPos) + .onItemSelectedListener(new ListDialog.OnItemSelectedListener() { @Override public void onSelect(String data, int pos) { - tv_intercept_host.setText(HostInterceptUtils.getName(pos)); - SettingUtils.getInstance().setUrlInterceptType(pos); + if (selectedPos == pos) { + return; + } + SettingUtils.ThemeMode themeMode = themeModes.get(pos); + SettingUtils.getInstance().setThemeMode(themeMode); + } + }) + .onDismissListener(new ListDialog.OnDismissListener() { + @Override + public void onPreDismiss(String data, int pos) { + } + + @Override + public void onPostDismiss(String data, int pos) { + if (selectedThemeMode == SettingUtils.getInstance().getThemeMode()) { + return; + } + new RecreateAnimation(SettingActivity.this, new Runnable() { + @Override + public void run() { + DarkModeUtils.initDarkMode(SettingActivity.this); + } + } + ).start(); } }) .show(); break; - case R.id.ll_rv_anim: + case R.id.rl_intercept_host: ListDialog.with(getContext()) .cancelable(true) -// .title("列表动画") - .datas(RvAnimUtils.getName(RvAnimUtils.RvAnim.NONE), - RvAnimUtils.getName(RvAnimUtils.RvAnim.ALPHAIN), - RvAnimUtils.getName(RvAnimUtils.RvAnim.SCALEIN), - RvAnimUtils.getName(RvAnimUtils.RvAnim.SLIDEIN_BOTTOM), - RvAnimUtils.getName(RvAnimUtils.RvAnim.SLIDEIN_LEFT), - RvAnimUtils.getName(RvAnimUtils.RvAnim.SLIDEIN_RIGHT)) - .currSelectPos(SettingUtils.getInstance().getRvAnim()) - .listener(new ListDialog.OnItemSelectedListener() { + .title(getString(R.string.web_blocking)) + .datas(HostInterceptUtils.getName(HostInterceptUtils.TYPE_NOTHING), + HostInterceptUtils.getName(HostInterceptUtils.TYPE_ONLY_WHITE), + HostInterceptUtils.getName(HostInterceptUtils.TYPE_INTERCEPT_BLACK)) + .currSelectPos(SettingUtils.getInstance().getUrlInterceptType()) + .onItemSelectedListener(new ListDialog.OnItemSelectedListener() { @Override public void onSelect(String data, int pos) { - tv_rv_anim.setText(RvAnimUtils.getName(pos)); - SettingUtils.getInstance().setRvAnim(pos); + tv_intercept_host.setText(HostInterceptUtils.getName(pos)); + SettingUtils.getInstance().setUrlInterceptType(pos); } }) .show(); break; - case R.id.ll_update: - presenter.update(true); - break; case R.id.ll_cache: TipDialog.with(getContext()) - .message("确定要清除缓存吗?") + .message(R.string.are_you_sure_you_want_to_clear_your_cache) .onYes(new SimpleCallback() { @Override public void onResult(Void data) { @@ -298,12 +326,20 @@ public void onResult(Void data) { }) .show(); break; + case R.id.ll_ai: + AiSettingActivity.start(getContext()); + break; case R.id.ll_about: AboutActivity.start(getContext()); break; + case R.id.ll_privacy_policy: + UrlOpenUtils.Companion + .with(Constant.PRIVACY_POLICY_URL) + .open(getContext()); + break; case R.id.ll_logout: TipDialog.with(getContext()) - .message("确定要退出登录吗?") + .message(R.string.are_you_sure_you_want_to_log_out) .onYes(new SimpleCallback() { @Override public void onResult(Void data) { @@ -312,54 +348,11 @@ public void onResult(Void data) { }) .show(); break; + default: + break; } } - @Override - public void updateSuccess(int code, UpdateBean data, boolean click) { - if (mUpdateUtils == null) { - mUpdateUtils = UpdateUtils.newInstance(); - } - boolean isNewest = mUpdateUtils.isNewest(data.getVersion_code()); - if (isNewest) { - tv_has_update.setTextColor(ResUtils.getColor(getContext(), R.color.text_main)); - tv_has_update.setText("发现新版本"); - } else { - tv_has_update.setTextColor(ResUtils.getColor(getContext(), R.color.text_third)); - tv_has_update.setText("已是最新版"); - } - if (!click) { - return; - } - if (!isNewest) { - ToastMaker.showShort("已是最新版"); - return; - } - UpdateDialog.with(getContext()) - .setUrl(data.getUrl()) - .setUrlBackup(data.getUrl_backup()) - .setVersionCode(data.getVersion_code()) - .setVersionName(data.getVersion_name()) - .setForce(data.isForce()) - .setDescription(data.getDesc()) - .setTime(data.getTime()) - .setOnUpdateListener(new UpdateDialog.OnUpdateListener() { - @Override - public void onDownload(String url, String urlBackup, boolean isForce) { - download(data.getVersion_name(), url, urlBackup, isForce); - } - - @Override - public void onIgnore(int versionCode) { - } - }) - .show(); - } - - @Override - public void updateFailed(int code, String msg, boolean click) { - } - @Override public void logoutSuccess(int code, BaseBean data) { UserUtils.getInstance().logout(); @@ -376,25 +369,4 @@ public void logoutFailed(int code, String msg) { public void getCacheSizeSuccess(String size) { tv_cache.setText(size); } - - @Override - protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { - super.onActivityResult(requestCode, resultCode, data); - if (mRuntimeRequester != null) { - mRuntimeRequester.onActivityResult(requestCode); - } - } - - private void download(final String versionName, final String url, final String urlBackup, final boolean isForce) { - mRuntimeRequester = PermissionUtils.request(new RequestListener() { - @Override - public void onSuccess() { - DownloadDialog.with(getActivity(), isForce, url, urlBackup, versionName); - } - - @Override - public void onFailed() { - } - }, getContext(), REQ_CODE_PERMISSION, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE); - } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/activity/UserInfoActivity.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/UserInfoActivity.kt new file mode 100644 index 00000000..b1ba9571 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/activity/UserInfoActivity.kt @@ -0,0 +1,54 @@ +package per.goweii.wanandroid.module.mine.activity + +import android.content.Context +import android.content.Intent +import android.view.LayoutInflater +import per.goweii.basic.core.base.BaseActivity +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.ActivityUserInfoBinding +import per.goweii.wanandroid.event.UserInfoUpdateEvent +import per.goweii.wanandroid.module.login.activity.AuthActivity +import per.goweii.wanandroid.module.login.model.UserEntity +import per.goweii.wanandroid.module.mine.contract.UserInfoPresenter +import per.goweii.wanandroid.module.mine.contract.UserInfoView +import per.goweii.wanandroid.utils.ImageLoader + +/** + * @author CuiZhen + * @date 2020/5/27 + */ +class UserInfoActivity : BaseActivity(), UserInfoView { + + companion object { + @JvmStatic + fun start(context: Context) { + context.startActivity(Intent(context, UserInfoActivity::class.java)) + } + } + + override fun initViewBinding(inflater: LayoutInflater): ActivityUserInfoBinding { + return ActivityUserInfoBinding.inflate(inflater) + } + + override fun initPresenter(): UserInfoPresenter = UserInfoPresenter() + + override fun initView() { + } + + override fun loadData() { + presenter.mineInfo() + } + + override fun gotoLogin() { + AuthActivity.startQuickLogin(context) + finish() + } + + override fun mineInfoSuccess(userEntity: UserEntity) { + UserInfoUpdateEvent().post() + ImageLoader.userIcon(binding.civUserIcon, userEntity.avatar ?: "") + ImageLoader.userBlur(binding.ivBlur, userEntity.cover ?: "") + binding.tvUserName.text = userEntity.username + binding.tvUserId.text = userEntity.wanid.toString() + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/CoinRankAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/CoinRankAdapter.java index 95d9761f..1da5bd7e 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/CoinRankAdapter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/CoinRankAdapter.java @@ -1,31 +1,34 @@ package per.goweii.wanandroid.module.mine.adapter; +import android.animation.Animator; import android.animation.ValueAnimator; -import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; import android.util.TypedValue; import android.view.animation.DecelerateInterpolator; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import java.util.List; +import per.goweii.basic.utils.ResUtils; import per.goweii.wanandroid.R; import per.goweii.wanandroid.module.main.model.CoinInfoBean; /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CoinRankAdapter extends BaseQuickAdapter { + private final int mScale = 1000; private int mMax = 0; public CoinRankAdapter() { @@ -43,7 +46,14 @@ public void setNewData(@Nullable List data) { @Override protected void convert(BaseViewHolder helper, CoinInfoBean item) { ProgressBar pb = helper.getView(R.id.pb); - doProgressAnim(pb, item.getCoinCount()); + pb.setMax(mMax * mScale); + cancelProgressAnim(pb); + if (!item.anim) { + item.anim = true; + doProgressAnim(pb, item.getCoinCount()); + } else { + pb.setProgress(item.getCoinCount() * mScale); + } int index = helper.getAdapterPosition() + 1; helper.setText(R.id.tv_index, "" + index); helper.setText(R.id.tv_index, "" + index); @@ -53,35 +63,43 @@ protected void convert(BaseViewHolder helper, CoinInfoBean item) { TextView tv_index = helper.getView(R.id.tv_index); if (index == 1) { iv_index.setImageResource(R.drawable.ic_rank_1); - tv_index.setTextColor(ContextCompat.getColor(tv_index.getContext(), R.color.text_surface_alpha)); + tv_index.setTextColor(Color.parseColor("#ffca28")); tv_index.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv_index.getContext().getResources().getDimension(R.dimen.text_auxiliary)); } else if (index == 2) { iv_index.setImageResource(R.drawable.ic_rank_2); - tv_index.setTextColor(ContextCompat.getColor(tv_index.getContext(), R.color.text_surface_alpha)); + tv_index.setTextColor(Color.parseColor("#cdcdcd")); tv_index.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv_index.getContext().getResources().getDimension(R.dimen.text_auxiliary)); } else if (index == 3) { iv_index.setImageResource(R.drawable.ic_rank_3); - tv_index.setTextColor(ContextCompat.getColor(tv_index.getContext(), R.color.text_surface_alpha)); + tv_index.setTextColor(Color.parseColor("#d49682")); tv_index.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv_index.getContext().getResources().getDimension(R.dimen.text_auxiliary)); } else { - iv_index.setImageResource(R.color.transparent); - tv_index.setTextColor(ContextCompat.getColor(tv_index.getContext(), R.color.text_second)); + iv_index.setImageDrawable(new ColorDrawable(Color.TRANSPARENT)); + tv_index.setTextColor(ResUtils.getThemeColor(tv_index.getContext(), R.attr.colorTextSecond)); tv_index.setTextSize(TypedValue.COMPLEX_UNIT_PX, tv_index.getContext().getResources().getDimension(R.dimen.text_content)); } } private void doProgressAnim(final ProgressBar pb, int to) { - final int f = 1000; - pb.setMax(mMax * f); ValueAnimator animator = ValueAnimator.ofInt(0, to); animator.setDuration(1000); animator.setInterpolator(new DecelerateInterpolator()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { - pb.setProgress((int) animation.getAnimatedValue() * f); + pb.setProgress((int) animation.getAnimatedValue() * mScale); } }); + pb.setTag(animator); animator.start(); } + + private void cancelProgressAnim(final ProgressBar pb) { + Object obj = pb.getTag(); + if (obj instanceof Animator) { + Animator animator = (Animator) obj; + animator.cancel(); + } + pb.setTag(null); + } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/CoinRecordAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/CoinRecordAdapter.java index 0d90eb9b..86e475cb 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/CoinRecordAdapter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/CoinRecordAdapter.java @@ -9,8 +9,6 @@ /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CoinRecordAdapter extends BaseQuickAdapter { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/CollectionLinkAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/CollectionLinkAdapter.java index 179bdc1e..6cfcf7f7 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/CollectionLinkAdapter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/CollectionLinkAdapter.java @@ -1,9 +1,10 @@ package per.goweii.wanandroid.module.mine.adapter; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; import android.text.Html; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.daimajia.swipe.SwipeLayout; @@ -17,8 +18,6 @@ /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CollectionLinkAdapter extends BaseQuickAdapter { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/HostInterruptAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/HostInterruptAdapter.java index b98af517..99d33843 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/HostInterruptAdapter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/HostInterruptAdapter.java @@ -1,8 +1,9 @@ package per.goweii.wanandroid.module.mine.adapter; -import android.support.v7.widget.SwitchCompat; import android.widget.CompoundButton; +import androidx.appcompat.widget.SwitchCompat; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; @@ -12,8 +13,6 @@ /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class HostInterruptAdapter extends BaseQuickAdapter { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/LanguageAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/LanguageAdapter.java new file mode 100644 index 00000000..ab3b24bd --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/LanguageAdapter.java @@ -0,0 +1,45 @@ +package per.goweii.wanandroid.module.mine.adapter; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatDelegate; +import androidx.core.app.LocaleManagerCompat; +import androidx.core.os.LocaleListCompat; + +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; + +import java.util.Locale; + +import per.goweii.wanandroid.R; + +public class LanguageAdapter extends BaseQuickAdapter { + @Nullable + private final Locale mAppLocale; + @Nullable + private final Locale mSysLocale; + + public LanguageAdapter(@Nullable Locale appLocale, @Nullable Locale sysLocale) { + super(R.layout.rv_item_language); + this.mAppLocale = appLocale; + this.mSysLocale = sysLocale; + } + + @Override + protected void convert(@NonNull BaseViewHolder helper, @Nullable Locale item) { + if (item == null) { + helper.setText(R.id.tv_title, R.string.follow_the_system); + if (mSysLocale == null) { + helper.setVisible(R.id.tv_subtitle, false); + } else { + helper.setVisible(R.id.tv_subtitle, true); + helper.setText(R.id.tv_subtitle, mSysLocale.getDisplayLanguage(mSysLocale)); + } + helper.setVisible(R.id.iv_checked, mAppLocale == null); + } else { + helper.setText(R.id.tv_title, item.getDisplayLanguage()); + helper.setText(R.id.tv_subtitle, item.getDisplayLanguage(item)); + helper.setVisible(R.id.iv_checked, item.equals(mAppLocale)); + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/MessageReadedAdapter.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/MessageReadedAdapter.kt new file mode 100644 index 00000000..dfda617a --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/MessageReadedAdapter.kt @@ -0,0 +1,84 @@ +package per.goweii.wanandroid.module.mine.adapter + +import android.text.Html +import android.text.TextUtils +import androidx.recyclerview.widget.RecyclerView +import com.chad.library.adapter.base.BaseQuickAdapter +import com.chad.library.adapter.base.BaseViewHolder +import com.daimajia.swipe.SwipeLayout +import com.daimajia.swipe.SwipeLayout.SwipeListener +import per.goweii.basic.utils.StringUtils +import per.goweii.wanandroid.R +import per.goweii.wanandroid.module.mine.model.MessageBean +import java.util.* + +/** + * @author CuiZhen + * @date 2020/5/16 + */ +class MessageReadedAdapter : BaseQuickAdapter( + R.layout.rv_item_message_readed +) { + + private val mUnCloseList: ArrayList = ArrayList() + + override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + super.onAttachedToRecyclerView(recyclerView) + recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { + closeAll(null) + } + } + }) + } + + fun closeAll(layout: SwipeLayout?) { + for (swipeLayout in mUnCloseList) { + if (layout === swipeLayout) { + continue + } + if (swipeLayout.openStatus != SwipeLayout.Status.Open) { + continue + } + swipeLayout.close() + } + } + + override fun convert(helper: BaseViewHolder, item: MessageBean) { + val sl = helper.getView(R.id.sl) + sl.addSwipeListener(object : SwipeListener { + override fun onStartOpen(layout: SwipeLayout) { + closeAll(layout) + } + + override fun onOpen(layout: SwipeLayout) { + mUnCloseList.add(layout) + } + + override fun onStartClose(layout: SwipeLayout) {} + override fun onClose(layout: SwipeLayout) { + mUnCloseList.remove(layout) + } + + override fun onUpdate(layout: SwipeLayout, leftOffset: Int, topOffset: Int) {} + override fun onHandRelease(layout: SwipeLayout, xvel: Float, yvel: Float) {} + }) + helper.addOnClickListener(R.id.rl_message, R.id.tv_delete) + + if (TextUtils.isEmpty(item.tag)) { + helper.setGone(R.id.tv_tag, false) + } else { + helper.setGone(R.id.tv_tag, true) + helper.setText(R.id.tv_tag, item.tag) + } + helper.setText(R.id.tv_user, item.fromUser) + helper.setText(R.id.tv_data, item.niceDate) + helper.setText(R.id.tv_detail, item.title) + var content: String = Html.fromHtml(item.message).toString() + content = StringUtils.removeAllBank(content, 2) + helper.setGone(R.id.tv_content, content.isNotEmpty()) + helper.setText(R.id.tv_content, content) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/MessageUnreadAdapter.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/MessageUnreadAdapter.kt new file mode 100644 index 00000000..2a2d3c1a --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/MessageUnreadAdapter.kt @@ -0,0 +1,31 @@ +package per.goweii.wanandroid.module.mine.adapter + +import android.text.Html +import android.text.TextUtils +import com.chad.library.adapter.base.BaseQuickAdapter +import com.chad.library.adapter.base.BaseViewHolder +import per.goweii.basic.utils.StringUtils +import per.goweii.wanandroid.R +import per.goweii.wanandroid.module.mine.model.MessageBean + +/** + * @author CuiZhen + * @date 2020/5/16 + */ +class MessageUnreadAdapter : BaseQuickAdapter(R.layout.rv_item_message_unread) { + override fun convert(helper: BaseViewHolder, item: MessageBean) { + if (TextUtils.isEmpty(item.tag)) { + helper.setGone(R.id.tv_tag, false) + } else { + helper.setGone(R.id.tv_tag, true) + helper.setText(R.id.tv_tag, item.tag) + } + helper.setText(R.id.tv_user, item.fromUser) + helper.setText(R.id.tv_data, item.niceDate) + helper.setText(R.id.tv_detail, item.title) + var content: String = Html.fromHtml(item.message).toString() + content = StringUtils.removeAllBank(content, 2) + helper.setGone(R.id.tv_content, content.isNotEmpty()) + helper.setText(R.id.tv_content, content) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/MineShareArticleAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/MineShareArticleAdapter.java index 1a092c70..bd71e093 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/MineShareArticleAdapter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/MineShareArticleAdapter.java @@ -1,9 +1,8 @@ package per.goweii.wanandroid.module.mine.adapter; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; -import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.daimajia.swipe.SwipeLayout; @@ -17,18 +16,21 @@ /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class MineShareArticleAdapter extends BaseQuickAdapter { +public class MineShareArticleAdapter extends ArticleAdapter { private final List mUnCloseList = new ArrayList<>(); private ArticleAdapter.OnCollectListener mOnCollectListener = null; public MineShareArticleAdapter() { - super(R.layout.rv_item_mine_share_article); + super(); + } + + @Override + protected int getArticleLayoutId() { + return R.layout.rv_item_mine_share_article; } public void setOnCollectListener(ArticleAdapter.OnCollectListener onCollectListener) { @@ -48,7 +50,7 @@ public void closeAll(SwipeLayout layout) { } @Override - public void onAttachedToRecyclerView(RecyclerView recyclerView) { + public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override @@ -67,7 +69,7 @@ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { } @Override - protected void convert(BaseViewHolder helper, ArticleBean item) { + protected void convertArticle(@NonNull BaseViewHolder helper, ArticleBean item) { SwipeLayout sl = helper.getView(R.id.sl); sl.addSwipeListener(new SwipeLayout.SwipeListener() { @Override @@ -97,7 +99,7 @@ public void onUpdate(SwipeLayout layout, int leftOffset, int topOffset) { public void onHandRelease(SwipeLayout layout, float xvel, float yvel) { } }); - ArticleAdapter.bindArticle(helper.getView(R.id.rl_article), item, mOnCollectListener); + bindArticle(helper.getView(R.id.rl_article), item, mOnCollectListener); helper.addOnClickListener(R.id.tv_delete); } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/ReadLaterAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/ReadLaterAdapter.java index 9cdc501b..3de2f9bb 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/ReadLaterAdapter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/ReadLaterAdapter.java @@ -1,8 +1,11 @@ package per.goweii.wanandroid.module.mine.adapter; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; +import android.annotation.SuppressLint; import android.text.Html; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; @@ -14,24 +17,23 @@ import java.util.List; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.module.mine.model.ReadLaterEntity; +import per.goweii.wanandroid.db.model.ReadLaterModel; /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class ReadLaterAdapter extends BaseQuickAdapter { +public class ReadLaterAdapter extends BaseQuickAdapter { private final SimpleDateFormat mSimpleDateFormat; private final List mUnCloseList = new ArrayList<>(); + @SuppressLint("SimpleDateFormat") public ReadLaterAdapter() { - super(R.layout.rv_item_readlater); - mSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + super(R.layout.rv_item_read_later); + mSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); } public void closeAll(SwipeLayout layout) { @@ -66,7 +68,7 @@ public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { } @Override - protected void convert(BaseViewHolder helper, ReadLaterEntity item) { + protected void convert(BaseViewHolder helper, ReadLaterModel item) { SwipeLayout sl = helper.getView(R.id.sl); sl.addSwipeListener(new SwipeLayout.SwipeListener() { @Override @@ -96,6 +98,11 @@ public void onUpdate(SwipeLayout layout, int leftOffset, int topOffset) { public void onHandRelease(SwipeLayout layout, float xvel, float yvel) { } }); + if (TextUtils.isEmpty(item.getTitle())) { + helper.setText(R.id.tv_title, item.getLink()); + } else { + helper.setText(R.id.tv_title, Html.fromHtml(item.getTitle())); + } helper.setText(R.id.tv_title, Html.fromHtml(item.getTitle())); String time = mSimpleDateFormat.format(new Date(item.getTime())); helper.setText(R.id.tv_time, time); diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/ReadRecordAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/ReadRecordAdapter.java new file mode 100644 index 00000000..6181d8ff --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/adapter/ReadRecordAdapter.java @@ -0,0 +1,110 @@ +package per.goweii.wanandroid.module.mine.adapter; + +import android.annotation.SuppressLint; +import android.text.Html; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; + +import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; +import com.daimajia.swipe.SwipeLayout; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.db.model.ReadRecordModel; + +/** + * @author CuiZhen + * @date 2019/5/15 + * GitHub: https://github.com/goweii + */ +public class ReadRecordAdapter extends BaseQuickAdapter { + + private final SimpleDateFormat mSimpleDateFormat; + + private final List mUnCloseList = new ArrayList<>(); + + @SuppressLint("SimpleDateFormat") + public ReadRecordAdapter() { + super(R.layout.rv_item_read_record); + mSimpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + + public void closeAll(SwipeLayout layout) { + for (SwipeLayout swipeLayout : mUnCloseList) { + if (layout == swipeLayout) { + continue; + } + if (swipeLayout.getOpenStatus() != SwipeLayout.Status.Open) { + continue; + } + swipeLayout.close(); + } + } + + @Override + public void onAttachedToRecyclerView(RecyclerView recyclerView) { + super.onAttachedToRecyclerView(recyclerView); + recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { + @Override + public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { + super.onScrollStateChanged(recyclerView, newState); + if (newState == RecyclerView.SCROLL_STATE_DRAGGING) { + closeAll(null); + } + } + + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + super.onScrolled(recyclerView, dx, dy); + } + }); + } + + @Override + protected void convert(BaseViewHolder helper, ReadRecordModel item) { + SwipeLayout sl = helper.getView(R.id.sl); + sl.addSwipeListener(new SwipeLayout.SwipeListener() { + @Override + public void onStartOpen(SwipeLayout layout) { + closeAll(layout); + } + + @Override + public void onOpen(SwipeLayout layout) { + mUnCloseList.add(layout); + } + + @Override + public void onStartClose(SwipeLayout layout) { + } + + @Override + public void onClose(SwipeLayout layout) { + mUnCloseList.remove(layout); + } + + @Override + public void onUpdate(SwipeLayout layout, int leftOffset, int topOffset) { + } + + @Override + public void onHandRelease(SwipeLayout layout, float xvel, float yvel) { + } + }); + if (TextUtils.isEmpty(item.getTitle())) { + helper.setText(R.id.tv_title, item.getLink()); + } else { + helper.setText(R.id.tv_title, Html.fromHtml(item.getTitle())); + } + String time = mSimpleDateFormat.format(new Date(item.getLastTime())); + helper.setText(R.id.tv_time, time); + helper.addOnClickListener(R.id.rl_top, R.id.tv_delete, R.id.tv_open, R.id.tv_copy); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/contract/UserInfoContract.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/contract/UserInfoContract.kt new file mode 100644 index 00000000..f267f740 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/contract/UserInfoContract.kt @@ -0,0 +1,31 @@ +package per.goweii.wanandroid.module.mine.contract + +import per.goweii.basic.core.base.BasePresenter +import per.goweii.basic.core.base.BaseView +import per.goweii.wanandroid.module.login.model.UserEntity +import per.goweii.wanandroid.utils.UserUtils + +/** + * @author CuiZhen + * @date 2020/5/27 + */ +interface UserInfoView : BaseView { + fun gotoLogin() + fun mineInfoSuccess(userEntity: UserEntity) +} + +class UserInfoPresenter : BasePresenter() { + fun mineInfo() { + if (!UserUtils.getInstance().isLogin) { + if (isAttach) { + baseView.gotoLogin() + } + return + } + UserUtils.getInstance().loginUser.let { + if (isAttach) { + baseView.mineInfoSuccess(it) + } + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/dialog/AddHostDialog.java b/app/src/main/java/per/goweii/wanandroid/module/mine/dialog/AddHostDialog.java index e18ebf93..0b526856 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/dialog/AddHostDialog.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/dialog/AddHostDialog.java @@ -1,14 +1,15 @@ package per.goweii.wanandroid.module.mine.dialog; -import android.animation.Animator; import android.content.Context; import android.view.Gravity; import android.view.View; import android.widget.EditText; -import per.goweii.anylayer.AnimatorHelper; +import androidx.annotation.NonNull; + import per.goweii.anylayer.AnyLayer; import per.goweii.anylayer.Layer; +import per.goweii.anylayer.widget.SwipeLayout; import per.goweii.basic.utils.InputMethodUtils; import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.wanandroid.R; @@ -16,47 +17,44 @@ /** * @author CuiZhen * @date 2019/5/23 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class AddHostDialog { public static void show(Context context, SimpleCallback callback) { AnyLayer.dialog(context) - .contentView(R.layout.dialog_add_host) - .contentAnimator(new Layer.AnimatorCreator() { + .setContentView(R.layout.dialog_add_host) + .addSoftInputCompat(true, R.id.dialog_add_host_et_host) + .setSwipeDismiss(SwipeLayout.Direction.BOTTOM) + .setBackgroundDimDefault() + .setCancelableOnClickKeyBack(true) + .setCancelableOnTouchOutside(true) + .setGravity(Gravity.BOTTOM) + .addOnVisibleChangeListener(new Layer.OnVisibleChangedListener() { @Override - public Animator createInAnimator(View target) { - return AnimatorHelper.createTopInAnim(target); + public void onShow(@NonNull Layer layer) { + EditText et_host = layer.requireView(R.id.dialog_add_host_et_host); + et_host.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (!et_host.isFocused()) { + InputMethodUtils.hide(et_host); + } + } + }); } @Override - public Animator createOutAnimator(View target) { - return AnimatorHelper.createTopOutAnim(target); - } - }) - .avoidStatusBar(true) - .backgroundColorRes(R.color.dialog_bg) - .cancelableOnClickKeyBack(true) - .cancelableOnTouchOutside(true) - .gravity(Gravity.TOP) - .onDismissListener(new Layer.OnDismissListener() { - @Override - public void onDismissing(Layer layer) { - EditText et_host = layer.getView(R.id.dialog_add_host_et_host); + public void onDismiss(@NonNull Layer layer) { + EditText et_host = layer.requireView(R.id.dialog_add_host_et_host); InputMethodUtils.hide(et_host); } - - @Override - public void onDismissed(Layer layer) { - } }) - .onClickToDismiss(R.id.dialog_add_host_tv_no) - .onClickToDismiss(new Layer.OnClickListener() { + .addOnClickToDismissListener(R.id.dialog_add_host_tv_no) + .addOnClickToDismissListener(new Layer.OnClickListener() { @Override - public void onClick(Layer layer, View v) { - EditText et_host = layer.getView(R.id.dialog_add_host_et_host); + public void onClick(@NonNull Layer layer, @NonNull View v) { + EditText et_host = layer.requireView(R.id.dialog_add_host_et_host); if (callback != null) { String host = et_host.getText().toString(); callback.onResult(host); diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/dialog/EditCollectLinkDialog.java b/app/src/main/java/per/goweii/wanandroid/module/mine/dialog/EditCollectLinkDialog.java index 53597531..f5ed1865 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/dialog/EditCollectLinkDialog.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/dialog/EditCollectLinkDialog.java @@ -1,14 +1,15 @@ package per.goweii.wanandroid.module.mine.dialog; -import android.animation.Animator; import android.content.Context; import android.view.Gravity; import android.view.View; import android.widget.EditText; -import per.goweii.anylayer.AnimatorHelper; +import androidx.annotation.NonNull; + import per.goweii.anylayer.AnyLayer; import per.goweii.anylayer.Layer; +import per.goweii.anylayer.widget.SwipeLayout; import per.goweii.basic.utils.EditTextUtils; import per.goweii.basic.utils.InputMethodUtils; import per.goweii.basic.utils.listener.SimpleCallback; @@ -18,60 +19,61 @@ /** * @author CuiZhen * @date 2019/5/23 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class EditCollectLinkDialog { - public static void show(Context context, CollectionLinkBean data, SimpleCallback callback){ + public static void show(Context context, CollectionLinkBean data, SimpleCallback callback) { AnyLayer.dialog(context) - .contentView(R.layout.dialog_edit_collect_link) - .contentAnimator(new Layer.AnimatorCreator() { - @Override - public Animator createInAnimator(View target) { - return AnimatorHelper.createTopInAnim(target); - } - - @Override - public Animator createOutAnimator(View target) { - return AnimatorHelper.createTopOutAnim(target); - } - }) - .avoidStatusBar(true) - .backgroundColorRes(R.color.dialog_bg) - .cancelableOnClickKeyBack(true) - .cancelableOnTouchOutside(true) - .gravity(Gravity.TOP) - .bindData(new Layer.DataBinder() { + .setContentView(R.layout.dialog_edit_collect_link) + .setSwipeDismiss(SwipeLayout.Direction.BOTTOM) + .addSoftInputCompat(true, R.id.dialog_edit_collect_link_et_title) + .addSoftInputCompat(true, R.id.dialog_edit_collect_link_et_link) + .setBackgroundDimDefault() + .setCancelableOnClickKeyBack(true) + .setCancelableOnTouchOutside(true) + .setGravity(Gravity.BOTTOM) + .addOnBindDataListener(new Layer.OnBindDataListener() { @Override - public void bindData(Layer layer) { - EditText et_title = layer.getView(R.id.dialog_edit_collect_link_et_title); - EditText et_link = layer.getView(R.id.dialog_edit_collect_link_et_link); + public void onBindData(@NonNull Layer layer) { + EditText et_title = layer.requireView(R.id.dialog_edit_collect_link_et_title); + EditText et_link = layer.requireView(R.id.dialog_edit_collect_link_et_link); EditTextUtils.setTextWithSelection(et_title, data.getName()); EditTextUtils.setTextWithSelection(et_link, data.getLink()); } }) - .onDismissListener(new Layer.OnDismissListener() { + .addOnVisibleChangeListener(new Layer.OnVisibleChangedListener() { @Override - public void onDismissing(Layer layer) { - EditText et_title = layer.getView(R.id.dialog_edit_collect_link_et_title); - EditText et_link = layer.getView(R.id.dialog_edit_collect_link_et_link); - InputMethodUtils.hide(et_title); - InputMethodUtils.hide(et_link); + public void onShow(@NonNull Layer layer) { + EditText et_title = layer.requireView(R.id.dialog_edit_collect_link_et_title); + EditText et_link = layer.requireView(R.id.dialog_edit_collect_link_et_link); + View.OnFocusChangeListener listener = new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (!et_title.isFocused() && !et_link.isFocused()) { + InputMethodUtils.hide(et_title); + InputMethodUtils.hide(et_link); + } + } + }; + et_title.setOnFocusChangeListener(listener); + et_link.setOnFocusChangeListener(listener); } @Override - public void onDismissed(Layer layer) { - + public void onDismiss(@NonNull Layer layer) { + EditText et_title = layer.requireView(R.id.dialog_edit_collect_link_et_title); + EditText et_link = layer.requireView(R.id.dialog_edit_collect_link_et_link); + InputMethodUtils.hide(et_title); + InputMethodUtils.hide(et_link); } }) - .onClickToDismiss(R.id.dialog_edit_collect_link_tv_no) - .onClickToDismiss(new Layer.OnClickListener() { + .addOnClickToDismissListener(R.id.dialog_edit_collect_link_tv_no) + .addOnClickToDismissListener(new Layer.OnClickListener() { @Override - public void onClick(Layer layer, View v) { - EditText et_title = layer.getView(R.id.dialog_edit_collect_link_et_title); - EditText et_link = layer.getView(R.id.dialog_edit_collect_link_et_link); + public void onClick(@NonNull Layer layer, @NonNull View v) { + EditText et_title = layer.requireView(R.id.dialog_edit_collect_link_et_title); + EditText et_link = layer.requireView(R.id.dialog_edit_collect_link_et_link); if (callback != null) { String title = et_title.getText().toString(); String link = et_link.getText().toString(); diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/dialog/InfoEditDialog.java b/app/src/main/java/per/goweii/wanandroid/module/mine/dialog/InfoEditDialog.java new file mode 100644 index 00000000..9e636c6d --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/dialog/InfoEditDialog.java @@ -0,0 +1,122 @@ +package per.goweii.wanandroid.module.mine.dialog; + +import android.content.Context; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; +import android.widget.EditText; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import per.goweii.anylayer.AnyLayer; +import per.goweii.anylayer.Layer; +import per.goweii.anylayer.widget.SwipeLayout; +import per.goweii.basic.utils.InputMethodUtils; +import per.goweii.basic.utils.listener.SimpleCallback; +import per.goweii.wanandroid.R; + +/** + * @author CuiZhen + * @date 2019/5/23 + * GitHub: https://github.com/goweii + */ +public class InfoEditDialog { + + public static InfoEditDialog with(Context context) { + return new InfoEditDialog(context); + } + + private final Context mContext; + + private String mTitle = null; + private String mHint = null; + private String mText = null; + private int mLines = 1; + + private InfoEditDialog(Context context) { + mContext = context; + } + + public InfoEditDialog title(String title) { + mTitle = title; + return this; + } + + public InfoEditDialog hint(String hint) { + mHint = hint; + return this; + } + + public InfoEditDialog text(String text) { + mText = text; + return this; + } + + public InfoEditDialog lines(int lines) { + mLines = lines; + return this; + } + + public void show(SimpleCallback callback) { + AnyLayer.dialog(mContext) + .setContentView(R.layout.dialog_info_edit) + .setSwipeDismiss(SwipeLayout.Direction.BOTTOM) + .addSoftInputCompat(true) + .setBackgroundDimDefault() + .setCancelableOnClickKeyBack(true) + .setCancelableOnTouchOutside(true) + .setGravity(Gravity.BOTTOM) + .addOnBindDataListener(new Layer.OnBindDataListener() { + @Override + public void onBindData(@NonNull Layer layer) { + if (!TextUtils.isEmpty(mTitle)) { + TextView tv_title = layer.requireView(R.id.dialog_info_edit_tv_title); + tv_title.setText(mTitle); + } + EditText et_input = layer.requireView(R.id.dialog_info_edit_et_input); + if (mLines > 0) { + et_input.setLines(mLines); + } + if (!TextUtils.isEmpty(mHint) || !TextUtils.isEmpty(mText)) { + et_input.setText(mText); + et_input.setHint(mHint); + } + } + }) + .addOnVisibleChangeListener(new Layer.OnVisibleChangedListener() { + @Override + public void onShow(@NonNull Layer layer) { + EditText et_input = layer.requireView(R.id.dialog_info_edit_et_input); + View.OnFocusChangeListener listener = new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (!et_input.isFocused()) { + InputMethodUtils.hide(et_input); + } + } + }; + et_input.setOnFocusChangeListener(listener); + } + + @Override + public void onDismiss(@NonNull Layer layer) { + EditText et_input = layer.requireView(R.id.dialog_info_edit_et_input); + InputMethodUtils.hide(et_input); + } + }) + .addOnClickToDismissListener(R.id.dialog_info_edit_tv_no) + .addOnClickToDismissListener(new Layer.OnClickListener() { + @Override + public void onClick(@NonNull Layer layer, @NonNull View v) { + EditText et_input = layer.requireView(R.id.dialog_info_edit_et_input); + if (callback != null) { + String str = et_input.getText().toString(); + callback.onResult(str); + } + } + }, R.id.dialog_info_edit_tv_yes) + .show(); + } + +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/CollectionArticleFragment.java b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/CollectionArticleFragment.java index f9cbbe17..09776987 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/CollectionArticleFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/CollectionArticleFragment.java @@ -1,49 +1,46 @@ package per.goweii.wanandroid.module.mine.fragment; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.View; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.kennyc.view.MultiStateView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import java.util.List; - import butterknife.BindView; import per.goweii.basic.core.base.BaseFragment; import per.goweii.basic.core.utils.SmartRefreshUtils; import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.common.ScrollTop; +import per.goweii.wanandroid.databinding.FragmentCollectionArticleBinding; import per.goweii.wanandroid.event.CollectionEvent; -import per.goweii.wanandroid.event.SettingChangeEvent; -import per.goweii.wanandroid.module.main.activity.WebActivity; import per.goweii.wanandroid.module.main.adapter.ArticleAdapter; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.ArticleListBean; import per.goweii.wanandroid.module.mine.presenter.CollectionArticlePresenter; import per.goweii.wanandroid.module.mine.view.CollectionArticleView; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; import per.goweii.wanandroid.utils.RvScrollTopUtils; -import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; import per.goweii.wanandroid.widget.CollectView; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class CollectionArticleFragment extends BaseFragment implements ScrollTop, CollectionArticleView { +public class CollectionArticleFragment extends BaseFragment implements RvScrollTopUtils.ScrollTop, CollectionArticleView { public static final int PAGE_START = 0; @@ -72,44 +69,38 @@ public void onCollectionEvent(CollectionEvent event) { currPage = PAGE_START; presenter.getCollectArticleList(currPage, true); } else { + presenter.updateCollectArticleList(PAGE_START); if (event.getArticleId() != -1 || event.getCollectId() != -1) { - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (event.getArticleId() != -1) { - if (item.getOriginId() == event.getArticleId()) { - mAdapter.remove(i); - break; - } - } else if (event.getCollectId() != -1) { - if (item.getId() == event.getCollectId()) { - mAdapter.remove(i); - break; + mAdapter.forEach(new ArticleAdapter.ArticleForEach() { + @Override + public boolean forEach(int dataPos, int adapterPos, ArticleBean bean) { + if (event.getArticleId() != -1) { + if (bean.getOriginId() == event.getArticleId()) { + mAdapter.remove(adapterPos); + return true; + } + } else if (event.getCollectId() != -1) { + if (bean.getId() == event.getCollectId()) { + mAdapter.remove(adapterPos); + return true; + } } + return false; } - } + }); } } } - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { - if (isDetached()) { - return; - } - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); - } - } - @Override protected boolean isRegisterEventBus() { return true; } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_collection_article; + protected FragmentCollectionArticleBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentCollectionArticleBinding.inflate(inflater, container, false); } @Nullable @@ -131,7 +122,6 @@ public void onRefresh() { }); rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new ArticleAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); mAdapter.setEnableLoadMore(false); mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override @@ -142,18 +132,18 @@ public void onLoadMoreRequested() { mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - WebActivity.start(getContext(), item); + UrlOpenUtils.Companion.with(item).open(getContext()); } } }); mAdapter.setOnItemChildViewClickListener(new ArticleAdapter.OnItemChildViewClickListener() { @Override public void onCollectClick(BaseViewHolder helper, CollectView v, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - presenter.uncollect(item, v); + presenter.uncollectArticle(item, v); } } }); @@ -192,7 +182,7 @@ public void getCollectArticleListSuccess(int code, ArticleListBean data) { } currPage = data.getCurPage() + PAGE_START; if (data.getCurPage() == 1) { - mAdapter.setNewData(data.getDatas()); + mAdapter.setArticleData(data.getDatas()); mAdapter.setEnableLoadMore(true); if (data.getDatas() == null || data.getDatas().isEmpty()) { MultiStateUtils.toEmpty(msv); diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/CollectionLinkFragment.java b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/CollectionLinkFragment.java index 568aba7d..fd70ab2e 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/CollectionLinkFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/CollectionLinkFragment.java @@ -1,16 +1,20 @@ package per.goweii.wanandroid.module.mine.fragment; -import android.support.annotation.Nullable; -import android.support.v4.view.ViewPager; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.text.TextUtils; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.view.ViewParent; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewpager.widget.ViewPager; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.kennyc.view.MultiStateView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -28,28 +32,24 @@ import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.common.ScrollTop; +import per.goweii.wanandroid.databinding.FragmentCollectionLinkBinding; import per.goweii.wanandroid.event.CollectionEvent; -import per.goweii.wanandroid.event.SettingChangeEvent; -import per.goweii.wanandroid.module.main.activity.WebActivity; import per.goweii.wanandroid.module.main.model.CollectionLinkBean; import per.goweii.wanandroid.module.mine.adapter.CollectionLinkAdapter; import per.goweii.wanandroid.module.mine.dialog.EditCollectLinkDialog; import per.goweii.wanandroid.module.mine.presenter.CollectionLinkPresenter; import per.goweii.wanandroid.module.mine.view.CollectionLinkView; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; +import per.goweii.wanandroid.utils.RvConfigUtils; import per.goweii.wanandroid.utils.RvScrollTopUtils; -import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class CollectionLinkFragment extends BaseFragment implements ScrollTop, CollectionLinkView { +public class CollectionLinkFragment extends BaseFragment implements RvScrollTopUtils.ScrollTop, CollectionLinkView { @BindView(R.id.msv) MultiStateView msv; @@ -73,6 +73,7 @@ public void onCollectionEvent(CollectionEvent event) { if (event.isCollect()) { presenter.getCollectLinkList(true); } else { + presenter.updateCollectLinkList(); if (event.getCollectId() != -1) { List list = mAdapter.getData(); for (int i = 0; i < list.size(); i++) { @@ -86,24 +87,15 @@ public void onCollectionEvent(CollectionEvent event) { } } - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { - if (isDetached()) { - return; - } - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); - } - } - @Override protected boolean isRegisterEventBus() { return true; } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_collection_link; + protected FragmentCollectionLinkBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentCollectionLinkBinding.inflate(inflater, container, false); } @Nullable @@ -124,7 +116,7 @@ public void onRefresh() { }); rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new CollectionLinkAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); + RvConfigUtils.init(mAdapter); mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() { @Override public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) { @@ -137,15 +129,20 @@ public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) default: break; case R.id.rl_top: - WebActivity.start(getContext(), item.getName(), item.getLink(), true); + UrlOpenUtils.Companion + .with(item.getLink()) + .collectId(item.getId()) + .title(item.getName()) + .collected(true) + .open(getContext()); break; case R.id.tv_copy: CopyUtils.copyText(item.getLink()); - ToastMaker.showShort("复制成功"); + ToastMaker.showShort(getString(R.string.copied)); break; case R.id.tv_open: if (TextUtils.isEmpty(item.getLink())) { - ToastMaker.showShort("链接为空"); + ToastMaker.showShort(getString(R.string.the_link_is_empty)); break; } if (getContext() != null) { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/HostBlackFragment.java b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/HostBlackFragment.java index b2a28678..247e8944 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/HostBlackFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/HostBlackFragment.java @@ -1,15 +1,15 @@ package per.goweii.wanandroid.module.mine.fragment; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; -import com.chad.library.adapter.base.BaseQuickAdapter; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; +import com.chad.library.adapter.base.BaseQuickAdapter; import java.util.ArrayList; import java.util.List; @@ -20,23 +20,20 @@ import per.goweii.basic.utils.listener.OnClickListener2; import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.common.ScrollTop; -import per.goweii.wanandroid.event.SettingChangeEvent; +import per.goweii.wanandroid.databinding.FragmentHostInterruptBinding; import per.goweii.wanandroid.module.mine.adapter.HostInterruptAdapter; import per.goweii.wanandroid.module.mine.dialog.AddHostDialog; import per.goweii.wanandroid.module.mine.model.HostEntity; -import per.goweii.wanandroid.utils.RvAnimUtils; +import per.goweii.wanandroid.utils.RvConfigUtils; import per.goweii.wanandroid.utils.RvScrollTopUtils; import per.goweii.wanandroid.utils.SettingUtils; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class HostBlackFragment extends BaseFragment implements ScrollTop { +public class HostBlackFragment extends BaseFragment implements RvScrollTopUtils.ScrollTop { @BindView(R.id.rv) RecyclerView rv; @@ -47,24 +44,10 @@ public static HostBlackFragment create() { return new HostBlackFragment(); } - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { - if (isDetached()) { - return; - } - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); - } - } - - @Override - protected boolean isRegisterEventBus() { - return true; - } - + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_host_interrupt; + protected FragmentHostInterruptBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentHostInterruptBinding.inflate(inflater, container, false); } @Nullable @@ -77,7 +60,7 @@ protected BasePresenter initPresenter() { protected void initView() { rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new HostInterruptAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); + RvConfigUtils.init(mAdapter); mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() { @Override public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/HostWhiteFragment.java b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/HostWhiteFragment.java index 1d287968..3cd289d0 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/HostWhiteFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/HostWhiteFragment.java @@ -1,15 +1,15 @@ package per.goweii.wanandroid.module.mine.fragment; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; -import com.chad.library.adapter.base.BaseQuickAdapter; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; +import com.chad.library.adapter.base.BaseQuickAdapter; import java.util.ArrayList; import java.util.List; @@ -20,23 +20,20 @@ import per.goweii.basic.utils.listener.OnClickListener2; import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.common.ScrollTop; -import per.goweii.wanandroid.event.SettingChangeEvent; +import per.goweii.wanandroid.databinding.FragmentHostInterruptBinding; import per.goweii.wanandroid.module.mine.adapter.HostInterruptAdapter; import per.goweii.wanandroid.module.mine.dialog.AddHostDialog; import per.goweii.wanandroid.module.mine.model.HostEntity; -import per.goweii.wanandroid.utils.RvAnimUtils; +import per.goweii.wanandroid.utils.RvConfigUtils; import per.goweii.wanandroid.utils.RvScrollTopUtils; import per.goweii.wanandroid.utils.SettingUtils; /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class HostWhiteFragment extends BaseFragment implements ScrollTop { +public class HostWhiteFragment extends BaseFragment implements RvScrollTopUtils.ScrollTop { @BindView(R.id.rv) RecyclerView rv; @@ -47,24 +44,10 @@ public static HostWhiteFragment create() { return new HostWhiteFragment(); } - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { - if (isDetached()) { - return; - } - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); - } - } - - @Override - protected boolean isRegisterEventBus() { - return true; - } - + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_host_interrupt; + protected FragmentHostInterruptBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentHostInterruptBinding.inflate(inflater, container, false); } @Nullable @@ -77,7 +60,7 @@ protected BasePresenter initPresenter() { protected void initView() { rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new HostInterruptAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); + RvConfigUtils.init(mAdapter); mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() { @Override public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/MessageReadedFragment.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/MessageReadedFragment.kt new file mode 100644 index 00000000..62d5ad88 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/MessageReadedFragment.kt @@ -0,0 +1,155 @@ +package per.goweii.wanandroid.module.mine.fragment + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.viewpager.widget.ViewPager +import androidx.viewpager.widget.ViewPager.OnPageChangeListener +import com.chad.library.adapter.base.BaseQuickAdapter +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import per.goweii.basic.core.base.BaseFragment +import per.goweii.basic.core.utils.SmartRefreshUtils +import per.goweii.basic.utils.listener.SimpleListener +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.FragmentMessageReadedBinding +import per.goweii.wanandroid.event.MessageDeleteEvent +import per.goweii.wanandroid.module.main.model.ListBean +import per.goweii.wanandroid.module.mine.adapter.MessageReadedAdapter +import per.goweii.wanandroid.module.mine.model.MessageBean +import per.goweii.wanandroid.module.mine.presenter.MessageReadedPresenter +import per.goweii.wanandroid.module.mine.view.MessageReadedView +import per.goweii.wanandroid.utils.MultiStateUtils +import per.goweii.wanandroid.utils.UrlOpenUtils + +/** + * @author CuiZhen + * @date 2020/5/16 + */ +class MessageReadedFragment : BaseFragment(), MessageReadedView { + + companion object { + const val PAGE_START = 1 + fun create() = MessageReadedFragment() + } + + private lateinit var mSmartRefreshUtils: SmartRefreshUtils + private lateinit var mAdapter: MessageReadedAdapter + + private var currPage = PAGE_START + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onMessageDeleteEvent(event: MessageDeleteEvent) { + if (isDetached) return + mAdapter.data.forEachIndexed { index, messageBean -> + if (messageBean.id == event.messageBean.id) { + mAdapter.remove(index) + return@forEachIndexed + } + } + if (mAdapter.data.isEmpty()) { + currPage = PAGE_START + presenter.getMessageReadList(currPage) + } + } + + override fun isRegisterEventBus(): Boolean { + return true + } + + override fun initViewBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentMessageReadedBinding { + return FragmentMessageReadedBinding.inflate(inflater, container, false) + } + + override fun initPresenter() = MessageReadedPresenter() + + override fun initView() { + mSmartRefreshUtils = SmartRefreshUtils.with(binding.srl) + mSmartRefreshUtils.pureScrollMode() + mSmartRefreshUtils.setRefreshListener { + currPage = PAGE_START + presenter.getMessageReadList(currPage) + } + binding.rv.layoutManager = LinearLayoutManager(context) + mAdapter = MessageReadedAdapter() + mAdapter.setEnableLoadMore(false) + mAdapter.setOnLoadMoreListener({ + presenter.getMessageReadList(currPage) + }, binding.rv) + mAdapter.onItemChildClickListener = BaseQuickAdapter.OnItemChildClickListener { _, view, position -> + mAdapter.closeAll(null) + val item = mAdapter.getItem(position) ?: return@OnItemChildClickListener + when (view.id) { + R.id.rl_message -> { + UrlOpenUtils.with(item.realLink).open(context) + } + R.id.tv_delete -> { + presenter.delete(item) + } + } + } + binding.rv.adapter = mAdapter + MultiStateUtils.setEmptyAndErrorClick(binding.msv, SimpleListener { + MultiStateUtils.toLoading(binding.msv) + currPage = PAGE_START + presenter.getMessageReadList(currPage) + }) + val parent = rootView?.parent + if (parent is ViewPager) { + parent.addOnPageChangeListener(object : OnPageChangeListener { + override fun onPageScrolled(i: Int, v: Float, i1: Int) {} + override fun onPageSelected(i: Int) {} + override fun onPageScrollStateChanged(i: Int) { + if (i != ViewPager.SCROLL_STATE_IDLE) { + mAdapter.closeAll(null) + } + } + }) + } + } + + override fun loadData() { + } + + override fun onVisible(isFirstVisible: Boolean) { + super.onVisible(isFirstVisible) + if (isFirstVisible) { + MultiStateUtils.toLoading(binding.msv) + currPage = PAGE_START + presenter.getMessageReadList(currPage) + } + } + + override fun getMessageReadListSuccess(code: Int, data: ListBean) { + if (data.curPage == PAGE_START) { + mAdapter.setNewData(data.datas) + mAdapter.setEnableLoadMore(true) + if (data.datas == null || data.datas.isEmpty()) { + MultiStateUtils.toEmpty(binding.msv) + } else { + MultiStateUtils.toContent(binding.msv) + } + } else { + mAdapter.addData(data.datas) + mAdapter.loadMoreComplete() + } + if (data.isOver) { + mAdapter.loadMoreEnd() + } + mSmartRefreshUtils.success() + currPage++ + } + + override fun getMessageReadListFail(code: Int, msg: String) { + } + + override fun deleteMessageSuccess(code: Int, data: MessageBean) { + MessageDeleteEvent.post(data) + } + + override fun deleteMessageFail(code: Int, msg: String) { + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/MessageUnreadFragment.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/MessageUnreadFragment.kt new file mode 100644 index 00000000..fdee4dba --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/MessageUnreadFragment.kt @@ -0,0 +1,129 @@ +package per.goweii.wanandroid.module.mine.fragment + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import com.chad.library.adapter.base.BaseQuickAdapter +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import per.goweii.basic.core.base.BaseFragment +import per.goweii.basic.core.utils.SmartRefreshUtils +import per.goweii.basic.utils.listener.SimpleListener +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.FragmentMessageUnreadBinding +import per.goweii.wanandroid.event.MessageDeleteEvent +import per.goweii.wanandroid.event.MessageUpdateEvent +import per.goweii.wanandroid.module.main.model.ListBean +import per.goweii.wanandroid.module.mine.adapter.MessageUnreadAdapter +import per.goweii.wanandroid.module.mine.model.MessageBean +import per.goweii.wanandroid.module.mine.presenter.MessageUnreadPresenter +import per.goweii.wanandroid.module.mine.view.MessageUnreadView +import per.goweii.wanandroid.utils.MultiStateUtils +import per.goweii.wanandroid.utils.UrlOpenUtils + +/** + * @author CuiZhen + * @date 2020/5/16 + */ +class MessageUnreadFragment : BaseFragment(), MessageUnreadView { + + companion object { + const val PAGE_START = 1 + fun create() = MessageUnreadFragment() + } + + private lateinit var mSmartRefreshUtils: SmartRefreshUtils + private lateinit var mAdapter: MessageUnreadAdapter + + private var currPage = PAGE_START + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onMessageDeleteEvent(event: MessageDeleteEvent) { + if (isDetached) return + mAdapter.data.forEachIndexed { index, notificationBean -> + if (notificationBean.id == event.messageBean.id) { + mAdapter.remove(index) + return@forEachIndexed + } + } + if (mAdapter.data.isEmpty()) { + currPage = PAGE_START + presenter.getMessageUnreadList(currPage) + } + } + + override fun isRegisterEventBus(): Boolean { + return true + } + + override fun initViewBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentMessageUnreadBinding { + return FragmentMessageUnreadBinding.inflate(inflater, container, false) + } + + override fun initPresenter() = MessageUnreadPresenter() + + override fun initView() { + mSmartRefreshUtils = SmartRefreshUtils.with(binding.srl) + mSmartRefreshUtils.pureScrollMode() + mSmartRefreshUtils.setRefreshListener { + currPage = PAGE_START + presenter.getMessageUnreadList(currPage) + } + binding.rv.layoutManager = LinearLayoutManager(context) + mAdapter = MessageUnreadAdapter() + mAdapter.setEnableLoadMore(false) + mAdapter.setOnLoadMoreListener({ + presenter.getMessageUnreadList(currPage) + }, binding.rv) + mAdapter.onItemClickListener = BaseQuickAdapter.OnItemClickListener { _, _, position -> + mAdapter.getItem(position)?.let { + UrlOpenUtils.with(it.realLink).open(context) + } + } + binding.rv.adapter = mAdapter + MultiStateUtils.setEmptyAndErrorClick(binding.msv, SimpleListener { + MultiStateUtils.toLoading(binding.msv) + currPage = PAGE_START + presenter.getMessageUnreadList(currPage) + }) + } + + override fun loadData() { + } + + override fun onVisible(isFirstVisible: Boolean) { + super.onVisible(isFirstVisible) + if (isFirstVisible) { + MultiStateUtils.toLoading(binding.msv) + currPage = PAGE_START + presenter.getMessageUnreadList(currPage) + } + } + + override fun getMessageUnreadListSuccess(code: Int, data: ListBean) { + if (data.curPage == PAGE_START) { + mAdapter.setNewData(data.datas) + mAdapter.setEnableLoadMore(true) + if (data.datas == null || data.datas.isEmpty()) { + MultiStateUtils.toEmpty(binding.msv) + } else { + MultiStateUtils.toContent(binding.msv) + } + } else { + mAdapter.addData(data.datas) + mAdapter.loadMoreComplete() + } + if (data.isOver) { + mAdapter.loadMoreEnd() + } + mSmartRefreshUtils.success() + currPage++ + MessageUpdateEvent().post() + } + + override fun getMessageUnreadListFail(code: Int, msg: String) { + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/MineFragment.java b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/MineFragment.java index df2482b6..c8e13d87 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/MineFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/fragment/MineFragment.java @@ -1,67 +1,67 @@ package per.goweii.wanandroid.module.mine.fragment; -import android.content.Intent; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.widget.NestedScrollView; -import android.text.TextUtils; +import android.annotation.SuppressLint; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; -import com.scwang.smartrefresh.layout.api.RefreshFooter; -import com.scwang.smartrefresh.layout.api.RefreshHeader; -import com.scwang.smartrefresh.layout.api.RefreshLayout; -import com.scwang.smartrefresh.layout.constant.RefreshState; -import com.scwang.smartrefresh.layout.listener.OnMultiPurposeListener; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.widget.NestedScrollView; + +import com.scwang.smart.refresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.api.RefreshFooter; +import com.scwang.smart.refresh.layout.api.RefreshHeader; +import com.scwang.smart.refresh.layout.simple.SimpleMultiListener; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import butterknife.BindView; import butterknife.OnClick; -import per.goweii.actionbarex.common.ActionBarCommon; -import per.goweii.actionbarex.common.OnActionBarChildClickListener; import per.goweii.basic.core.base.BaseFragment; import per.goweii.basic.core.utils.SmartRefreshUtils; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.common.Config; +import per.goweii.wanandroid.databinding.FragmentMineBinding; import per.goweii.wanandroid.event.LoginEvent; -import per.goweii.wanandroid.event.SettingChangeEvent; -import per.goweii.wanandroid.module.login.model.LoginBean; +import per.goweii.wanandroid.event.MessageCountEvent; +import per.goweii.wanandroid.event.MessageUpdateEvent; +import per.goweii.wanandroid.event.UserInfoUpdateEvent; +import per.goweii.wanandroid.module.login.model.UserEntity; import per.goweii.wanandroid.module.mine.activity.AboutMeActivity; import per.goweii.wanandroid.module.mine.activity.CoinActivity; -import per.goweii.wanandroid.module.mine.activity.CoinRankActivity; import per.goweii.wanandroid.module.mine.activity.CollectionActivity; +import per.goweii.wanandroid.module.mine.activity.MessageActivity; import per.goweii.wanandroid.module.mine.activity.MineShareActivity; import per.goweii.wanandroid.module.mine.activity.OpenActivity; import per.goweii.wanandroid.module.mine.activity.ReadLaterActivity; +import per.goweii.wanandroid.module.mine.activity.ReadRecordActivity; import per.goweii.wanandroid.module.mine.activity.SettingActivity; +import per.goweii.wanandroid.module.mine.activity.UserInfoActivity; import per.goweii.wanandroid.module.mine.model.UserInfoBean; import per.goweii.wanandroid.module.mine.presenter.MinePresenter; import per.goweii.wanandroid.module.mine.view.MineView; import per.goweii.wanandroid.utils.ImageLoader; -import per.goweii.wanandroid.utils.PictureSelectorUtils; -import per.goweii.wanandroid.utils.SettingUtils; -import per.goweii.wanandroid.utils.UserInfoUtils; import per.goweii.wanandroid.utils.UserUtils; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class MineFragment extends BaseFragment implements MineView { - - private static final int REQUEST_CODE_SELECT_USER_ICON = 1; - private static final int REQUEST_CODE_SELECT_BG = 2; +public class MineFragment extends BaseFragment implements MineView { - @BindView(R.id.abc) - ActionBarCommon abc; + @BindView(R.id.aiv_notification) + ImageView aiv_notification; + @BindView(R.id.tv_notification) + TextView tv_notification; @BindView(R.id.srl) SmartRefreshLayout srl; @BindView(R.id.nsv) @@ -74,10 +74,12 @@ public class MineFragment extends BaseFragment implements MineVie ImageView civ_user_icon; @BindView(R.id.tv_user_name) TextView tv_user_name; - @BindView(R.id.tv_user_id) - TextView tv_user_id; + @BindView(R.id.ll_user_level_ranking) + LinearLayout ll_user_level_ranking; @BindView(R.id.ll_read_later) LinearLayout ll_read_later; + @BindView(R.id.ll_read_record) + LinearLayout ll_read_record; @BindView(R.id.ll_open) LinearLayout ll_open; @BindView(R.id.ll_about_me) @@ -101,18 +103,25 @@ public void onLoginEvent(LoginEvent event) { return; } setRefresh(); - changeUserInfo(); + refreshUserInfo(); loadUserInfo(); + loadNotificationCount(); } @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { + public void onUserInfoUpdateEvent(UserInfoUpdateEvent event) { if (isDetached()) { return; } - if (event.isShowReadLaterChanged() || event.isHideAboutMeChanged() || event.isHideOpenChanged()) { - changeMenuVisible(); + refreshUserInfo(); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onMessageUpdateEvent(MessageUpdateEvent event) { + if (isDetached()) { + return; } + presenter.getMessageUnreadCount(); } @Override @@ -120,9 +129,10 @@ protected boolean isRegisterEventBus() { return true; } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_mine; + protected FragmentMineBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentMineBinding.inflate(inflater, container, false); } @Nullable @@ -133,81 +143,41 @@ protected MinePresenter initPresenter() { @Override protected void initView() { - abc.setOnRightIconClickListener(new OnActionBarChildClickListener() { + aiv_notification.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - CoinRankActivity.start(getContext()); + if (UserUtils.getInstance().doIfLogin(getContext())) { + MessageActivity.start(requireContext()); + loadNotificationCount(); + } } }); nsv.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() { @Override public void onScrollChange(NestedScrollView nestedScrollView, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { + if (nsv == null || rl_user_info == null) return; setIvBlurHeight(rl_user_info.getMeasuredHeight() - scrollY); } }); - srl.setOnMultiPurposeListener(new OnMultiPurposeListener() { + srl.setOnMultiListener(new SimpleMultiListener() { @Override public void onHeaderMoving(RefreshHeader header, boolean isDragging, float percent, int offset, int headerHeight, int maxDragHeight) { + if (nsv == null || rl_user_info == null) return; setIvBlurHeight(rl_user_info.getMeasuredHeight() - nsv.getScrollY() + offset); } - @Override - public void onHeaderReleased(RefreshHeader header, int headerHeight, int maxDragHeight) { - } - - @Override - public void onHeaderStartAnimator(RefreshHeader header, int headerHeight, int maxDragHeight) { - } - - @Override - public void onHeaderFinish(RefreshHeader header, boolean success) { - } - @Override public void onFooterMoving(RefreshFooter footer, boolean isDragging, float percent, int offset, int footerHeight, int maxDragHeight) { setIvBlurHeight(rl_user_info.getMeasuredHeight() - nsv.getScrollY() - offset); } - - @Override - public void onFooterReleased(RefreshFooter footer, int footerHeight, int maxDragHeight) { - } - - @Override - public void onFooterStartAnimator(RefreshFooter footer, int footerHeight, int maxDragHeight) { - } - - @Override - public void onFooterFinish(RefreshFooter footer, boolean success) { - } - - @Override - public void onLoadMore(@NonNull RefreshLayout refreshLayout) { - } - - @Override - public void onRefresh(@NonNull RefreshLayout refreshLayout) { - } - - @Override - public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) { - } - }); - rl_user_info.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (UserUtils.getInstance().doIfLogin(getContext())) { - PictureSelectorUtils.ofImage(MineFragment.this, REQUEST_CODE_SELECT_BG); - } - return true; - } }); mSmartRefreshUtils = SmartRefreshUtils.with(srl); mSmartRefreshUtils.pureScrollMode(); setRefresh(); - changeMenuVisible(); } private void setIvBlurHeight(int h) { + if (iv_blur == null) return; if (h >= 0) { iv_blur.getLayoutParams().height = h; } else { @@ -222,6 +192,7 @@ private void setRefresh() { @Override public void onRefresh() { loadUserInfo(); + loadNotificationCount(); } }); } else { @@ -231,7 +202,8 @@ public void onRefresh() { @Override protected void loadData() { - changeUserInfo(); + refreshUserInfo(); + loadNotificationCount(); } private void loadUserInfo() { @@ -240,6 +212,10 @@ private void loadUserInfo() { } } + private void loadNotificationCount() { + presenter.getMessageUnreadCount(); + } + @Override public void onVisible(boolean isFirstVisible) { super.onVisible(isFirstVisible); @@ -248,40 +224,28 @@ public void onVisible(boolean isFirstVisible) { } } - private void changeMenuVisible() { - SettingUtils settingUtils = SettingUtils.getInstance(); - if (settingUtils.isShowReadLater()) { - ll_read_later.setVisibility(View.VISIBLE); - } else { - ll_read_later.setVisibility(View.GONE); - } - if (!settingUtils.isHideAboutMe()) { - ll_about_me.setVisibility(View.VISIBLE); - } else { - ll_about_me.setVisibility(View.GONE); - } - if (!settingUtils.isHideOpen()) { - ll_open.setVisibility(View.VISIBLE); - } else { - ll_open.setVisibility(View.GONE); - } - } - - private void changeUserInfo() { + @SuppressLint("SetTextI18n") + private void refreshUserInfo() { if (UserUtils.getInstance().isLogin()) { - LoginBean bean = UserUtils.getInstance().getLoginBean(); - ImageLoader.userIcon(civ_user_icon, UserInfoUtils.getInstance().getIcon()); - ImageLoader.userBlur(iv_blur, UserInfoUtils.getInstance().getBg()); + UserEntity bean = UserUtils.getInstance().getLoginUser(); + ImageLoader.userIcon(civ_user_icon, bean.getAvatar()); + ImageLoader.userBlur(iv_blur, bean.getCover()); tv_user_name.setText(bean.getUsername()); - tv_user_id.setText(bean.getId() + ""); - tv_user_level.setText("--"); - tv_user_ranking.setText("--"); - tv_coin.setText(""); + ll_user_level_ranking.setVisibility(View.VISIBLE); + if (presenter.mUserInfoBean != null) { + tv_coin.setText(presenter.mUserInfoBean.getCoinCount() + ""); + tv_user_level.setText(presenter.mUserInfoBean.getLevel() + ""); + tv_user_ranking.setText(presenter.mUserInfoBean.getRank() + ""); + } else { + tv_user_level.setText("--"); + tv_user_ranking.setText("--"); + tv_coin.setText(""); + } } else { - civ_user_icon.setImageResource(R.color.transparent); - iv_blur.setImageResource(R.color.transparent); - tv_user_name.setText("去登陆"); - tv_user_id.setText("-----"); + civ_user_icon.setImageDrawable(new ColorDrawable(Color.TRANSPARENT)); + iv_blur.setImageDrawable(new ColorDrawable(Color.TRANSPARENT)); + tv_user_name.setText(getString(R.string.go_to_sign_in)); + ll_user_level_ranking.setVisibility(View.INVISIBLE); tv_user_level.setText("--"); tv_user_ranking.setText("--"); tv_coin.setText(""); @@ -289,8 +253,8 @@ private void changeUserInfo() { } @OnClick({ - R.id.civ_user_icon, R.id.tv_user_name, R.id.ll_user_id, - R.id.ll_collect, R.id.ll_read_later, R.id.ll_about_me, + R.id.civ_user_icon, R.id.tv_user_name, + R.id.ll_collect, R.id.ll_read_later, R.id.ll_read_record, R.id.ll_about_me, R.id.ll_open, R.id.ll_setting, R.id.ll_coin, R.id.ll_share }) @Override @@ -304,35 +268,32 @@ protected void onClick2(View v) { default: break; case R.id.ll_coin: - if (UserUtils.getInstance().doIfLogin(getContext())) { - CoinActivity.start(getContext()); + if (UserUtils.getInstance().doIfLogin(requireContext())) { + CoinActivity.start(requireContext()); } break; case R.id.civ_user_icon: - if (UserUtils.getInstance().doIfLogin(getContext())) { - PictureSelectorUtils.ofImage(this, REQUEST_CODE_SELECT_USER_ICON); - } - break; case R.id.tv_user_name: - if (UserUtils.getInstance().doIfLogin(getContext())) { + if (UserUtils.getInstance().doIfLogin(requireContext())) { + UserInfoActivity.start(requireContext()); } break; - case R.id.ll_user_id: - UserUtils.getInstance().doIfLogin(getContext()); - break; case R.id.ll_share: - if (UserUtils.getInstance().doIfLogin(getContext())) { - MineShareActivity.start(getContext()); + if (UserUtils.getInstance().doIfLogin(requireContext())) { + MineShareActivity.start(requireContext()); } break; case R.id.ll_collect: - if (UserUtils.getInstance().doIfLogin(getContext())) { - CollectionActivity.start(getContext()); + if (UserUtils.getInstance().doIfLogin(requireContext())) { + CollectionActivity.start(requireContext()); } break; case R.id.ll_read_later: ReadLaterActivity.start(getContext()); break; + case R.id.ll_read_record: + ReadRecordActivity.start(getContext()); + break; case R.id.ll_about_me: AboutMeActivity.start(getContext()); break; @@ -345,35 +306,11 @@ protected void onClick2(View v) { } } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - switch (requestCode) { - default: - break; - case REQUEST_CODE_SELECT_USER_ICON: - String userIconPath = PictureSelectorUtils.forResult(resultCode, data); - if (!TextUtils.isEmpty(userIconPath)) { - UserInfoUtils.getInstance().setIcon(userIconPath); - UserInfoUtils.getInstance().setBg(userIconPath); - ImageLoader.userIcon(civ_user_icon, userIconPath); - ImageLoader.userBlur(iv_blur, userIconPath); - } - break; - case REQUEST_CODE_SELECT_BG: - String bgPath = PictureSelectorUtils.forResult(resultCode, data); - if (!TextUtils.isEmpty(bgPath)) { - UserInfoUtils.getInstance().setBg(bgPath); - ImageLoader.userBlur(iv_blur, bgPath); - } - break; - } - } - @Override public void getUserInfoSuccess(int code, UserInfoBean data) { mSmartRefreshUtils.success(); tv_coin.setText(data.getCoinCount() + ""); + ll_user_level_ranking.setVisibility(View.VISIBLE); tv_user_level.setText(data.getLevel() + ""); tv_user_ranking.setText(data.getRank() + ""); } @@ -385,4 +322,20 @@ public void getUserInfoFail(int code, String msg) { tv_user_level.setText("--"); tv_user_ranking.setText("--"); } + + @Override + public void getMessageUnreadCountSuccess(int count) { + MessageCountEvent.post(count); + if (count > 0) { + tv_notification.setVisibility(View.VISIBLE); + if (count > Config.NOTIFICATION_MAX_SHOW_COUNT) { + tv_notification.setText(Config.NOTIFICATION_MAX_SHOW_COUNT + "+"); + } else { + tv_notification.setText(count + ""); + } + } else { + tv_notification.setVisibility(View.GONE); + } + } + } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/model/AboutMeBean.java b/app/src/main/java/per/goweii/wanandroid/module/mine/model/AboutMeBean.java index 037f5ca6..85e2c21d 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/model/AboutMeBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/model/AboutMeBean.java @@ -5,8 +5,6 @@ /** * @author CuiZhen * @date 2019/5/23 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class AboutMeBean extends BaseBean { @@ -18,8 +16,8 @@ public class AboutMeBean extends BaseBean { * jianshu : https://www.jianshu.com/u/78fecab193fa * qq : 302833254 * qq_group : 147715512 - * qq_qrcode : https://raw.githubusercontent.com/goweii/WanAndroidServer/master/about/qq_qrcode.png - * wx_qrcode : https://raw.githubusercontent.com/goweii/WanAndroidServer/master/about/wx_qrcode.png + * qq_qrcode : https://goweii.gitee.io/wanandroidserver/about/qq_qrcode.png + * wx_qrcode : https://goweii.gitee.io/wanandroidserver/about/wx_qrcode.png */ private String icon; @@ -32,6 +30,7 @@ public class AboutMeBean extends BaseBean { private String qq_group_key; private String qq_qrcode; private String wx_qrcode; + private String zfb_qrcode; public String getIcon() { return icon; @@ -112,4 +111,12 @@ public String getWx_qrcode() { public void setWx_qrcode(String wx_qrcode) { this.wx_qrcode = wx_qrcode; } + + public String getZfb_qrcode() { + return zfb_qrcode; + } + + public void setZfb_qrcode(String zfb_qrcode) { + this.zfb_qrcode = zfb_qrcode; + } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/model/CoinRankBean.java b/app/src/main/java/per/goweii/wanandroid/module/mine/model/CoinRankBean.java index 9e34eaad..5ebf8f7e 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/model/CoinRankBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/model/CoinRankBean.java @@ -8,8 +8,6 @@ /** * @author CuiZhen * @date 2019/8/31 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CoinRankBean extends BaseBean { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/model/CoinRecordBean.java b/app/src/main/java/per/goweii/wanandroid/module/mine/model/CoinRecordBean.java index 5320c8b7..fae2ff67 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/model/CoinRecordBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/model/CoinRecordBean.java @@ -7,8 +7,6 @@ /** * @author CuiZhen * @date 2019/8/31 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CoinRecordBean extends BaseBean { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/model/HostEntity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/model/HostEntity.java index afbd76fa..b69fded7 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/model/HostEntity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/model/HostEntity.java @@ -9,8 +9,6 @@ /** * @author CuiZhen * @date 2019/10/13 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class HostEntity extends BaseEntity { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/model/MessageBean.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/model/MessageBean.kt new file mode 100644 index 00000000..6e8833bb --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/model/MessageBean.kt @@ -0,0 +1,36 @@ +package per.goweii.wanandroid.module.mine.model + +import android.annotation.SuppressLint +import android.net.Uri +import java.text.SimpleDateFormat +import java.util.* + +data class MessageBean( + val category: Int, + val date: Long, + val fromUser: String, + val fromUserId: Int, + val fullLink: String, + val id: Int, + val isRead: Int, + val link: String, + val message: String, + val niceDate: String, + val tag: String, + val title: String, + val userId: Int +) { + val realLink: String + @SuppressLint("SimpleDateFormat") + get() = Uri.parse(fullLink) + .buildUpon() + .appendQueryParameter("fid", fromUserId.toString()) + .appendQueryParameter("date", SimpleDateFormat("yyyy_MM_dd_HH_mm_ss").format(Date(date))) + .appendQueryParameter("message", message) + .appendQueryParameter("scrollToKeywords", message + .substring(0, message.length.coerceAtMost(20)) + .split(Regex("[ ]")) + .joinToString(",")) + .build() + .toString() +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/model/MineRequest.java b/app/src/main/java/per/goweii/wanandroid/module/mine/model/MineRequest.java index bf929840..2782176f 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/model/MineRequest.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/model/MineRequest.java @@ -1,26 +1,26 @@ package per.goweii.wanandroid.module.mine.model; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; import java.util.List; import io.reactivex.disposables.Disposable; import per.goweii.rxhttp.core.RxLife; import per.goweii.rxhttp.request.base.BaseBean; +import per.goweii.rxhttp.request.exception.ExceptionHandle; import per.goweii.wanandroid.http.BaseRequest; import per.goweii.wanandroid.http.RequestListener; import per.goweii.wanandroid.http.WanApi; import per.goweii.wanandroid.http.WanCache; import per.goweii.wanandroid.module.main.model.ArticleListBean; import per.goweii.wanandroid.module.main.model.CollectionLinkBean; +import per.goweii.wanandroid.module.main.model.ListBean; import per.goweii.wanandroid.module.main.model.UserPageBean; /** * @author CuiZhen * @date 2019/5/16 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class MineRequest extends BaseRequest { @@ -46,8 +46,35 @@ public static void getCollectArticleList(RxLife rxLife, boolean removeAndRefresh } } + public static void updateCollectArticleList(RxLife rxLife, @IntRange(from = 0) int page) { + netBean(rxLife, + WanApi.api().getCollectArticleList(page), + WanCache.CacheKey.COLLECT_ARTICLE_LIST(page), + new RequestListener() { + @Override + public void onStart() { + } + + @Override + public void onSuccess(int code, ArticleListBean data) { + } + + @Override + public void onFailed(int code, String msg) { + } + + @Override + public void onError(ExceptionHandle handle) { + } + + @Override + public void onFinish() { + } + }); + } + public static void getCollectLinkListCache(@NonNull RequestListener> listener) { - cacheList(WanCache.CacheKey.COLLECT_LINK_LIST, CollectionLinkBean.class, listener); + cacheList(WanCache.CacheKey.COLLECT_LINK_LIST(), CollectionLinkBean.class, listener); } public static void getCollectLinkListNet(RxLife rxLife, @NonNull RequestListener> listener) { @@ -58,11 +85,38 @@ public static void getCollectLinkList(RxLife rxLife, boolean removeAndRefresh, @ cacheAndNetList(rxLife, WanApi.api().getCollectLinkList(), removeAndRefresh, - WanCache.CacheKey.COLLECT_LINK_LIST, + WanCache.CacheKey.COLLECT_LINK_LIST(), CollectionLinkBean.class, listener); } + public static void updateCollectLinkList(RxLife rxLife) { + netList(rxLife, + WanApi.api().getCollectLinkList(), + WanCache.CacheKey.COLLECT_LINK_LIST(), + new RequestListener>() { + @Override + public void onStart() { + } + + @Override + public void onSuccess(int code, List data) { + } + + @Override + public void onFailed(int code, String msg) { + } + + @Override + public void onError(ExceptionHandle handle) { + } + + @Override + public void onFinish() { + } + }); + } + public static Disposable updateCollectLink(int id, String name, String link, @NonNull RequestListener listener) { return request(WanApi.api().updateCollectLink(id, name, link), listener); } @@ -87,6 +141,22 @@ public static Disposable getUserInfo(@NonNull RequestListener list return request(WanApi.api().getUserInfo(), listener); } + public static Disposable getMessageUnreadCount(@NonNull RequestListener listener) { + return request(WanApi.api().getMessageUnreadCount(), listener); + } + + public static Disposable getMessageUnreadList(int page, @NonNull RequestListener> listener) { + return request(WanApi.api().getMessageUnreadList(page), listener); + } + + public static Disposable getMessageReadList(int page, @NonNull RequestListener> listener) { + return request(WanApi.api().getMessageReadList(page), listener); + } + + public static Disposable deleteMessage(int id, @NonNull RequestListener listener) { + return request(WanApi.api().deleteMessage(id), listener); + } + public static Disposable getCoinRecordList(int page, @NonNull RequestListener listener) { return request(WanApi.api().getCoinRecordList(page), listener); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/model/NotificationBean.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/model/NotificationBean.kt new file mode 100644 index 00000000..4b2dde5d --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/model/NotificationBean.kt @@ -0,0 +1,15 @@ +package per.goweii.wanandroid.module.mine.model + +/** + * @author CuiZhen + * @date 2020/5/16 + */ +data class NotificationBean( + val tags: List, + val aniceDate: String, + val fromUser: String, + val articleContent: String, + val content: String, + val articleUrl: String, + val deleteUrl: String +) \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/model/OpenEntity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/model/OpenEntity.java index c9344aed..5d5fa04e 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/model/OpenEntity.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/model/OpenEntity.java @@ -5,8 +5,6 @@ /** * @author CuiZhen * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class OpenEntity extends BaseEntity { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/model/ReadLaterEntity.java b/app/src/main/java/per/goweii/wanandroid/module/mine/model/ReadLaterEntity.java deleted file mode 100644 index 8725d4b9..00000000 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/model/ReadLaterEntity.java +++ /dev/null @@ -1,50 +0,0 @@ -package per.goweii.wanandroid.module.mine.model; - -import io.realm.RealmObject; - -/** - * @author CuiZhen - * @date 2019/5/26 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public class ReadLaterEntity extends RealmObject { - - private int userId; - private String title; - private String link; - private long time; - - public int getUserId() { - return userId; - } - - public void setUserId(int userId) { - this.userId = userId; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getLink() { - return link; - } - - public void setLink(String link) { - this.link = link; - } - - public long getTime() { - return time; - } - - public void setTime(long time) { - this.time = time; - } -} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/model/UserInfoBean.java b/app/src/main/java/per/goweii/wanandroid/module/mine/model/UserInfoBean.java index f469ba9f..0c76b0e0 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/model/UserInfoBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/model/UserInfoBean.java @@ -5,8 +5,6 @@ /** * @author CuiZhen * @date 2019/11/9 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class UserInfoBean extends BaseBean { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/AboutMePresenter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/AboutMePresenter.java index 6ce9c212..b0042935 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/AboutMePresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/AboutMePresenter.java @@ -11,6 +11,7 @@ import per.goweii.basic.utils.CopyUtils; import per.goweii.basic.utils.bitmap.BitmapUtils; import per.goweii.basic.utils.listener.SimpleCallback; +import per.goweii.wanandroid.R; import per.goweii.wanandroid.http.RequestCallback; import per.goweii.wanandroid.module.mine.model.AboutMeBean; import per.goweii.wanandroid.module.mine.model.MineRequest; @@ -19,15 +20,13 @@ /** * @author CuiZhen * @date 2019/5/23 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class AboutMePresenter extends BasePresenter { private AboutMeBean mAboutMeBean = null; - public void getAboutMe(){ + public void getAboutMe() { MineRequest.getAboutMe(getRxLife(), new RequestCallback() { @Override public void onSuccess(int code, AboutMeBean data) { @@ -56,7 +55,7 @@ public void openQQChat() { } } CopyUtils.copyText(mAboutMeBean.getQq()); - ToastMaker.showShort("QQ已复制"); + ToastMaker.showShort(getContext().getString(R.string.copied)); } public void openQQGroup() { @@ -71,7 +70,7 @@ public void openQQGroup() { } } CopyUtils.copyText(mAboutMeBean.getQq_group()); - ToastMaker.showShort("QQ群已复制"); + ToastMaker.showShort(getContext().getString(R.string.copied)); } public void saveQQQrcode() { @@ -79,14 +78,36 @@ public void saveQQQrcode() { return; } GlideHelper.with(getContext()) + .asBitmap() .load(mAboutMeBean.getQq_qrcode()) - .get(new SimpleCallback() { + .getBitmap(new SimpleCallback() { @Override public void onResult(Bitmap data) { - if (null != BitmapUtils.saveGallery(data, mAboutMeBean.getName() + "_qq_qrcode_" + System.currentTimeMillis())) { - ToastMaker.showShort("保存成功"); + if (BitmapUtils.saveGallery(data, mAboutMeBean.getName() + "_qq_qrcode_" + System.currentTimeMillis())) { + ToastMaker.showShort(getContext().getString(R.string.save_successful)); + AppOpenUtils.openQQ(getContext()); } else { - ToastMaker.showShort("保存失败"); + ToastMaker.showShort(getContext().getString(R.string.save_failed)); + } + } + }); + } + + public void saveZFBQrcode() { + if (mAboutMeBean == null) { + return; + } + GlideHelper.with(getContext()) + .asBitmap() + .load(mAboutMeBean.getZfb_qrcode()) + .getBitmap(new SimpleCallback() { + @Override + public void onResult(Bitmap data) { + if (BitmapUtils.saveGallery(data, mAboutMeBean.getName() + "_qq_qrcode_" + System.currentTimeMillis())) { + ToastMaker.showShort(getContext().getString(R.string.save_successful)); + AppOpenUtils.openZFBScan(getContext()); + } else { + ToastMaker.showShort(getContext().getString(R.string.save_failed)); } } }); @@ -97,14 +118,16 @@ public void saveWXQrcode() { return; } GlideHelper.with(getContext()) + .asBitmap() .load(mAboutMeBean.getWx_qrcode()) - .get(new SimpleCallback() { + .getBitmap(new SimpleCallback() { @Override public void onResult(Bitmap data) { - if (null != BitmapUtils.saveGallery(data, mAboutMeBean.getName() + "_wx_qrcode_" + System.currentTimeMillis())) { - ToastMaker.showShort("保存成功"); + if (BitmapUtils.saveGallery(data, mAboutMeBean.getName() + "_wx_qrcode_" + System.currentTimeMillis())) { + ToastMaker.showShort(getContext().getString(R.string.save_successful)); + AppOpenUtils.openWechatQRCode(getContext()); } else { - ToastMaker.showShort("保存失败"); + ToastMaker.showShort(getContext().getString(R.string.save_failed)); } } }); diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/AboutPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/AboutPresenter.java new file mode 100644 index 00000000..c99365e3 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/AboutPresenter.java @@ -0,0 +1,79 @@ +package per.goweii.wanandroid.module.mine.presenter; + +import per.goweii.basic.core.base.BasePresenter; +import per.goweii.rxhttp.request.exception.ExceptionHandle; +import per.goweii.wanandroid.http.RequestListener; +import per.goweii.wanandroid.module.main.model.MainRequest; +import per.goweii.wanandroid.module.main.model.UpdateBean; +import per.goweii.wanandroid.module.mine.view.AboutView; + +/** + * @author CuiZhen + * @date 2019/5/19 + * GitHub: https://github.com/goweii + */ +public class AboutPresenter extends BasePresenter { + public void update(int updateType) { + MainRequest.update(getRxLife(), new RequestListener() { + @Override + public void onStart() { + showLoadingBar(); + } + + @Override + public void onSuccess(int code, UpdateBean data) { + if (isAttach()) { + getBaseView().updateSuccess(code, data, updateType); + } + } + + @Override + public void onFailed(int code, String msg) { + if (isAttach()) { + getBaseView().updateFailed(code, msg, updateType); + } + } + + @Override + public void onError(ExceptionHandle handle) { + } + + @Override + public void onFinish() { + dismissLoadingBar(); + } + }); + } + + public void betaUpdate(int updateType) { + MainRequest.betaUpdate(getRxLife(), new RequestListener() { + @Override + public void onStart() { + showLoadingBar(); + } + + @Override + public void onSuccess(int code, UpdateBean data) { + if (isAttach()) { + getBaseView().betaUpdateSuccess(code, data, updateType); + } + } + + @Override + public void onFailed(int code, String msg) { + if (isAttach()) { + getBaseView().betaUpdateFailed(code, msg, updateType); + } + } + + @Override + public void onError(ExceptionHandle handle) { + } + + @Override + public void onFinish() { + dismissLoadingBar(); + } + }); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CoinPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CoinPresenter.java index ccfd7f5a..2a081ee7 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CoinPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CoinPresenter.java @@ -10,8 +10,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CoinPresenter extends BasePresenter { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CoinRankPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CoinRankPresenter.java index 8f0eb049..99c55c14 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CoinRankPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CoinRankPresenter.java @@ -10,8 +10,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CoinRankPresenter extends BasePresenter { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CollectionArticlePresenter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CollectionArticlePresenter.java index 18b4bfc3..8b9870c1 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CollectionArticlePresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CollectionArticlePresenter.java @@ -15,8 +15,6 @@ /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CollectionArticlePresenter extends BasePresenter { @@ -78,8 +76,12 @@ public void onFinish() { }); } - public void uncollect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.uncollect(item.getId(), item.getOriginId(), new RequestListener() { + public void updateCollectArticleList(int page) { + MineRequest.updateCollectArticleList(getRxLife(), page); + } + + public void uncollectArticle(ArticleBean item, final CollectView v) { + addToRxLife(MainRequest.uncollectArticle(item.getId(), item.getOriginId(), new RequestListener() { @Override public void onStart() { } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CollectionLinkPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CollectionLinkPresenter.java index a5de70dc..b214f5c9 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CollectionLinkPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/CollectionLinkPresenter.java @@ -16,8 +16,6 @@ /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CollectionLinkPresenter extends BasePresenter { @@ -79,6 +77,10 @@ public void onFinish() { }); } + public void updateCollectLinkList() { + MineRequest.updateCollectLinkList(getRxLife()); + } + public void uncollectLink(CollectionLinkBean item) { addToRxLife(MainRequest.uncollectLink(item.getId(), new RequestListener() { @Override diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MessageReadedPresenter.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MessageReadedPresenter.kt new file mode 100644 index 00000000..9ef384d8 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MessageReadedPresenter.kt @@ -0,0 +1,67 @@ +package per.goweii.wanandroid.module.mine.presenter + +import com.google.gson.stream.MalformedJsonException +import per.goweii.basic.core.base.BasePresenter +import per.goweii.rxhttp.request.exception.ExceptionHandle +import per.goweii.wanandroid.http.RequestListener +import per.goweii.wanandroid.http.WanApi +import per.goweii.wanandroid.module.main.model.ListBean +import per.goweii.wanandroid.module.mine.model.MessageBean +import per.goweii.wanandroid.module.mine.model.MineRequest +import per.goweii.wanandroid.module.mine.view.MessageReadedView + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +class MessageReadedPresenter : BasePresenter() { + + fun getMessageReadList(page: Int) { + addToRxLife(MineRequest.getMessageReadList(page, object : RequestListener> { + override fun onStart() {} + override fun onSuccess(code: Int, data: ListBean) { + if (isAttach) { + baseView.getMessageReadListSuccess(code, data) + } + } + + override fun onFailed(code: Int, msg: String) { + if (isAttach) { + baseView.getMessageReadListFail(code, msg) + } + } + + override fun onError(handle: ExceptionHandle) {} + override fun onFinish() {} + })) + } + + fun delete(item: MessageBean) { + addToRxLife(MineRequest.deleteMessage(item.id, object : RequestListener { + override fun onStart() {} + override fun onSuccess(code: Int, data: Any?) { + if (isAttach) { + baseView.deleteMessageSuccess(code, item) + } + } + + override fun onFailed(code: Int, msg: String) { + if (isAttach) { + baseView.deleteMessageFail(code, msg) + } + } + + override fun onError(handle: ExceptionHandle) { + if (handle.exception is MalformedJsonException) { + if (isAttach) { + baseView.deleteMessageSuccess(WanApi.ApiCode.SUCCESS, item) + } + } + } + + override fun onFinish() {} + })) + } + +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MessageUnreadPresenter.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MessageUnreadPresenter.kt new file mode 100644 index 00000000..86025149 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MessageUnreadPresenter.kt @@ -0,0 +1,40 @@ +package per.goweii.wanandroid.module.mine.presenter + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.MainScope +import per.goweii.basic.core.base.BasePresenter +import per.goweii.rxhttp.request.exception.ExceptionHandle +import per.goweii.wanandroid.http.RequestListener +import per.goweii.wanandroid.module.main.model.ListBean +import per.goweii.wanandroid.module.mine.model.MessageBean +import per.goweii.wanandroid.module.mine.model.MineRequest +import per.goweii.wanandroid.module.mine.view.MessageUnreadView + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +class MessageUnreadPresenter : BasePresenter(), CoroutineScope by MainScope() { + + fun getMessageUnreadList(page: Int) { + addToRxLife(MineRequest.getMessageUnreadList(page, object : RequestListener> { + override fun onStart() {} + override fun onSuccess(code: Int, data: ListBean) { + if (isAttach) { + baseView.getMessageUnreadListSuccess(code, data) + } + } + + override fun onFailed(code: Int, msg: String) { + if (isAttach) { + baseView.getMessageUnreadListFail(code, msg) + } + } + + override fun onError(handle: ExceptionHandle) {} + override fun onFinish() {} + })) + } + +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MinePresenter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MinePresenter.java index 9b74d722..8b8c1475 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MinePresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MinePresenter.java @@ -1,21 +1,42 @@ package per.goweii.wanandroid.module.mine.presenter; +import android.text.TextUtils; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import io.reactivex.Observable; +import io.reactivex.ObservableEmitter; +import io.reactivex.ObservableOnSubscribe; +import io.reactivex.Observer; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; +import okhttp3.Cookie; import per.goweii.basic.core.base.BasePresenter; import per.goweii.rxhttp.request.exception.ExceptionHandle; import per.goweii.wanandroid.http.RequestListener; import per.goweii.wanandroid.module.mine.model.MineRequest; import per.goweii.wanandroid.module.mine.model.UserInfoBean; import per.goweii.wanandroid.module.mine.view.MineView; +import per.goweii.wanandroid.utils.CookieUtils; +import per.goweii.wanandroid.utils.UserUtils; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class MinePresenter extends BasePresenter { + public UserInfoBean mUserInfoBean = null; + public void getUserInfo() { addToRxLife(MineRequest.getUserInfo(new RequestListener() { @Override @@ -24,6 +45,7 @@ public void onStart() { @Override public void onSuccess(int code, UserInfoBean data) { + mUserInfoBean = data; if (isAttach()) { getBaseView().getUserInfoSuccess(code, data); } @@ -45,4 +67,118 @@ public void onFinish() { } })); } + + public void getMessageUnreadCount() { + if (!UserUtils.getInstance().isLogin()) { + if (isAttach()) { + getBaseView().getMessageUnreadCountSuccess(0); + } + return; + } + addToRxLife(MineRequest.getMessageUnreadCount(new RequestListener() { + @Override + public void onStart() { + } + + @Override + public void onSuccess(int code, Integer data) { + if (isAttach()) { + getBaseView().getMessageUnreadCountSuccess(data); + } + } + + @Override + public void onFailed(int code, String msg) { + if (isAttach()) { + getBaseView().getMessageUnreadCountSuccess(0); + } + } + + @Override + public void onError(ExceptionHandle handle) { + } + + @Override + public void onFinish() { + } + })); + } + + public void getNotificationCount() { + if (!UserUtils.getInstance().isLogin()) { + if (isAttach()) { + getBaseView().getMessageUnreadCountSuccess(0); + } + return; + } + Observable.create(new ObservableOnSubscribe() { + @Override + public void subscribe(ObservableEmitter emitter) throws Exception { + String url = "https://wanandroid.com/"; + List cookies = CookieUtils.INSTANCE.loadForUrl(url); + if (cookies == null || cookies.isEmpty()) { + emitter.onNext(0); + emitter.onComplete(); + return; + } + Map map = new HashMap<>(cookies.size()); + for (Cookie cookie : cookies) { + map.put(cookie.name(), cookie.value()); + } + Document document = Jsoup.connect("https://wanandroid.com/") + .cookies(map) + .get(); + String num = null; + try { + num = document.getElementsByClass("header_inbox").get(0) + .getElementsByTag("a").get(0) + .getElementsByTag("i").get(0) + .ownText(); + } catch (Exception ignore) { + } + if (TextUtils.isEmpty(num)) { + try { + Pattern pattern = Pattern.compile("你有([0-9]*)条未读消息"); + String text = document.getElementsByClass("lead_list").get(0) + .getElementsMatchingOwnText(pattern).get(0) + .ownText(); + Matcher matcher = pattern.matcher(text); + if (matcher.find()) { + num = matcher.group(1); + } + } catch (Exception ignore) { + } + } + int count = 0; + if (!TextUtils.isEmpty(num)) { + count = Integer.parseInt(num); + } + emitter.onNext(count); + emitter.onComplete(); + } + }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer() { + @Override + public void onSubscribe(Disposable d) { + addToRxLife(d); + } + + @Override + public void onNext(Integer integer) { + if (isAttach()) { + getBaseView().getMessageUnreadCountSuccess(integer); + } + } + + @Override + public void onError(Throwable e) { + if (isAttach()) { + getBaseView().getMessageUnreadCountSuccess(0); + } + } + + @Override + public void onComplete() { + } + }); + } } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MineSharePresenter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MineSharePresenter.java index cbe1a0e9..274bb2e5 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MineSharePresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/MineSharePresenter.java @@ -17,8 +17,6 @@ /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class MineSharePresenter extends BasePresenter { @@ -80,7 +78,7 @@ public void onFinish() { } public void collect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.collect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.collectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } @@ -112,7 +110,7 @@ public void onFinish() { } public void uncollect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.uncollect(item.getId(), item.getOriginId(), new RequestListener() { + addToRxLife(MainRequest.uncollectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/ReadLaterPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/ReadLaterPresenter.java new file mode 100644 index 00000000..4a43d9ea --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/ReadLaterPresenter.java @@ -0,0 +1,89 @@ +package per.goweii.wanandroid.module.mine.presenter; + +import java.util.List; + +import per.goweii.basic.core.base.BasePresenter; +import per.goweii.basic.utils.listener.SimpleCallback; +import per.goweii.basic.utils.listener.SimpleListener; +import per.goweii.wanandroid.db.executor.ReadLaterExecutor; +import per.goweii.wanandroid.db.model.ReadLaterModel; +import per.goweii.wanandroid.module.mine.view.ReadLaterView; + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +public class ReadLaterPresenter extends BasePresenter { + + private ReadLaterExecutor mReadLaterExecutor = null; + + @Override + public void attach(ReadLaterView baseView) { + super.attach(baseView); + mReadLaterExecutor = new ReadLaterExecutor(); + } + + @Override + public void detach() { + if (mReadLaterExecutor != null) mReadLaterExecutor.destroy(); + super.detach(); + } + + public void getList(int offset, int perPageCount) { + if (mReadLaterExecutor == null) return; + mReadLaterExecutor.getList(offset, perPageCount, new SimpleCallback>() { + @Override + public void onResult(List data) { + if (isAttach()) { + getBaseView().getReadLaterListSuccess(data); + } + } + }, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + getBaseView().getReadLaterListFailed(); + } + } + }); + } + + public void remove(String link) { + if (mReadLaterExecutor == null) return; + mReadLaterExecutor.remove(link, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + getBaseView().removeReadLaterSuccess(link); + } + } + }, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + getBaseView().removeReadLaterFailed(); + } + } + }); + } + + public void removeAll() { + if (mReadLaterExecutor == null) return; + mReadLaterExecutor.removeAll(new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + getBaseView().removeAllReadLaterSuccess(); + } + } + }, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + getBaseView().removeAllReadLaterFailed(); + } + } + }); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/ReadRecordPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/ReadRecordPresenter.java new file mode 100644 index 00000000..468cd98a --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/ReadRecordPresenter.java @@ -0,0 +1,89 @@ +package per.goweii.wanandroid.module.mine.presenter; + +import java.util.List; + +import per.goweii.basic.core.base.BasePresenter; +import per.goweii.basic.utils.listener.SimpleCallback; +import per.goweii.basic.utils.listener.SimpleListener; +import per.goweii.wanandroid.db.executor.ReadRecordExecutor; +import per.goweii.wanandroid.db.model.ReadRecordModel; +import per.goweii.wanandroid.module.mine.view.ReadRecordView; + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +public class ReadRecordPresenter extends BasePresenter { + + private ReadRecordExecutor mReadRecordExecutor = null; + + @Override + public void attach(ReadRecordView baseView) { + super.attach(baseView); + mReadRecordExecutor = new ReadRecordExecutor(); + } + + @Override + public void detach() { + if (mReadRecordExecutor != null) mReadRecordExecutor.destroy(); + super.detach(); + } + + public void getList(int offset, int perPageCount) { + if (mReadRecordExecutor == null) return; + mReadRecordExecutor.getList(offset, perPageCount, new SimpleCallback>() { + @Override + public void onResult(List data) { + if (isAttach()) { + getBaseView().getReadRecordListSuccess(data); + } + } + }, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + getBaseView().getReadRecordListFailed(); + } + } + }); + } + + public void remove(String link) { + if (mReadRecordExecutor == null) return; + mReadRecordExecutor.remove(link, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + getBaseView().removeReadRecordSuccess(link); + } + } + }, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + getBaseView().removeReadRecordFailed(); + } + } + }); + } + + public void removeAll() { + if (mReadRecordExecutor == null) return; + mReadRecordExecutor.removeAll(new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + getBaseView().removeAllReadRecordSuccess(); + } + } + }, new SimpleListener() { + @Override + public void onResult() { + if (isAttach()) { + getBaseView().removeAllReadRecordFailed(); + } + } + }); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/SettingPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/SettingPresenter.java index 4178bd5b..802128a7 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/SettingPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/presenter/SettingPresenter.java @@ -13,52 +13,15 @@ import per.goweii.rxhttp.request.exception.ExceptionHandle; import per.goweii.wanandroid.http.RequestListener; import per.goweii.wanandroid.http.WanCache; -import per.goweii.wanandroid.module.main.model.MainRequest; -import per.goweii.wanandroid.module.main.model.UpdateBean; import per.goweii.wanandroid.module.mine.model.MineRequest; import per.goweii.wanandroid.module.mine.view.SettingView; /** * @author CuiZhen * @date 2019/5/19 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class SettingPresenter extends BasePresenter { - - public void update(boolean click) { - MainRequest.update(getRxLife(), new RequestListener() { - @Override - public void onStart() { - showLoadingBar(); - } - - @Override - public void onSuccess(int code, UpdateBean data) { - if (isAttach()) { - getBaseView().updateSuccess(code, data, click); - } - } - - @Override - public void onFailed(int code, String msg) { - if (isAttach()) { - getBaseView().updateFailed(code, msg, click); - } - } - - @Override - public void onError(ExceptionHandle handle) { - } - - @Override - public void onFinish() { - dismissLoadingBar(); - } - }); - } - public void logout() { addToRxLife(MineRequest.logout(new RequestListener() { @Override diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/AboutMeView.java b/app/src/main/java/per/goweii/wanandroid/module/mine/view/AboutMeView.java index f28a4fa0..35822641 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/view/AboutMeView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/AboutMeView.java @@ -6,11 +6,10 @@ /** * @author CuiZhen * @date 2019/5/23 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface AboutMeView extends BaseView { void getAboutMeSuccess(int code, AboutMeBean data); + void getAboutMeFailed(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/AboutView.java b/app/src/main/java/per/goweii/wanandroid/module/mine/view/AboutView.java new file mode 100644 index 00000000..883a082f --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/AboutView.java @@ -0,0 +1,19 @@ +package per.goweii.wanandroid.module.mine.view; + +import per.goweii.basic.core.base.BaseView; +import per.goweii.wanandroid.module.main.model.UpdateBean; + +/** + * @author CuiZhen + * @date 2019/5/19 + * GitHub: https://github.com/goweii + */ +public interface AboutView extends BaseView { + void updateSuccess(int code, UpdateBean data, int updateType); + + void updateFailed(int code, String msg, int updateType); + + void betaUpdateSuccess(int code, UpdateBean data, int updateType); + + void betaUpdateFailed(int code, String msg, int updateType); +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/CoinRankView.java b/app/src/main/java/per/goweii/wanandroid/module/mine/view/CoinRankView.java index 8a00eb99..15ad6b59 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/view/CoinRankView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/CoinRankView.java @@ -6,8 +6,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface CoinRankView extends BaseView { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/CoinView.java b/app/src/main/java/per/goweii/wanandroid/module/mine/view/CoinView.java index 491ecd0a..c3791777 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/view/CoinView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/CoinView.java @@ -6,8 +6,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface CoinView extends BaseView { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/CollectionArticleView.java b/app/src/main/java/per/goweii/wanandroid/module/mine/view/CollectionArticleView.java index dd7f886c..c9988449 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/view/CollectionArticleView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/CollectionArticleView.java @@ -6,11 +6,10 @@ /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface CollectionArticleView extends BaseView { void getCollectArticleListSuccess(int code, ArticleListBean data); + void getCollectArticleListFailed(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/CollectionLinkView.java b/app/src/main/java/per/goweii/wanandroid/module/mine/view/CollectionLinkView.java index b0fd11c7..4ff3ddcf 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/view/CollectionLinkView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/CollectionLinkView.java @@ -8,12 +8,11 @@ /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface CollectionLinkView extends BaseView { void getCollectLinkListSuccess(int code, List data); + void getCollectLinkListFailed(int code, String msg); void updateCollectLinkSuccess(int code, CollectionLinkBean data); diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/MessageReadedView.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/view/MessageReadedView.kt new file mode 100644 index 00000000..7a55ac98 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/MessageReadedView.kt @@ -0,0 +1,17 @@ +package per.goweii.wanandroid.module.mine.view + +import per.goweii.basic.core.base.BaseView +import per.goweii.wanandroid.module.main.model.ListBean +import per.goweii.wanandroid.module.mine.model.MessageBean + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +interface MessageReadedView : BaseView { + fun getMessageReadListSuccess(code: Int, data: ListBean) + fun getMessageReadListFail(code: Int, msg: String) + fun deleteMessageSuccess(code: Int, data: MessageBean) + fun deleteMessageFail(code: Int, msg: String) +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/MessageUnreadView.kt b/app/src/main/java/per/goweii/wanandroid/module/mine/view/MessageUnreadView.kt new file mode 100644 index 00000000..efd1f03e --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/MessageUnreadView.kt @@ -0,0 +1,15 @@ +package per.goweii.wanandroid.module.mine.view + +import per.goweii.basic.core.base.BaseView +import per.goweii.wanandroid.module.main.model.ListBean +import per.goweii.wanandroid.module.mine.model.MessageBean + +/** + * @author CuiZhen + * @date 2019/5/17 + * GitHub: https://github.com/goweii + */ +interface MessageUnreadView : BaseView { + fun getMessageUnreadListSuccess(code: Int, data: ListBean) + fun getMessageUnreadListFail(code: Int, msg: String) +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/MineShareView.java b/app/src/main/java/per/goweii/wanandroid/module/mine/view/MineShareView.java index e8a5bdcb..cfb42f53 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/view/MineShareView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/MineShareView.java @@ -6,8 +6,6 @@ /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface MineShareView extends BaseView { diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/MineView.java b/app/src/main/java/per/goweii/wanandroid/module/mine/view/MineView.java index c3d202de..616460ab 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/view/MineView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/MineView.java @@ -6,12 +6,12 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface MineView extends BaseView { void getUserInfoSuccess(int code, UserInfoBean coin); void getUserInfoFail(int code, String msg); + + void getMessageUnreadCountSuccess(int count); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/ReadLaterView.java b/app/src/main/java/per/goweii/wanandroid/module/mine/view/ReadLaterView.java new file mode 100644 index 00000000..910a04d4 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/ReadLaterView.java @@ -0,0 +1,25 @@ +package per.goweii.wanandroid.module.mine.view; + +import java.util.List; + +import per.goweii.basic.core.base.BaseView; +import per.goweii.wanandroid.db.model.ReadLaterModel; + +/** + * @author CuiZhen + * @date 2019/5/23 + * GitHub: https://github.com/goweii + */ +public interface ReadLaterView extends BaseView { + void getReadLaterListSuccess(List list); + + void getReadLaterListFailed(); + + void removeReadLaterSuccess(String link); + + void removeReadLaterFailed(); + + void removeAllReadLaterSuccess(); + + void removeAllReadLaterFailed(); +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/ReadRecordView.java b/app/src/main/java/per/goweii/wanandroid/module/mine/view/ReadRecordView.java new file mode 100644 index 00000000..5430599e --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/ReadRecordView.java @@ -0,0 +1,25 @@ +package per.goweii.wanandroid.module.mine.view; + +import java.util.List; + +import per.goweii.basic.core.base.BaseView; +import per.goweii.wanandroid.db.model.ReadRecordModel; + +/** + * @author CuiZhen + * @date 2019/5/23 + * GitHub: https://github.com/goweii + */ +public interface ReadRecordView extends BaseView { + void getReadRecordListSuccess(List list); + + void getReadRecordListFailed(); + + void removeReadRecordSuccess(String link); + + void removeReadRecordFailed(); + + void removeAllReadRecordSuccess(); + + void removeAllReadRecordFailed(); +} diff --git a/app/src/main/java/per/goweii/wanandroid/module/mine/view/SettingView.java b/app/src/main/java/per/goweii/wanandroid/module/mine/view/SettingView.java index 3582d888..b3fd5cae 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/mine/view/SettingView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/mine/view/SettingView.java @@ -2,20 +2,15 @@ import per.goweii.basic.core.base.BaseView; import per.goweii.rxhttp.request.base.BaseBean; -import per.goweii.wanandroid.module.main.model.UpdateBean; /** * @author CuiZhen * @date 2019/5/19 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface SettingView extends BaseView { - void updateSuccess(int code, UpdateBean data, boolean click); - void updateFailed(int code, String msg, boolean click); - void logoutSuccess(int code, BaseBean data); + void logoutFailed(int code, String msg); void getCacheSizeSuccess(String size); diff --git a/app/src/main/java/per/goweii/wanandroid/module/navigation/adapter/NaviAdapter.java b/app/src/main/java/per/goweii/wanandroid/module/navigation/adapter/NaviAdapter.java index cede6eaa..03998fcf 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/navigation/adapter/NaviAdapter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/navigation/adapter/NaviAdapter.java @@ -1,10 +1,12 @@ package per.goweii.wanandroid.module.navigation.adapter; -import android.support.annotation.NonNull; +import android.text.Html; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; +import androidx.annotation.NonNull; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.google.android.flexbox.FlexboxLayout; @@ -19,8 +21,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class NaviAdapter extends BaseQuickAdapter { @@ -44,7 +44,7 @@ protected void convert(BaseViewHolder helper, NaviBean item) { for (int i = 0; i < item.getArticles().size(); i++) { final ArticleBean childItem = item.getArticles().get(i); TextView child = createOrGetCacheFlexItemTextView(fbl); - child.setText(childItem.getTitle()); + child.setText(Html.fromHtml(childItem.getTitle())); int finalI = i; child.setOnClickListener(new View.OnClickListener() { @Override diff --git a/app/src/main/java/per/goweii/wanandroid/module/navigation/fragment/NaviFragment.java b/app/src/main/java/per/goweii/wanandroid/module/navigation/fragment/NaviFragment.java index 6faa97b1..82d12656 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/navigation/fragment/NaviFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/navigation/fragment/NaviFragment.java @@ -1,8 +1,12 @@ package per.goweii.wanandroid.module.navigation.fragment; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.kennyc.view.MultiStateView; @@ -13,8 +17,7 @@ import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.common.ScrollTop; -import per.goweii.wanandroid.module.main.activity.WebActivity; +import per.goweii.wanandroid.databinding.FragmentKnowledgeNavigationChildBinding; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.navigation.adapter.NaviAdapter; import per.goweii.wanandroid.module.navigation.model.NaviBean; @@ -22,15 +25,14 @@ import per.goweii.wanandroid.module.navigation.view.NaviView; import per.goweii.wanandroid.utils.MultiStateUtils; import per.goweii.wanandroid.utils.RvScrollTopUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class NaviFragment extends BaseFragment implements ScrollTop, NaviView { +public class NaviFragment extends BaseFragment implements RvScrollTopUtils.ScrollTop, NaviView { @BindView(R.id.msv) MultiStateView msv; @@ -43,9 +45,10 @@ public static NaviFragment create() { return new NaviFragment(); } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_knowledge_navigation_child; + protected FragmentKnowledgeNavigationChildBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentKnowledgeNavigationChildBinding.inflate(inflater, container, false); } @Nullable @@ -62,7 +65,7 @@ protected void initView() { mAdapter.setOnItemClickListener(new NaviAdapter.OnItemClickListener() { @Override public void onClick(ArticleBean bean, int pos) { - WebActivity.start(getContext(), bean); + UrlOpenUtils.Companion.with(bean).open(getContext()); } }); rv.setAdapter(mAdapter); diff --git a/app/src/main/java/per/goweii/wanandroid/module/navigation/model/NaviBean.java b/app/src/main/java/per/goweii/wanandroid/module/navigation/model/NaviBean.java index 94fb0d1d..d76261f7 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/navigation/model/NaviBean.java +++ b/app/src/main/java/per/goweii/wanandroid/module/navigation/model/NaviBean.java @@ -8,13 +8,11 @@ /** * @author CuiZhen * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class NaviBean extends BaseBean { /** - * articles : [{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1848,"link":"https://developers.google.cn/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515322795000,"superChapterId":0,"superChapterName":"","tags":[],"title":"Google开发者","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1849,"link":"http://www.github.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515322817000,"superChapterId":0,"superChapterName":"","tags":[],"title":"Github","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1850,"link":"https://stackoverflow.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515322829000,"superChapterId":0,"superChapterName":"","tags":[],"title":"stackoverflow","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1851,"link":"https://juejin.im/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323408000,"superChapterId":0,"superChapterName":"","tags":[],"title":"掘金","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1852,"link":"https://www.csdn.net/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323423000,"superChapterId":0,"superChapterName":"","tags":[],"title":"CSDN","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1853,"link":"https://www.jianshu.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323438000,"superChapterId":0,"superChapterName":"","tags":[],"title":"简书","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1854,"link":"http://www.androidweekly.cn/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323568000,"superChapterId":0,"superChapterName":"","tags":[],"title":"开发技术周报","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1855,"link":"https://toutiao.io/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323607000,"superChapterId":0,"superChapterName":"","tags":[],"title":"开发者头条","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1856,"link":"https://segmentfault.com/t/android","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323635000,"superChapterId":0,"superChapterName":"","tags":[],"title":"segmentfault","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1857,"link":"http://www.androiddevtools.cn/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323651000,"superChapterId":0,"superChapterName":"","tags":[],"title":"androiddevtools","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1858,"link":"https://developers.googleblog.cn/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323695000,"superChapterId":0,"superChapterName":"","tags":[],"title":"Google中文Blog","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"gank.io","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1859,"link":"http://gank.io/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323720000,"superChapterId":0,"superChapterName":"","tags":[],"title":"干货集中营","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1862,"link":"http://a.codekk.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515324437000,"superChapterId":0,"superChapterName":"","tags":[],"title":"CodeKK","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1863,"link":"https://xiaozhuanlan.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515324456000,"superChapterId":0,"superChapterName":"","tags":[],"title":"小专栏","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1864,"link":"http://www.wanandroid.com/article/list/0?cid=176","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515324541000,"superChapterId":0,"superChapterName":"","tags":[],"title":"国内大牛","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1865,"link":"https://github.com/android-cn/android-dev-com","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515324559000,"superChapterId":0,"superChapterName":"","tags":[],"title":"国外大牛","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1866,"link":"https://www.androidos.net.cn/sourcecode","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515324594000,"superChapterId":0,"superChapterName":"","tags":[],"title":"Android源码","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1867,"link":"http://design.1sters.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515324880000,"superChapterId":0,"superChapterName":"","tags":[],"title":"Material Design 中文版","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1868,"link":"https://leetcode.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515325010000,"superChapterId":0,"superChapterName":"","tags":[],"title":"leetcode","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":2405,"link":"https://dl.google.com/dl/android/maven2/index.html","niceDate":"2018-02-25","origin":"","prefix":"","projectLink":"","publishTime":1519537704000,"superChapterId":0,"superChapterName":"","tags":[],"title":"google mvn仓库","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":2406,"link":"http://jcenter.bintray.com/","niceDate":"2018-02-25","origin":"","prefix":"","projectLink":"","publishTime":1519537722000,"superChapterId":0,"superChapterName":"","tags":[],"title":"jcenter仓库","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":3469,"link":"https://mvnrepository.com/artifact/com.google.code.gson/gson","niceDate":"2018-10-10","origin":"","prefix":"","projectLink":"","publishTime":1539139799000,"superChapterId":0,"superChapterName":"","tags":[],"title":"maven仓库","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8104,"link":"https://maven.aliyun.com/mvn/search","niceDate":"2019-03-20","origin":"","prefix":"","projectLink":"","publishTime":1553095634000,"superChapterId":0,"superChapterName":"","tags":[],"title":"maven仓库 阿里云托管","type":0,"userId":-1,"visible":1,"zan":0}] + * articles : [{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1848,"link":"https://developers.google.cn/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515322795000,"superChapterId":0,"superChapterName":"","tags":[],"title":"Google开发者","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1849,"link":"http://www.github.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515322817000,"superChapterId":0,"superChapterName":"","tags":[],"title":"Github","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1850,"link":"https://stackoverflow.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515322829000,"superChapterId":0,"superChapterName":"","tags":[],"title":"stackoverflow","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1851,"link":"https://juejin.im/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323408000,"superChapterId":0,"superChapterName":"","tags":[],"title":"掘金","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1852,"link":"https://www.csdn.net/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323423000,"superChapterId":0,"superChapterName":"","tags":[],"title":"CSDN","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1853,"link":"https://www.jianshu.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323438000,"superChapterId":0,"superChapterName":"","tags":[],"title":"简书","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1854,"link":"http://www.androidweekly.cn/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323568000,"superChapterId":0,"superChapterName":"","tags":[],"title":"开发技术周报","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1855,"link":"https://toutiao.io/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323607000,"superChapterId":0,"superChapterName":"","tags":[],"title":"开发者头条","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1856,"link":"https://segmentfault.com/t/android","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323635000,"superChapterId":0,"superChapterName":"","tags":[],"title":"segmentfault","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1857,"link":"http://www.androiddevtools.cn/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323651000,"superChapterId":0,"superChapterName":"","tags":[],"title":"androiddevtools","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1858,"link":"https://developers.googleblog.cn/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323695000,"superChapterId":0,"superChapterName":"","tags":[],"title":"Google中文Blog","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"gank.io","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1859,"link":"http://gank.io/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515323720000,"superChapterId":0,"superChapterName":"","tags":[],"title":"干货集中营","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1862,"link":"http://a.codekk.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515324437000,"superChapterId":0,"superChapterName":"","tags":[],"title":"CodeKK","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1863,"link":"https://xiaozhuanlan.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515324456000,"superChapterId":0,"superChapterName":"","tags":[],"title":"小专栏","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1864,"link":"http://wanandroid.com/article/list/0?cid=176","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515324541000,"superChapterId":0,"superChapterName":"","tags":[],"title":"国内大牛","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1865,"link":"https://github.com/android-cn/android-dev-com","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515324559000,"superChapterId":0,"superChapterName":"","tags":[],"title":"国外大牛","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1866,"link":"https://www.androidos.net.cn/sourcecode","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515324594000,"superChapterId":0,"superChapterName":"","tags":[],"title":"Android源码","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1867,"link":"http://design.1sters.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515324880000,"superChapterId":0,"superChapterName":"","tags":[],"title":"Material Design 中文版","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":1868,"link":"https://leetcode.com/","niceDate":"2018-01-07","origin":"","prefix":"","projectLink":"","publishTime":1515325010000,"superChapterId":0,"superChapterName":"","tags":[],"title":"leetcode","type":0,"userId":-1,"visible":0,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":2405,"link":"https://dl.google.com/dl/android/maven2/index.html","niceDate":"2018-02-25","origin":"","prefix":"","projectLink":"","publishTime":1519537704000,"superChapterId":0,"superChapterName":"","tags":[],"title":"google mvn仓库","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":2406,"link":"http://jcenter.bintray.com/","niceDate":"2018-02-25","origin":"","prefix":"","projectLink":"","publishTime":1519537722000,"superChapterId":0,"superChapterName":"","tags":[],"title":"jcenter仓库","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":3469,"link":"https://mvnrepository.com/artifact/com.google.code.gson/gson","niceDate":"2018-10-10","origin":"","prefix":"","projectLink":"","publishTime":1539139799000,"superChapterId":0,"superChapterName":"","tags":[],"title":"maven仓库","type":0,"userId":-1,"visible":1,"zan":0},{"apkLink":"","author":"小编","chapterId":272,"chapterName":"常用网站","collect":false,"courseId":13,"desc":"","envelopePic":"","fresh":false,"id":8104,"link":"https://maven.aliyun.com/mvn/search","niceDate":"2019-03-20","origin":"","prefix":"","projectLink":"","publishTime":1553095634000,"superChapterId":0,"superChapterName":"","tags":[],"title":"maven仓库 阿里云托管","type":0,"userId":-1,"visible":1,"zan":0}] * cid : 272 * name : 常用网站 */ diff --git a/app/src/main/java/per/goweii/wanandroid/module/navigation/model/NaviRequest.java b/app/src/main/java/per/goweii/wanandroid/module/navigation/model/NaviRequest.java index 5905abab..b9d90b4f 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/navigation/model/NaviRequest.java +++ b/app/src/main/java/per/goweii/wanandroid/module/navigation/model/NaviRequest.java @@ -1,6 +1,6 @@ package per.goweii.wanandroid.module.navigation.model; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import java.util.List; @@ -13,8 +13,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class NaviRequest extends BaseRequest { diff --git a/app/src/main/java/per/goweii/wanandroid/module/navigation/presenter/NaviPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/navigation/presenter/NaviPresenter.java index 1b6464d2..4a7c5fd3 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/navigation/presenter/NaviPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/navigation/presenter/NaviPresenter.java @@ -12,8 +12,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class NaviPresenter extends BasePresenter { diff --git a/app/src/main/java/per/goweii/wanandroid/module/navigation/view/NaviView.java b/app/src/main/java/per/goweii/wanandroid/module/navigation/view/NaviView.java index b84c7389..5a1b75b4 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/navigation/view/NaviView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/navigation/view/NaviView.java @@ -8,11 +8,10 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface NaviView extends BaseView { void getNaviListSuccess(int code, List data); + void getNaviListFail(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/project/fragment/ProjectArticleFragment.java b/app/src/main/java/per/goweii/wanandroid/module/project/fragment/ProjectArticleFragment.java index f5f8dc61..52175af5 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/project/fragment/ProjectArticleFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/project/fragment/ProjectArticleFragment.java @@ -1,32 +1,33 @@ package per.goweii.wanandroid.module.project.fragment; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.kennyc.view.MultiStateView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import java.util.List; - import butterknife.BindView; import per.goweii.basic.core.base.BaseFragment; import per.goweii.basic.core.utils.SmartRefreshUtils; import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.FragmentProjectArticleBinding; import per.goweii.wanandroid.event.CollectionEvent; import per.goweii.wanandroid.event.LoginEvent; import per.goweii.wanandroid.event.ScrollTopEvent; -import per.goweii.wanandroid.event.SettingChangeEvent; -import per.goweii.wanandroid.module.main.activity.WebActivity; import per.goweii.wanandroid.module.main.adapter.ArticleAdapter; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.ArticleListBean; @@ -34,19 +35,16 @@ import per.goweii.wanandroid.module.project.presenter.ProjectArticlePresenter; import per.goweii.wanandroid.module.project.view.ProjectArticleView; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; import per.goweii.wanandroid.utils.RvScrollTopUtils; -import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.UrlOpenUtils; import per.goweii.wanandroid.widget.CollectView; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class ProjectArticleFragment extends BaseFragment implements ProjectArticleView { +public class ProjectArticleFragment extends BaseFragment implements ProjectArticleView { private static final int PAGE_START = 1; @@ -82,17 +80,7 @@ public void onCollectionEvent(CollectionEvent event) { if (event.getArticleId() == -1) { return; } - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.getId() == event.getArticleId()) { - if (item.isCollect() != event.isCollect()) { - item.setCollect(event.isCollect()); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - break; - } - } + mAdapter.notifyCollectionEvent(event); } @Subscribe(threadMode = ThreadMode.MAIN) @@ -104,24 +92,7 @@ public void onLoginEvent(LoginEvent event) { currPage = PAGE_START; getProjectArticleList(true); } else { - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.isCollect()) { - item.setCollect(false); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - } - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { - if (isDetached()) { - return; - } - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); + mAdapter.notifyAllUnCollect(); } } @@ -143,9 +114,10 @@ protected boolean isRegisterEventBus() { return true; } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_project_article; + protected FragmentProjectArticleBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentProjectArticleBinding.inflate(inflater, container, false); } @Nullable @@ -173,7 +145,6 @@ public void onRefresh() { }); rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new ArticleAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); mAdapter.setEnableLoadMore(false); mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override @@ -185,18 +156,18 @@ public void onLoadMoreRequested() { mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { @Override public void onItemClick(BaseQuickAdapter adapter, View view, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - WebActivity.start(getContext(), item); + UrlOpenUtils.Companion.with(item).open(getContext()); } } }); mAdapter.setOnItemChildViewClickListener(new ArticleAdapter.OnItemChildViewClickListener() { @Override public void onCollectClick(BaseViewHolder helper, CollectView v, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - if (!v.isChecked()) { + if (v.isChecked()) { presenter.collect(item, v); } else { presenter.uncollect(item, v); @@ -244,7 +215,7 @@ public void getProjectArticleList(boolean refresh) { public void getProjectArticleListSuccess(int code, ArticleListBean data) { currPage = data.getCurPage() + PAGE_START; if (data.getCurPage() == 1) { - mAdapter.setNewData(data.getDatas()); + mAdapter.setArticleData(data.getDatas()); mAdapter.setEnableLoadMore(true); if (data.getDatas() == null || data.getDatas().isEmpty()) { MultiStateUtils.toEmpty(msv); diff --git a/app/src/main/java/per/goweii/wanandroid/module/project/fragment/ProjectFragment.java b/app/src/main/java/per/goweii/wanandroid/module/project/fragment/ProjectFragment.java index 354ca545..4fbb9a3b 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/project/fragment/ProjectFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/project/fragment/ProjectFragment.java @@ -1,9 +1,12 @@ package per.goweii.wanandroid.module.project.fragment; -import android.support.annotation.Nullable; -import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager.widget.ViewPager; -import net.lucode.hackware.magicindicator.MagicIndicator; import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator; import java.util.List; @@ -16,21 +19,20 @@ import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.wanandroid.R; import per.goweii.wanandroid.common.Config; -import per.goweii.wanandroid.common.ScrollTop; +import per.goweii.wanandroid.databinding.FragmentProjectBinding; import per.goweii.wanandroid.event.ScrollTopEvent; import per.goweii.wanandroid.module.main.model.ChapterBean; import per.goweii.wanandroid.module.project.presenter.ProjectPresenter; import per.goweii.wanandroid.module.project.view.ProjectView; import per.goweii.wanandroid.utils.MagicIndicatorUtils; +import per.goweii.wanandroid.utils.RvScrollTopUtils; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class ProjectFragment extends BaseFragment implements ScrollTop, ProjectView { +public class ProjectFragment extends BaseFragment implements RvScrollTopUtils.ScrollTop, ProjectView { @BindView(R.id.ab) ActionBarEx ab; @@ -46,9 +48,10 @@ public static ProjectFragment create() { return new ProjectFragment(); } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_project; + protected FragmentProjectBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentProjectBinding.inflate(inflater, container, false); } @Nullable @@ -73,7 +76,7 @@ public String getTitle(ChapterBean data) { } }); vp.setAdapter(mAdapter); - mCommonNavigator = MagicIndicatorUtils.commonNavigator((MagicIndicator) ab.getTitleBarChild(), vp, mAdapter, new SimpleCallback() { + mCommonNavigator = MagicIndicatorUtils.commonNavigator(ab.getView(R.id.mi), vp, mAdapter, new SimpleCallback() { @Override public void onResult(Integer data) { notifyScrollTop(data); diff --git a/app/src/main/java/per/goweii/wanandroid/module/project/model/ProjectRequest.java b/app/src/main/java/per/goweii/wanandroid/module/project/model/ProjectRequest.java index 73ae1139..3e3dea0b 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/project/model/ProjectRequest.java +++ b/app/src/main/java/per/goweii/wanandroid/module/project/model/ProjectRequest.java @@ -1,7 +1,7 @@ package per.goweii.wanandroid.module.project.model; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; import java.util.List; @@ -16,8 +16,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ProjectRequest extends BaseRequest { diff --git a/app/src/main/java/per/goweii/wanandroid/module/project/presenter/ProjectArticlePresenter.java b/app/src/main/java/per/goweii/wanandroid/module/project/presenter/ProjectArticlePresenter.java index 3b13f5b7..8ff41109 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/project/presenter/ProjectArticlePresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/project/presenter/ProjectArticlePresenter.java @@ -15,8 +15,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ProjectArticlePresenter extends BasePresenter { @@ -79,7 +77,7 @@ public void onFinish() { } public void collect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.collect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.collectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } @@ -111,7 +109,7 @@ public void onFinish() { } public void uncollect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.uncollect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.uncollectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } diff --git a/app/src/main/java/per/goweii/wanandroid/module/project/presenter/ProjectPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/project/presenter/ProjectPresenter.java index 1675a36c..797195bd 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/project/presenter/ProjectPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/project/presenter/ProjectPresenter.java @@ -12,13 +12,11 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ProjectPresenter extends BasePresenter { - public void getProjectChapters(){ + public void getProjectChapters() { ProjectRequest.getProjectChapters(getRxLife(), new RequestListener>() { @Override public void onStart() { diff --git a/app/src/main/java/per/goweii/wanandroid/module/project/view/ProjectArticleView.java b/app/src/main/java/per/goweii/wanandroid/module/project/view/ProjectArticleView.java index 3d140a85..3dcd0e27 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/project/view/ProjectArticleView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/project/view/ProjectArticleView.java @@ -6,11 +6,10 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface ProjectArticleView extends BaseView { void getProjectArticleListSuccess(int code, ArticleListBean data); + void getProjectArticleListFailed(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/project/view/ProjectView.java b/app/src/main/java/per/goweii/wanandroid/module/project/view/ProjectView.java index 6a2e4922..6993727c 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/project/view/ProjectView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/project/view/ProjectView.java @@ -8,11 +8,10 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface ProjectView extends BaseView { void getProjectChaptersSuccess(int code, List data); + void getProjectChaptersFailed(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/question/fragment/QuestionFragment.kt b/app/src/main/java/per/goweii/wanandroid/module/question/fragment/QuestionFragment.kt new file mode 100644 index 00000000..73474f57 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/question/fragment/QuestionFragment.kt @@ -0,0 +1,160 @@ +package per.goweii.wanandroid.module.question.fragment + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import org.greenrobot.eventbus.Subscribe +import org.greenrobot.eventbus.ThreadMode +import per.goweii.basic.core.base.BaseFragment +import per.goweii.basic.core.utils.SmartRefreshUtils +import per.goweii.basic.ui.toast.ToastMaker +import per.goweii.basic.utils.listener.SimpleListener +import per.goweii.wanandroid.R +import per.goweii.wanandroid.databinding.FragmentQuestionBinding +import per.goweii.wanandroid.event.CollectionEvent +import per.goweii.wanandroid.event.LoginEvent +import per.goweii.wanandroid.event.ScrollTopEvent +import per.goweii.wanandroid.module.main.adapter.ArticleAdapter +import per.goweii.wanandroid.module.main.model.ArticleListBean +import per.goweii.wanandroid.module.question.presenter.QuestionPresenter +import per.goweii.wanandroid.module.question.view.QuestionView +import per.goweii.wanandroid.utils.MultiStateUtils +import per.goweii.wanandroid.utils.MultiStateUtils.Companion.setEmptyAndErrorClick +import per.goweii.wanandroid.utils.RvScrollTopUtils +import per.goweii.wanandroid.utils.RvScrollTopUtils.ScrollTop + +/** + * @author CuiZhen + * @date 2020/3/25 + */ +class QuestionFragment : BaseFragment(), QuestionView, ScrollTop { + + companion object { + private const val PAGE_START = 1 + + @JvmStatic + fun create(): QuestionFragment { + return QuestionFragment() + } + } + + private lateinit var mSmartRefreshUtils: SmartRefreshUtils + private lateinit var mAdapter: ArticleAdapter + + private var currPage = PAGE_START + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onCollectionEvent(event: CollectionEvent) { + if (isDetached) { + return + } + if (event.articleId == -1) { + return + } + mAdapter.notifyCollectionEvent(event) + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onLoginEvent(event: LoginEvent) { + if (isDetached) { + return + } + if (event.isLogin) { + currPage = PAGE_START + presenter.getQuestionList(currPage) + } else { + mAdapter.notifyAllUnCollect() + } + } + + @Subscribe(threadMode = ThreadMode.MAIN) + fun onScrollTopEvent(event: ScrollTopEvent) { + if (isAdded && !isDetached) { + RvScrollTopUtils.smoothScrollTop(binding.rv) + } + } + + override fun isRegisterEventBus() = true + + override fun initViewBinding( + inflater: LayoutInflater, + container: ViewGroup? + ): FragmentQuestionBinding { + return FragmentQuestionBinding.inflate(inflater, container, false) + } + + override fun initPresenter(): QuestionPresenter = QuestionPresenter() + + override fun initView() { + mSmartRefreshUtils = SmartRefreshUtils.with(binding.srl) + mSmartRefreshUtils.pureScrollMode() + mSmartRefreshUtils.setRefreshListener { + currPage = PAGE_START + presenter.getQuestionList(currPage) + } + binding.rv.layoutManager = LinearLayoutManager(context) + mAdapter = ArticleAdapter() + mAdapter.setEnableLoadMore(false) + mAdapter.setOnLoadMoreListener({ + presenter.getQuestionList(currPage) + }, binding.rv) + mAdapter.setOnItemChildViewClickListener { _, v, position -> + mAdapter.getArticleItem(position)?.let { item -> + if (v.isChecked) { + presenter.collect(item, v) + } else { + presenter.uncollect(item, v) + } + } + } + binding.rv.adapter = mAdapter + setEmptyAndErrorClick(binding.msv, SimpleListener { + MultiStateUtils.toLoading(binding.msv) + presenter.getQuestionList(currPage) + }) + } + + override fun loadData() { + MultiStateUtils.toLoading(binding.msv) + presenter.getQuestionListCache(PAGE_START) + } + + override fun onVisible(isFirstVisible: Boolean) { + super.onVisible(isFirstVisible) + if (isFirstVisible) { + currPage = PAGE_START + presenter.getQuestionList(currPage) + } + } + + override fun scrollTop() { + if (isAdded && !isDetached) { + RvScrollTopUtils.smoothScrollTop(binding.rv) + } + } + + override fun getQuestionListSuccess(code: Int, data: ArticleListBean) { + currPage = data.curPage + PAGE_START + if (data.curPage == PAGE_START) { + MultiStateUtils.toContent(binding.msv) + mAdapter.articleData = data.datas + } else { + mAdapter.addData(data.datas) + mAdapter.loadMoreComplete() + } + if (data.isOver) { + mAdapter.loadMoreEnd() + } else { + if (!mAdapter.isLoadMoreEnable) { + mAdapter.setEnableLoadMore(true) + } + } + mSmartRefreshUtils.success() + } + + override fun getQuestionListFail(code: Int, msg: String) { + ToastMaker.showShort(msg) + mSmartRefreshUtils.fail() + mAdapter.loadMoreFail() + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/question/model/QuestionRequest.kt b/app/src/main/java/per/goweii/wanandroid/module/question/model/QuestionRequest.kt new file mode 100644 index 00000000..93433f08 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/question/model/QuestionRequest.kt @@ -0,0 +1,36 @@ +package per.goweii.wanandroid.module.question.model + +import androidx.annotation.IntRange +import per.goweii.rxhttp.core.RxLife +import per.goweii.wanandroid.http.BaseRequest +import per.goweii.wanandroid.http.RequestListener +import per.goweii.wanandroid.http.WanApi +import per.goweii.wanandroid.http.WanCache +import per.goweii.wanandroid.module.main.model.ArticleListBean + +/** + * @author CuiZhen + * @date 2019/5/16 + * GitHub: https://github.com/goweii + */ +object QuestionRequest : BaseRequest() { + fun getQuestionListCache(@IntRange(from = 1) page: Int, + listener: RequestListener) { + cacheBean(WanCache.CacheKey.QUESTION_LIST(page), + ArticleListBean::class.java, + listener) + } + + fun getQuestionList(rxLife: RxLife, + @IntRange(from = 1) page: Int, + listener: RequestListener) { + if (page == 1) { + netBean(rxLife, + WanApi.api().getQuestionList(page), + WanCache.CacheKey.QUESTION_LIST(page), + listener) + } else { + rxLife.add(request(WanApi.api().getQuestionList(page), listener)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/question/presenter/QuestionPresenter.kt b/app/src/main/java/per/goweii/wanandroid/module/question/presenter/QuestionPresenter.kt new file mode 100644 index 00000000..55040e86 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/question/presenter/QuestionPresenter.kt @@ -0,0 +1,99 @@ +package per.goweii.wanandroid.module.question.presenter + +import per.goweii.basic.core.base.BasePresenter +import per.goweii.rxhttp.request.base.BaseBean +import per.goweii.rxhttp.request.exception.ExceptionHandle +import per.goweii.wanandroid.event.CollectionEvent +import per.goweii.wanandroid.http.RequestListener +import per.goweii.wanandroid.module.main.model.ArticleBean +import per.goweii.wanandroid.module.main.model.ArticleListBean +import per.goweii.wanandroid.module.main.model.MainRequest +import per.goweii.wanandroid.module.question.model.QuestionRequest +import per.goweii.wanandroid.module.question.view.QuestionView +import per.goweii.wanandroid.widget.CollectView + +/** + * @author CuiZhen + * @date 2020/3/25 + */ +class QuestionPresenter : BasePresenter() { + + fun getQuestionListCache(page: Int) { + QuestionRequest.getQuestionListCache(page, object : RequestListener { + override fun onStart() {} + override fun onSuccess(code: Int, data: ArticleListBean) { + if (isAttach) { + baseView.getQuestionListSuccess(code, data) + } + } + + override fun onFailed(code: Int, msg: String) {} + override fun onError(handle: ExceptionHandle) {} + override fun onFinish() {} + }) + } + + fun getQuestionList(page: Int) { + QuestionRequest.getQuestionList(rxLife, page, object : RequestListener { + override fun onStart() {} + override fun onSuccess(code: Int, data: ArticleListBean) { + if (isAttach) { + baseView.getQuestionListSuccess(code, data) + } + } + + override fun onFailed(code: Int, msg: String) { + if (isAttach) { + baseView.getQuestionListFail(code, msg) + } + } + + override fun onError(handle: ExceptionHandle) {} + override fun onFinish() {} + }) + } + + fun collect(item: ArticleBean, v: CollectView) { + addToRxLife(MainRequest.collectArticle(item.id, object : RequestListener { + override fun onStart() {} + override fun onSuccess(code: Int, data: BaseBean?) { + item.isCollect = true + if (!v.isChecked) { + v.toggle() + } + CollectionEvent.postCollectWithArticleId(item.id) + } + + override fun onFailed(code: Int, msg: String) { + if (v.isChecked) { + v.toggle() + } + } + + override fun onError(handle: ExceptionHandle) {} + override fun onFinish() {} + })) + } + + fun uncollect(item: ArticleBean, v: CollectView) { + addToRxLife(MainRequest.uncollectArticle(item.id, object : RequestListener { + override fun onStart() {} + override fun onSuccess(code: Int, data: BaseBean?) { + item.isCollect = false + if (v.isChecked) { + v.toggle() + } + CollectionEvent.postUnCollectWithArticleId(item.id) + } + + override fun onFailed(code: Int, msg: String) { + if (!v.isChecked) { + v.toggle() + } + } + + override fun onError(handle: ExceptionHandle) {} + override fun onFinish() {} + })) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/question/view/QuestionView.kt b/app/src/main/java/per/goweii/wanandroid/module/question/view/QuestionView.kt new file mode 100644 index 00000000..139e1c5c --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/module/question/view/QuestionView.kt @@ -0,0 +1,13 @@ +package per.goweii.wanandroid.module.question.view + +import per.goweii.basic.core.base.BaseView +import per.goweii.wanandroid.module.main.model.ArticleListBean + +/** + * @author CuiZhen + * @date 2020/3/25 + */ +interface QuestionView : BaseView { + fun getQuestionListSuccess(code: Int, data: ArticleListBean) + fun getQuestionListFail(code: Int, msg: String) +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/fragment/WxArticleFragment.java b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/fragment/WxArticleFragment.java index 20736a56..0a0f681d 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/fragment/WxArticleFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/fragment/WxArticleFragment.java @@ -1,32 +1,32 @@ package per.goweii.wanandroid.module.wxarticle.fragment; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.View; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.kennyc.view.MultiStateView; -import com.scwang.smartrefresh.layout.SmartRefreshLayout; +import com.scwang.smart.refresh.layout.SmartRefreshLayout; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import java.util.List; - import butterknife.BindView; import per.goweii.basic.core.base.BaseFragment; import per.goweii.basic.core.utils.SmartRefreshUtils; import per.goweii.basic.ui.toast.ToastMaker; import per.goweii.basic.utils.listener.SimpleListener; import per.goweii.wanandroid.R; +import per.goweii.wanandroid.databinding.FragmentWxArticleBinding; import per.goweii.wanandroid.event.CollectionEvent; import per.goweii.wanandroid.event.LoginEvent; import per.goweii.wanandroid.event.ScrollTopEvent; -import per.goweii.wanandroid.event.SettingChangeEvent; -import per.goweii.wanandroid.module.main.activity.WebActivity; import per.goweii.wanandroid.module.main.adapter.ArticleAdapter; import per.goweii.wanandroid.module.main.model.ArticleBean; import per.goweii.wanandroid.module.main.model.ArticleListBean; @@ -34,19 +34,15 @@ import per.goweii.wanandroid.module.wxarticle.presenter.WxArticlePresenter; import per.goweii.wanandroid.module.wxarticle.view.WxArticleView; import per.goweii.wanandroid.utils.MultiStateUtils; -import per.goweii.wanandroid.utils.RvAnimUtils; import per.goweii.wanandroid.utils.RvScrollTopUtils; -import per.goweii.wanandroid.utils.SettingUtils; import per.goweii.wanandroid.widget.CollectView; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class WxArticleFragment extends BaseFragment implements WxArticleView { +public class WxArticleFragment extends BaseFragment implements WxArticleView { private static final int PAGE_START = 1; @@ -82,17 +78,7 @@ public void onCollectionEvent(CollectionEvent event) { if (event.getArticleId() == -1) { return; } - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.getId() == event.getArticleId()) { - if (item.isCollect() != event.isCollect()) { - item.setCollect(event.isCollect()); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - break; - } - } + mAdapter.notifyCollectionEvent(event); } @Subscribe(threadMode = ThreadMode.MAIN) @@ -104,24 +90,7 @@ public void onLoginEvent(LoginEvent event) { currPage = PAGE_START; getWxArticleList(true); } else { - List list = mAdapter.getData(); - for (int i = 0; i < list.size(); i++) { - ArticleBean item = list.get(i); - if (item.isCollect()) { - item.setCollect(false); - mAdapter.notifyItemChanged(i + mAdapter.getHeaderLayoutCount()); - } - } - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - public void onSettingChangeEvent(SettingChangeEvent event) { - if (isDetached()) { - return; - } - if (event.isRvAnimChanged()) { - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); + mAdapter.notifyAllUnCollect(); } } @@ -143,9 +112,10 @@ protected boolean isRegisterEventBus() { return true; } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_wx_article; + protected FragmentWxArticleBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentWxArticleBinding.inflate(inflater, container, false); } @Nullable @@ -173,7 +143,6 @@ public void onRefresh() { }); rv.setLayoutManager(new LinearLayoutManager(getContext())); mAdapter = new ArticleAdapter(); - RvAnimUtils.setAnim(mAdapter, SettingUtils.getInstance().getRvAnim()); mAdapter.setEnableLoadMore(false); mAdapter.setOnLoadMoreListener(new BaseQuickAdapter.RequestLoadMoreListener() { @Override @@ -181,21 +150,12 @@ public void onLoadMoreRequested() { getWxArticleList(false); } }, rv); - mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() { - @Override - public void onItemClick(BaseQuickAdapter adapter, View view, int position) { - ArticleBean item = mAdapter.getItem(position); - if (item != null) { - WebActivity.start(getContext(), item); - } - } - }); mAdapter.setOnItemChildViewClickListener(new ArticleAdapter.OnItemChildViewClickListener() { @Override public void onCollectClick(BaseViewHolder helper, CollectView v, int position) { - ArticleBean item = mAdapter.getItem(position); + ArticleBean item = mAdapter.getArticleItem(position); if (item != null) { - if (!v.isChecked()) { + if (v.isChecked()) { presenter.collect(item, v); } else { presenter.uncollect(item, v); @@ -243,7 +203,7 @@ public void getWxArticleList(boolean refresh) { public void getWxArticleListSuccess(int code, ArticleListBean data) { currPage = data.getCurPage() + PAGE_START; if (data.getCurPage() == 1) { - mAdapter.setNewData(data.getDatas()); + mAdapter.setArticleData(data.getDatas()); mAdapter.setEnableLoadMore(true); if (data.getDatas() == null || data.getDatas().isEmpty()) { MultiStateUtils.toEmpty(msv); diff --git a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/fragment/WxFragment.java b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/fragment/WxFragment.java index 07d23ef1..0249609d 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/fragment/WxFragment.java +++ b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/fragment/WxFragment.java @@ -1,9 +1,12 @@ package per.goweii.wanandroid.module.wxarticle.fragment; -import android.support.annotation.Nullable; -import android.support.v4.view.ViewPager; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager.widget.ViewPager; -import net.lucode.hackware.magicindicator.MagicIndicator; import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator; import java.util.List; @@ -16,21 +19,20 @@ import per.goweii.basic.utils.listener.SimpleCallback; import per.goweii.wanandroid.R; import per.goweii.wanandroid.common.Config; -import per.goweii.wanandroid.common.ScrollTop; +import per.goweii.wanandroid.databinding.FragmentWxBinding; import per.goweii.wanandroid.event.ScrollTopEvent; import per.goweii.wanandroid.module.main.model.ChapterBean; import per.goweii.wanandroid.module.wxarticle.presenter.WxPresenter; import per.goweii.wanandroid.module.wxarticle.view.WxView; import per.goweii.wanandroid.utils.MagicIndicatorUtils; +import per.goweii.wanandroid.utils.RvScrollTopUtils; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class WxFragment extends BaseFragment implements ScrollTop, WxView { +public class WxFragment extends BaseFragment implements RvScrollTopUtils.ScrollTop, WxView { @BindView(R.id.ab) ActionBarEx ab; @@ -46,9 +48,10 @@ public static WxFragment create() { return new WxFragment(); } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.fragment_wx; + protected FragmentWxBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return FragmentWxBinding.inflate(inflater, container, false); } @Nullable @@ -73,7 +76,7 @@ public String getTitle(ChapterBean data) { } }); vp.setAdapter(mAdapter); - mCommonNavigator = MagicIndicatorUtils.commonNavigator((MagicIndicator) ab.getTitleBarChild(), vp, mAdapter, new SimpleCallback() { + mCommonNavigator = MagicIndicatorUtils.commonNavigator(ab.getView(R.id.mi), vp, mAdapter, new SimpleCallback() { @Override public void onResult(Integer data) { notifyScrollTop(data); diff --git a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/model/WxRequest.java b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/model/WxRequest.java index 6d057013..89c44b2a 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/model/WxRequest.java +++ b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/model/WxRequest.java @@ -1,7 +1,7 @@ package per.goweii.wanandroid.module.wxarticle.model; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; import java.util.List; @@ -16,8 +16,6 @@ /** * @author CuiZhen * @date 2019/5/16 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WxRequest extends BaseRequest { diff --git a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/presenter/WxArticlePresenter.java b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/presenter/WxArticlePresenter.java index e53e7c4d..036e14b7 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/presenter/WxArticlePresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/presenter/WxArticlePresenter.java @@ -15,8 +15,6 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WxArticlePresenter extends BasePresenter { @@ -78,7 +76,7 @@ public void onFinish() { }); } - public void getWxArticleListSearch(int id, int page, String key, boolean refresh){ + public void getWxArticleListSearch(int id, int page, String key, boolean refresh) { WxRequest.getWxArticleList(getRxLife(), refresh, id, page, key, new RequestListener() { @Override public void onStart() { @@ -109,7 +107,7 @@ public void onFinish() { } public void collect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.collect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.collectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } @@ -141,7 +139,7 @@ public void onFinish() { } public void uncollect(ArticleBean item, final CollectView v) { - addToRxLife(MainRequest.uncollect(item.getId(), new RequestListener() { + addToRxLife(MainRequest.uncollectArticle(item.getId(), new RequestListener() { @Override public void onStart() { } diff --git a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/presenter/WxPresenter.java b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/presenter/WxPresenter.java index bf4dc233..31293336 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/presenter/WxPresenter.java +++ b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/presenter/WxPresenter.java @@ -12,13 +12,11 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class WxPresenter extends BasePresenter { - public void getWxArticleChapters(){ + public void getWxArticleChapters() { WxRequest.getWxArticleChapters(getRxLife(), new RequestListener>() { @Override public void onStart() { diff --git a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/view/WxArticleView.java b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/view/WxArticleView.java index 2af9d679..9c934c30 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/view/WxArticleView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/view/WxArticleView.java @@ -6,14 +6,14 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface WxArticleView extends BaseView { void getWxArticleListSuccess(int code, ArticleListBean data); + void getWxArticleListFailed(int code, String msg); void getWxArticleListSearchSuccess(int code, ArticleListBean data); + void getWxArticleListSearchFailed(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/view/WxView.java b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/view/WxView.java index 5967d80e..3023f33e 100644 --- a/app/src/main/java/per/goweii/wanandroid/module/wxarticle/view/WxView.java +++ b/app/src/main/java/per/goweii/wanandroid/module/wxarticle/view/WxView.java @@ -8,11 +8,10 @@ /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public interface WxView extends BaseView { void getWxArticleChaptersSuccess(int code, List data); + void getWxArticleChaptersFailed(int code, String msg); } diff --git a/app/src/main/java/per/goweii/wanandroid/utils/ADUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/ADUtils.java new file mode 100644 index 00000000..ec107c6b --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/ADUtils.java @@ -0,0 +1,73 @@ +package per.goweii.wanandroid.utils; + +import java.util.Date; + +import per.goweii.basic.utils.SPUtils; +import per.goweii.wanandroid.module.main.model.AdvertBean; + +/** + * @author CuiZhen + * @date 2019/5/18 + * GitHub: https://github.com/goweii + */ +public class ADUtils { + private static final String SP_NAME = "ad"; + private static final String KEY_AD = "KEY_AD"; + + private final SPUtils mSPUtils = SPUtils.newInstance(SP_NAME); + + private static class Holder { + private static final ADUtils INSTANCE = new ADUtils(); + } + + public static ADUtils getInstance() { + return Holder.INSTANCE; + } + + private ADUtils() { + } + + public boolean shouldShowAD(AdvertBean advertBean) { + AdvertBean.ShowMode showMode = AdvertBean.ShowMode.from(advertBean.getShowMode()); + boolean shouldShow = false; + switch (showMode) { + case DO_NOT_SHOW: + shouldShow = false; + break; + case ONCE_OF_DAY: + shouldShow = !ADUtils.getInstance().isADShownToday(); + break; + case EVERY_TIME: + shouldShow = true; + break; + } + if (shouldShow) { + shouldShow = advertBean.shouldShowAtNow(); + } + return shouldShow; + } + + public boolean isADShownToday() { + long showTime = mSPUtils.get(KEY_AD, 0L); + long currTime = System.currentTimeMillis(); + if (currTime - showTime > 24 * 60 * 60 * 1000L) { + return false; + } + Date currDate = new Date(currTime); + Date showDate = new Date(showTime); + if (currDate.getYear() != showDate.getYear()) { + return false; + } + if (currDate.getMonth() != showDate.getMonth()) { + return false; + } + if (currDate.getDay() != showDate.getDay()) { + return false; + } + return true; + } + + public void setAdShown() { + mSPUtils.save(KEY_AD, System.currentTimeMillis()); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/ArticleDiffCallback.kt b/app/src/main/java/per/goweii/wanandroid/utils/ArticleDiffCallback.kt new file mode 100644 index 00000000..86c846ae --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/ArticleDiffCallback.kt @@ -0,0 +1,60 @@ +package per.goweii.wanandroid.utils + +import com.chad.library.adapter.base.diff.BaseQuickDiffCallback +import com.chad.library.adapter.base.entity.MultiItemEntity +import com.google.android.gms.ads.nativead.NativeAd +import per.goweii.wanandroid.module.main.adapter.ArticleAdapter +import per.goweii.wanandroid.module.main.model.NativeAdBean +import per.goweii.wanandroid.module.main.model.ArticleBean + +/** + * @author CuiZhen + * @date 2020/3/7 + */ +class ArticleDiffCallback( + newList: List? +) : BaseQuickDiffCallback(newList) { + override fun areItemsTheSame(oldItem: ArticleBean, newItem: ArticleBean): Boolean { + return oldItem.id == newItem.id + } + + override fun areContentsTheSame(oldItem: ArticleBean, newItem: ArticleBean): Boolean { + return oldItem == newItem + } +} + +class ArticleAndAdDiffCallback( + newList: List? +) : BaseQuickDiffCallback(newList) { + override fun areItemsTheSame(oldItem: MultiItemEntity, newItem: MultiItemEntity): Boolean { + if (oldItem.itemType != newItem.itemType) { + return false + } + if (oldItem.itemType == ArticleAdapter.ITEM_TYPE_ARTICLE) { + oldItem as ArticleBean + newItem as ArticleBean + return oldItem.id == newItem.id + } else if (oldItem.itemType == ArticleAdapter.ITEM_TYPE_AD) { + oldItem as NativeAdBean + newItem as NativeAdBean + return oldItem === newItem + } + return false + } + + override fun areContentsTheSame(oldItem: MultiItemEntity, newItem: MultiItemEntity): Boolean { + if (oldItem.itemType != newItem.itemType) { + return false + } + if (oldItem.itemType == ArticleAdapter.ITEM_TYPE_ARTICLE) { + oldItem as ArticleBean + newItem as ArticleBean + return oldItem == newItem + } else if (oldItem.itemType == ArticleAdapter.ITEM_TYPE_AD) { + oldItem as NativeAdBean + newItem as NativeAdBean + return oldItem.nativeAd === newItem.nativeAd + } + return false + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/ConfigUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/ConfigUtils.java index 39b3d8a2..ec88b810 100644 --- a/app/src/main/java/per/goweii/wanandroid/utils/ConfigUtils.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/ConfigUtils.java @@ -1,7 +1,9 @@ package per.goweii.wanandroid.utils; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; @@ -12,14 +14,10 @@ /** * @author CuiZhen * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ConfigUtils { - private static final String SP_NAME = "config"; - private static final String KEY_LAST_UPDATE = "KEY_LAST_UPDATE"; private static final String KEY_CONFIG = "KEY_CONFIG"; private final SPUtils mSPUtils = SPUtils.newInstance(SP_NAME); @@ -38,17 +36,6 @@ public static ConfigUtils getInstance() { private ConfigUtils() { } - public boolean isTodayUpdate() { - return false; -// long last = getLastUpdate(); -// long curr = System.currentTimeMillis(); -// Date lastDate = new Date(last); -// Date currDate = new Date(curr); -// return lastDate.getYear() == currDate.getYear() && -// lastDate.getMonth() == currDate.getMonth() && -// lastDate.getDay() == currDate.getDay(); - } - @NonNull public ConfigBean getConfig() { if (mConfigBean != null) { @@ -71,17 +58,22 @@ public void setConfig(@Nullable ConfigBean configBean) { mConfigBean = new ConfigBean(); } else { mConfigBean = configBean; - setLastUpdate(); } mSPUtils.save(KEY_CONFIG, mGson.toJson(mConfigBean)); + GrayFilterHelper.INSTANCE.refresh(); } - private long getLastUpdate() { - return mSPUtils.get(KEY_LAST_UPDATE, 0L); + @Nullable + public String getThemeName() { + ConfigBean configBean = getConfig(); + if (configBean.isEnableAtNow() && !TextUtils.isEmpty(configBean.getTheme())) { + return configBean.getTheme(); + } + return null; } - private void setLastUpdate() { - mSPUtils.save(KEY_LAST_UPDATE, System.currentTimeMillis()); + public int getTheme() { + return ThemeUtils.getTheme(getThemeName()); } } diff --git a/app/src/main/java/per/goweii/wanandroid/utils/CookieUtils.kt b/app/src/main/java/per/goweii/wanandroid/utils/CookieUtils.kt new file mode 100644 index 00000000..45ca15cc --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/CookieUtils.kt @@ -0,0 +1,27 @@ +package per.goweii.wanandroid.utils + +import android.content.Context +import com.franmontiel.persistentcookiejar.PersistentCookieJar +import com.franmontiel.persistentcookiejar.cache.SetCookieCache +import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor +import okhttp3.Cookie +import okhttp3.HttpUrl + +object CookieUtils { + lateinit var cookieJar: PersistentCookieJar + + fun init(context: Context) { + cookieJar = PersistentCookieJar(SetCookieCache(), + SharedPrefsCookiePersistor(context.applicationContext)) + } + + fun loadForUrl(url: String): MutableList { + if (url.isBlank()) { + return arrayListOf() + } + if (!this::cookieJar.isInitialized) { + return arrayListOf() + } + return cookieJar.loadForRequest(HttpUrl.get(url)) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/CopiedTextProcessor.java b/app/src/main/java/per/goweii/wanandroid/utils/CopiedTextProcessor.java new file mode 100644 index 00000000..6010d431 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/CopiedTextProcessor.java @@ -0,0 +1,111 @@ +package per.goweii.wanandroid.utils; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.net.Uri; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import per.goweii.basic.utils.LogUtils; +import per.goweii.basic.utils.Utils; +import per.goweii.wanandroid.utils.wanpwd.WanPwdParser; + +/** + * @author CuiZhen + * @date 2019/12/28 + * GitHub: https://github.com/goweii + */ +public class CopiedTextProcessor { + + private final ClipboardManager mClipboardManager; + + private ProcessCallback mProcessCallback = null; + + private String mLastCopiedText = null; + private String mCurrProcessText = null; + + private static class Holder { + private static CopiedTextProcessor sInstance = new CopiedTextProcessor(); + } + + private CopiedTextProcessor() { + mClipboardManager = (ClipboardManager) Utils.getAppContext().getSystemService(Context.CLIPBOARD_SERVICE); + } + + public static CopiedTextProcessor getInstance() { + return Holder.sInstance; + } + + public void processed() { + if (TextUtils.isEmpty(mCurrProcessText)) { + return; + } + mLastCopiedText = mCurrProcessText; + } + + public void setProcessCallback(ProcessCallback processCallback) { + mProcessCallback = processCallback; + } + + public void process() { + mCurrProcessText = getPrimaryClipText(); + if (!TextUtils.isEmpty(mCurrProcessText)) { + if (isDiff(mCurrProcessText)) { + if (isLink(mCurrProcessText)) { + if (mProcessCallback != null) { + mProcessCallback.isLink(mCurrProcessText); + } + return; + } + WanPwdParser parser = WanPwdParser.match(mCurrProcessText); + if (parser != null) { + if (mProcessCallback != null) { + mProcessCallback.isWanPwd(parser); + } + return; + } + } + } + if (mProcessCallback != null) { + mProcessCallback.ignored(); + } + LogUtils.d("CopiedTextProcessor", "nothing"); + } + + @NonNull + private String getPrimaryClipText() { + ClipData clip = mClipboardManager.getPrimaryClip(); + if (clip == null || clip.getItemCount() <= 0) { + return ""; + } + ClipData.Item item = clip.getItemAt(0); + if (TextUtils.isEmpty(item.getText())) { + return ""; + } + return item.getText().toString(); + } + + private boolean isDiff(String text) { + return !TextUtils.equals(mLastCopiedText, text); + } + + private boolean isLink(String text) { + try { + Uri uri = Uri.parse(text); + return TextUtils.equals(uri.getScheme(), "http") || TextUtils.equals(uri.getScheme(), "https"); + } catch (Exception e) { + return false; + } + } + + public interface ProcessCallback { + void isLink(String link); + + void isWanPwd(WanPwdParser pwd); + + void ignored(); + } + +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/DarkModeUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/DarkModeUtils.java new file mode 100644 index 00000000..aee0a919 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/DarkModeUtils.java @@ -0,0 +1,53 @@ +package per.goweii.wanandroid.utils; + +import android.app.UiModeManager; +import android.content.Context; +import android.content.res.Configuration; +import android.os.Build; + +import androidx.appcompat.app.AppCompatDelegate; + +/** + * @author CuiZhen + * @date 2020/2/17 + * GitHub: https://github.com/goweii + */ +public class DarkModeUtils { + public static boolean isDarkMode(Configuration config) { + int uiMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK; + return uiMode == Configuration.UI_MODE_NIGHT_YES; + } + + public static boolean isDarkMode(Context context) { + return isDarkMode(context.getResources().getConfiguration()); + } + + public static void initDarkMode(Context context) { + SettingUtils.ThemeMode themeMode = SettingUtils.getInstance().getThemeMode(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + UiModeManager uiModeManager = (UiModeManager) context.getSystemService(Context.UI_MODE_SERVICE); + switch (themeMode) { + case FOLLOW_SYSTEM: + uiModeManager.setApplicationNightMode(UiModeManager.MODE_NIGHT_AUTO); + break; + case LIGHT: + uiModeManager.setApplicationNightMode(UiModeManager.MODE_NIGHT_NO); + break; + case DARK: + uiModeManager.setApplicationNightMode(UiModeManager.MODE_NIGHT_YES); + break; + } + } + switch (themeMode) { + case FOLLOW_SYSTEM: + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); + break; + case LIGHT: + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); + break; + case DARK: + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + break; + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/GoogleAdUnitIds.kt b/app/src/main/java/per/goweii/wanandroid/utils/GoogleAdUnitIds.kt new file mode 100644 index 00000000..47d9997a --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/GoogleAdUnitIds.kt @@ -0,0 +1,8 @@ +package per.goweii.wanandroid.utils + +import per.goweii.wanandroid.BuildConfig + +object GoogleAdUnitIds { + const val ARTICLE_LIST_NATIVE_AD = BuildConfig.GOOGLE_AD_UNIT_ID_ARTICLE_LIST_NATIVE_AD + const val ARTICLE_DETAILS_BOTTOM_BANNER_AD = BuildConfig.GOOGLE_AD_UNIT_ID_ARTICLE_DETAILS_BOTTOM_BANNER_AD +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/GrayFilterHelper.kt b/app/src/main/java/per/goweii/wanandroid/utils/GrayFilterHelper.kt new file mode 100644 index 00000000..b55de7a7 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/GrayFilterHelper.kt @@ -0,0 +1,72 @@ +package per.goweii.wanandroid.utils + +import android.app.Activity +import android.app.Application +import android.graphics.ColorMatrix +import android.graphics.ColorMatrixColorFilter +import android.graphics.Paint +import android.os.Bundle +import android.view.View + +/** + * @author CuiZhen + * @date 2020/4/5 + */ +object GrayFilterHelper : Application.ActivityLifecycleCallbacks { + + private val activitys = arrayListOf() + + fun attach(application: Application) { + application.registerActivityLifecycleCallbacks(this) + } + + fun refresh() { + activitys.forEach { refresh(it) } + } + + private fun refresh(activity: Activity) { + val config = ConfigUtils.getInstance().config + if (config.isEnableAtNow && config.isGrayFilter) { + enable(activity) + } else { + disable(activity) + } + } + + private fun enable(activity: Activity) { + val colorMatrix = ColorMatrix() + colorMatrix.setSaturation(0f) + val paint = Paint() + paint.colorFilter = ColorMatrixColorFilter(colorMatrix) + activity.window.decorView.setLayerType(View.LAYER_TYPE_HARDWARE, paint) + } + + private fun disable(activity: Activity) { + activity.window.decorView.setLayerType(View.LAYER_TYPE_NONE, null) + } + + override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { + activitys.add(activity) + refresh(activity) + } + + override fun onActivityDestroyed(activity: Activity) { + activitys.remove(activity) + } + + override fun onActivityPaused(activity: Activity) { + } + + override fun onActivityResumed(activity: Activity) { + } + + override fun onActivityStarted(activity: Activity) { + } + + override fun onActivityStopped(activity: Activity) { + } + + override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) { + } + +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/GuideSPUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/GuideSPUtils.java index 20116d6e..f997924c 100644 --- a/app/src/main/java/per/goweii/wanandroid/utils/GuideSPUtils.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/GuideSPUtils.java @@ -5,17 +5,21 @@ /** * @author CuiZhen * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class GuideSPUtils { private static final String SP_NAME = "guide"; private static final String KEY_WEB_GUIDE = "KEY_WEB_GUIDE"; + private static final String KEY_ARTICLE_GUIDE = "KEY_ARTICLE_GUIDE"; + private static final String KEY_PRIVACY_POLICY = "KEY_PRIVACY_POLICY"; private final SPUtils mSPUtils = SPUtils.newInstance(SP_NAME); + private boolean webGuideShown = false; + private boolean articleGuideShown = false; + private boolean privacyPolicyShown = false; + private static class Holder { private static final GuideSPUtils INSTANCE = new GuideSPUtils(); } @@ -28,11 +32,36 @@ private GuideSPUtils() { } public boolean isWebGuideShown() { - return mSPUtils.get(KEY_WEB_GUIDE, false); + if (webGuideShown) return true; + webGuideShown = mSPUtils.get(KEY_WEB_GUIDE, false); + return webGuideShown; } public void setWebGuideShown() { + webGuideShown = true; mSPUtils.save(KEY_WEB_GUIDE, true); } + public boolean isArticleGuideShown() { + if (articleGuideShown) return true; + articleGuideShown = mSPUtils.get(KEY_ARTICLE_GUIDE, false); + return articleGuideShown; + } + + public void setArticleGuideShown() { + articleGuideShown = true; + mSPUtils.save(KEY_ARTICLE_GUIDE, true); + } + + public boolean isPrivacyPolicyShown() { + if (privacyPolicyShown) return true; + privacyPolicyShown = mSPUtils.get(KEY_PRIVACY_POLICY, false); + return privacyPolicyShown; + } + + public void setPrivacyPolicyShown() { + privacyPolicyShown = true; + mSPUtils.save(KEY_PRIVACY_POLICY, true); + } + } diff --git a/app/src/main/java/per/goweii/wanandroid/utils/ImageLoader.java b/app/src/main/java/per/goweii/wanandroid/utils/ImageLoader.java index fa2e8dd1..55edea1d 100644 --- a/app/src/main/java/per/goweii/wanandroid/utils/ImageLoader.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/ImageLoader.java @@ -1,63 +1,106 @@ package per.goweii.wanandroid.utils; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.net.Uri; import android.text.TextUtils; import android.widget.ImageView; +import androidx.core.content.ContextCompat; + import per.goweii.basic.core.glide.GlideHelper; import per.goweii.basic.core.glide.transformation.BlurTransformation; +import per.goweii.basic.core.glide.transformation.RoundTransformation; import per.goweii.wanandroid.R; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ImageLoader { - public static void image(ImageView imageView, String url){ + public static void image(ImageView imageView, String url) { GlideHelper.with(imageView.getContext()) - .errorHolder(R.drawable.image_holder) - .placeHolder(R.drawable.image_holder) + .errorHolder(ContextCompat.getDrawable(imageView.getContext(), R.drawable.shape_image_place_holder)) + .placeHolder(ContextCompat.getDrawable(imageView.getContext(), R.drawable.shape_image_place_holder)) .cache(true) .load(url) .into(imageView); } - public static void banner(ImageView imageView, String url){ + public static void roundImage(ImageView imageView, String url, int radius) { GlideHelper.with(imageView.getContext()) - .errorHolder(R.drawable.image_holder) - .placeHolder(R.drawable.image_holder) + .errorHolder(ContextCompat.getDrawable(imageView.getContext(), R.drawable.shape_image_place_holder)) + .placeHolder(ContextCompat.getDrawable(imageView.getContext(), R.drawable.shape_image_place_holder)) + .transformation(new RoundTransformation(radius)) .cache(true) .load(url) .into(imageView); } - public static void userIcon(ImageView imageView, String url){ + public static void gif(ImageView imageView, String url) { + GlideHelper.with(imageView.getContext()) + .asGif() + .errorHolder(ContextCompat.getDrawable(imageView.getContext(), R.drawable.shape_image_place_holder)) + .placeHolder(ContextCompat.getDrawable(imageView.getContext(), R.drawable.shape_image_place_holder)) + .cache(true) + .load(url) + .into(imageView); + } + + public static void banner(ImageView imageView, String url) { + GlideHelper.with(imageView.getContext()) + .errorHolder(ContextCompat.getDrawable(imageView.getContext(), R.drawable.shape_image_place_holder)) + .placeHolder(ContextCompat.getDrawable(imageView.getContext(), R.drawable.shape_image_place_holder)) + .cache(true) + .load(url) + .into(imageView); + } + + public static void userIcon(ImageView imageView, String url) { GlideHelper.with(imageView.getContext()) -// .errorHolder(R.drawable.image_holder) -// .placeHolder(R.drawable.image_holder) .cache(true) + .errorHolder(ContextCompat.getDrawable(imageView.getContext(), R.drawable.shape_image_place_holder)) + .placeHolder(ContextCompat.getDrawable(imageView.getContext(), R.drawable.shape_image_place_holder)) .load(url) .into(imageView); } - public static void userBlur(ImageView imageView, String url){ + public static void userIcon(ImageView imageView, Uri uri) { + GlideHelper.with(imageView.getContext()) + .cache(true) + .errorHolder(ContextCompat.getDrawable(imageView.getContext(), R.drawable.shape_image_place_holder)) + .placeHolder(ContextCompat.getDrawable(imageView.getContext(), R.drawable.shape_image_place_holder)) + .load(uri) + .into(imageView); + } + + public static void userBlur(ImageView imageView, String url) { if (TextUtils.isEmpty(url)) { - imageView.setImageResource(R.color.transparent); + imageView.setImageDrawable(new ColorDrawable(Color.TRANSPARENT)); return; } GlideHelper.with(imageView.getContext()) -// .errorHolder(R.drawable.image_holder) -// .placeHolder(R.drawable.image_holder) .cache(true) .load(url) .transformation(new BlurTransformation(0.2F)) .into(imageView); } - public static void userBlur(ImageView imageView, int res){ + public static void userBlur(ImageView imageView, Uri uri) { + if (uri == null) { + imageView.setImageDrawable(new ColorDrawable(Color.TRANSPARENT)); + return; + } + GlideHelper.with(imageView.getContext()) + .cache(true) + .load(uri) + .transformation(new BlurTransformation(0.2F)) + .into(imageView); + } + + public static void userBlur(ImageView imageView, int res) { GlideHelper.with(imageView.getContext()) // .errorHolder(R.drawable.image_holder) // .placeHolder(R.drawable.image_holder) diff --git a/app/src/main/java/per/goweii/wanandroid/utils/KeyboardHelper.java b/app/src/main/java/per/goweii/wanandroid/utils/KeyboardHelper.java index 520c0b43..a76d7794 100644 --- a/app/src/main/java/per/goweii/wanandroid/utils/KeyboardHelper.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/KeyboardHelper.java @@ -2,7 +2,6 @@ import android.animation.ObjectAnimator; import android.app.Activity; -import android.support.annotation.NonNull; import android.view.View; import android.view.ViewTreeObserver; import android.view.Window; @@ -10,6 +9,8 @@ import android.view.animation.DecelerateInterpolator; import android.widget.EditText; +import androidx.annotation.NonNull; + import per.goweii.keyboardcompat.KeyboardCompat; /** @@ -106,8 +107,6 @@ public KeyboardHelper duration(long duration) { /** * 设置moveView移动以ScrollY属性滚动内容 - * - * @return SoftInputHelper */ public KeyboardHelper moveWithScroll() { this.moveWithScroll = true; @@ -116,8 +115,6 @@ public KeyboardHelper moveWithScroll() { /** * 设置moveView移动以TranslationY属性移动位置 - * - * @return SoftInputHelper */ public KeyboardHelper moveWithTranslation() { this.moveWithScroll = false; diff --git a/app/src/main/java/per/goweii/wanandroid/utils/MagicIndicatorUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/MagicIndicatorUtils.java index 392dfec9..e51b6c57 100644 --- a/app/src/main/java/per/goweii/wanandroid/utils/MagicIndicatorUtils.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/MagicIndicatorUtils.java @@ -1,13 +1,14 @@ package per.goweii.wanandroid.utils; import android.content.Context; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; import android.text.Html; import android.util.TypedValue; import android.view.View; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; + import net.lucode.hackware.magicindicator.MagicIndicator; import net.lucode.hackware.magicindicator.ViewPagerHelper; import net.lucode.hackware.magicindicator.buildins.commonnavigator.CommonNavigator; @@ -25,8 +26,6 @@ /** * @author CuiZhen * @date 2019/5/17 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class MagicIndicatorUtils { @@ -50,8 +49,8 @@ public IPagerTitleView getTitleView(Context context, final int index) { SimplePagerTitleView simplePagerTitleView = new SimplePagerTitleView(context); simplePagerTitleView.setText(Html.fromHtml(adapter.getPageTitle(index).toString())); simplePagerTitleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, ResUtils.getDimens(R.dimen.actionbarex_common_title_bar_text_size_def)); - simplePagerTitleView.setSelectedColor(ResUtils.getColor(context, R.color.basic_ui_action_bar_text)); - simplePagerTitleView.setNormalColor(ResUtils.getColor(context, R.color.basic_ui_action_bar_text_alpha)); + simplePagerTitleView.setSelectedColor(ResUtils.getThemeColor(context, R.attr.colorOnMainOrSurface)); + simplePagerTitleView.setNormalColor(ResUtils.getThemeColor(context, R.attr.colorOnMainOrSurfaceAlpha)); int paddingH = (int) DisplayInfoUtils.getInstance().dp2px(8); int paddingV = (int) DisplayInfoUtils.getInstance().dp2px(5); simplePagerTitleView.setPadding(paddingH, paddingV, paddingH, paddingV); @@ -96,8 +95,8 @@ public IPagerTitleView getTitleView(Context context, final int index) { SimplePagerTitleView simplePagerTitleView = new SimplePagerTitleView(context); simplePagerTitleView.setText(Html.fromHtml(adapter.getPageTitle(index).toString())); simplePagerTitleView.setTextSize(TypedValue.COMPLEX_UNIT_PX, ResUtils.getDimens(R.dimen.actionbarex_common_title_bar_title_text_size_def)); - simplePagerTitleView.setSelectedColor(ResUtils.getColor(context, R.color.basic_ui_action_bar_text)); - simplePagerTitleView.setNormalColor(ResUtils.getColor(context, R.color.basic_ui_action_bar_text_alpha)); + simplePagerTitleView.setSelectedColor(ResUtils.getThemeColor(context, R.attr.colorOnMainOrSurface)); + simplePagerTitleView.setNormalColor(ResUtils.getThemeColor(context, R.attr.colorOnMainOrSurfaceAlpha)); int paddingH = (int) DisplayInfoUtils.getInstance().dp2px(8); int paddingV = (int) DisplayInfoUtils.getInstance().dp2px(5); simplePagerTitleView.setPadding(paddingH, paddingV, paddingH, paddingV); diff --git a/app/src/main/java/per/goweii/wanandroid/utils/MultiStateUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/MultiStateUtils.java deleted file mode 100644 index b130affe..00000000 --- a/app/src/main/java/per/goweii/wanandroid/utils/MultiStateUtils.java +++ /dev/null @@ -1,69 +0,0 @@ -package per.goweii.wanandroid.utils; - -import android.view.View; - -import com.kennyc.view.MultiStateView; - -import per.goweii.basic.utils.listener.OnClickListener2; -import per.goweii.basic.utils.listener.SimpleListener; - -/** - * @author CuiZhen - * @date 2019/5/25 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public class MultiStateUtils { - - public static void toLoading(MultiStateView view){ - view.setViewState(MultiStateView.VIEW_STATE_LOADING); - } - - public static void toEmpty(MultiStateView view){ - if (view.getViewState() == MultiStateView.VIEW_STATE_CONTENT) { - return; - } - view.setViewState(MultiStateView.VIEW_STATE_EMPTY); - } - - public static void toError(MultiStateView view){ - if (view.getViewState() == MultiStateView.VIEW_STATE_CONTENT) { - return; - } - view.setViewState(MultiStateView.VIEW_STATE_ERROR); - } - - public static void toContent(MultiStateView view){ - view.setViewState(MultiStateView.VIEW_STATE_CONTENT); - } - - public static void setEmptyAndErrorClick(MultiStateView view, SimpleListener listener){ - setEmptyClick(view, listener); - setErrorClick(view, listener); - } - - public static void setEmptyClick(MultiStateView view, SimpleListener listener){ - View empty = view.getView(MultiStateView.VIEW_STATE_EMPTY); - if (empty != null) { - empty.setOnClickListener(new OnClickListener2() { - @Override - public void onClick2(View v) { - listener.onResult(); - } - }); - } - } - - public static void setErrorClick(MultiStateView view, SimpleListener listener){ - View error = view.getView(MultiStateView.VIEW_STATE_ERROR); - if (error != null) { - error.setOnClickListener(new OnClickListener2() { - @Override - public void onClick2(View v) { - listener.onResult(); - } - }); - } - } -} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/MultiStateUtils.kt b/app/src/main/java/per/goweii/wanandroid/utils/MultiStateUtils.kt new file mode 100644 index 00000000..54ac9559 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/MultiStateUtils.kt @@ -0,0 +1,117 @@ +package per.goweii.wanandroid.utils + +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import com.kennyc.view.MultiStateView +import per.goweii.basic.utils.ext.gone +import per.goweii.basic.utils.ext.visible +import per.goweii.basic.utils.listener.OnClickListener2 +import per.goweii.basic.utils.listener.SimpleListener +import per.goweii.wanandroid.R + +/** + * @author CuiZhen + * @date 2019/5/25 + * GitHub: https://github.com/goweii + */ +class MultiStateUtils { + companion object { + + @JvmStatic + fun isContent(view: MultiStateView): Boolean { + return view.viewState == MultiStateView.VIEW_STATE_CONTENT + } + + @JvmStatic + fun toLoading(view: MultiStateView) { + view.viewState = MultiStateView.VIEW_STATE_LOADING + } + + @JvmStatic + @JvmOverloads + fun toEmpty(view: MultiStateView, force: Boolean = false, icon: Int? = R.drawable.ic_empty, text: String? = view.context.getString(R.string.nothing)) { + if (force || view.viewState != MultiStateView.VIEW_STATE_CONTENT) { + view.viewState = MultiStateView.VIEW_STATE_EMPTY + } + view.getView(MultiStateView.VIEW_STATE_EMPTY)?.apply { + val iv = findViewById(R.id.iv_empty) + if (icon == null || icon <= 0) { + iv.setImageDrawable(ColorDrawable(Color.TRANSPARENT)) + iv.gone() + } else { + iv.setImageResource(icon) + iv.visible() + } + val tv = findViewById(R.id.tv_empty) + if (text.isNullOrEmpty()) { + tv.text = "" + tv.gone() + } else { + tv.text = text + tv.visible() + } + } + } + + @JvmStatic + @JvmOverloads + fun toError(view: MultiStateView, force: Boolean = false, icon: Int? = R.drawable.ic_error, text: String? = view.context.getString(R.string.how_did_it_go_wrong_again)) { + if (force || view.viewState != MultiStateView.VIEW_STATE_CONTENT) { + view.viewState = MultiStateView.VIEW_STATE_ERROR + } + view.getView(MultiStateView.VIEW_STATE_ERROR)?.apply { + val iv = findViewById(R.id.iv_error) + if (icon == null || icon <= 0) { + iv.setImageDrawable(ColorDrawable(Color.TRANSPARENT)) + iv.gone() + } else { + iv.setImageResource(icon) + iv.visible() + } + val tv = findViewById(R.id.tv_error) + if (text.isNullOrEmpty()) { + tv.text = "" + tv.gone() + } else { + tv.text = text + tv.visible() + } + } + } + + @JvmStatic + fun toContent(view: MultiStateView) { + view.viewState = MultiStateView.VIEW_STATE_CONTENT + } + + @JvmStatic + fun setEmptyAndErrorClick(view: MultiStateView, listener: SimpleListener) { + setEmptyClick(view, listener) + setErrorClick(view, listener) + } + + @JvmStatic + fun setEmptyClick(view: MultiStateView, listener: SimpleListener) { + val empty = view.getView(MultiStateView.VIEW_STATE_EMPTY) + empty?.setOnClickListener(object : OnClickListener2() { + override fun onClick2(v: View) { + listener.onResult() + } + }) + } + + @JvmStatic + fun setErrorClick(view: MultiStateView, listener: SimpleListener) { + val error = view.getView(MultiStateView.VIEW_STATE_ERROR) + error?.setOnClickListener(object : OnClickListener2() { + override fun onClick2(v: View) { + listener.onResult() + } + }) + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/NotificationHtmlParser.kt b/app/src/main/java/per/goweii/wanandroid/utils/NotificationHtmlParser.kt new file mode 100644 index 00000000..97482898 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/NotificationHtmlParser.kt @@ -0,0 +1,84 @@ +package per.goweii.wanandroid.utils + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import org.jsoup.Jsoup +import org.jsoup.nodes.Document +import per.goweii.wanandroid.module.mine.model.NotificationBean + +/** + * @author CuiZhen + * @date 2020/5/17 + */ +class NotificationHtmlParser( + private val urlPath: String // message/lg/list/ | message/lg/history/list/ +) { + + private val baseUrl = "https://wanandroid.com/" + + suspend fun get(page: Int): List { + return try { + parseDocument(request("$baseUrl$urlPath$page")) + } catch (e: Throwable) { + arrayListOf() + } + } + + suspend fun request(url: String): Document = withContext(Dispatchers.IO) { + Jsoup.connect(url).cookies(getCookies(url)).get() + } + + private suspend fun parseDocument(doc: Document): List = withContext(Dispatchers.IO) { + val list = arrayListOf() + val listArticleElements = doc.getElementsByClass("list_article list_msg") + if (listArticleElements.isNullOrEmpty()) { + return@withContext list + } + val listArticleElement = listArticleElements[0] + val infoArtElements = listArticleElement.getElementsByClass("info_art") + if (infoArtElements.isNullOrEmpty()) { + return@withContext list + } + infoArtElements.forEach { infoArtElement -> + val infoArtDivs = infoArtElement.children() + val infoArtDiv1 = infoArtDivs[0] + val tagsElements = infoArtDiv1.getElementsByClass("tags") + val tagsElement = tagsElements[0] + val tags = mutableListOf() + tagsElement.getElementsByTag("a").forEach { + tags.add(it.ownText()) + } + val aniceDate = infoArtDiv1.getElementsByClass("aniceDate")[0].ownText() + val fromUser = infoArtDiv1.getElementsByTag("em")[0].ownText().removePrefix("@") + val articleContent = infoArtDiv1.getElementsByTag("span")[2].ownText() + val deleteUrlElement = infoArtDiv1.getElementsByTag("a")[0] + var deleteUrl = deleteUrlElement.attr("href") + if (!deleteUrl.startsWith("https://") && !deleteUrl.startsWith("https://")) { + deleteUrl = "https://wanandroid.com$deleteUrl" + } + val infoArtDiv2 = infoArtDivs[1] + val ariticleUrlElement = infoArtDiv2.getElementsByTag("a")[0] + val content = ariticleUrlElement.ownText() + var ariticleUrl = ariticleUrlElement.attr("href") + if (!ariticleUrl.startsWith("https://") && !ariticleUrl.startsWith("https://")) { + ariticleUrl = "https://wanandroid.com$ariticleUrl" + } + list.add(NotificationBean(tags, aniceDate, fromUser, articleContent, content, ariticleUrl, deleteUrl)) + } + list + } + + private fun getCookies(url: String): Map { + val map: MutableMap = mutableMapOf() + val cookies = CookieUtils.loadForUrl(url) + if (cookies.isNullOrEmpty()) { + return map + } + for (cookie in cookies) { + map[cookie.name()] = cookie.value() + } + return map + } + + +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/PictureSelector.kt b/app/src/main/java/per/goweii/wanandroid/utils/PictureSelector.kt new file mode 100644 index 00000000..7e32d17d --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/PictureSelector.kt @@ -0,0 +1,145 @@ +package per.goweii.wanandroid.utils + +import android.app.Activity +import android.content.ContentValues +import android.content.Context +import android.content.CursorLoader +import android.content.Intent +import android.graphics.Bitmap +import android.net.Uri +import android.os.Build +import android.provider.MediaStore +import androidx.fragment.app.Fragment +import java.io.File + + +/** + * @author CuiZhen + * @date 2019/6/4 + * GitHub: https://github.com/goweii + */ +class PictureSelector { + companion object { + @JvmStatic + fun select(activity: Activity, requestCode: Int) = apply { + activity.startActivityForResult(selectIntent(), requestCode) + } + + @JvmStatic + fun select(fragment: Fragment, requestCode: Int) = apply { + fragment.startActivityForResult(selectIntent(), requestCode) + } + + @JvmStatic + fun crop(activity: Activity, fileSource: File, fileClip: File, requestCode: Int) = apply { + activity.startActivityForResult(cropIntent(activity, fileSource, fileClip), requestCode) + } + + @JvmStatic + fun crop(fragment: Fragment, fileSource: File, fileClip: File, requestCode: Int) = apply { + val context = fragment.context ?: return@apply + fragment.startActivityForResult(cropIntent(context, fileSource, fileClip), requestCode) + } + + private fun selectIntent(): Intent { + return Intent(Intent.ACTION_PICK).apply { + setDataAndType(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*") + } + } + + private fun cropIntent(context: Context, fileSource: File, fileClip: File): Intent { + return Intent("com.android.camera.action.CROP").apply { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + setDataAndType(getUriFromFile(context, fileSource), "image/*") + } else { + setDataAndType(Uri.fromFile(fileSource), "image/*") + } + putExtra("crop", "true") + putExtra("scale", true) + putExtra("aspectX", 1) + putExtra("aspectY", 1) + putExtra("outputX", 200) + putExtra("outputY", 200) + putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(fileClip)) + putExtra("noFaceDetection", true) + putExtra("scaleUpIfNeeded", true) + putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()) + putExtra("return-data", false) + } + } + + @JvmStatic + fun result(resultCode: Int, data: Intent?): Uri? { + if (resultCode != Activity.RESULT_OK) return null + if (null == data) return null + if (null == data.data) return null + return data.data + } + + @JvmStatic + fun getFileFormUri(context: Context, uri: Uri): File? { + val projection = arrayOf(MediaStore.Images.Media.DATA) + CursorLoader(context, uri, projection, null, null, null).loadInBackground()?.apply { + getColumnIndexOrThrow(MediaStore.Images.Media.DATA) + }?.use { cursor -> + val index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA) + cursor.moveToFirst() + return File(cursor.getString(index)) + } + var path = uri.path ?: return null + return when (uri.scheme) { + "file" -> { + val buff = StringBuffer() + buff.append("(").append(MediaStore.Images.ImageColumns.DATA).append("=").append("'$path'").append(")") + context.contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, arrayOf(MediaStore.Images.ImageColumns._ID, MediaStore.Images.ImageColumns.DATA), buff.toString(), null, null)?.use { cursor -> + var dataIdx: Int + while (!cursor.isAfterLast) { + dataIdx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA) + path = cursor.getString(dataIdx) + cursor.moveToNext() + } + File(path) + } + } + "content" -> { + context.contentResolver.query(uri, arrayOf(MediaStore.Images.Media.DATA), null, null, null)?.use { cursor -> + if (cursor.moveToFirst()) { + val columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA) + path = cursor.getString(columnIndex) + } + File(path) + } + } + else -> { + null + } + } + } + + @JvmStatic + fun getUriFromFile(context: Context, imageFile: File): Uri? { + val filePath = imageFile.absolutePath + val cursor = context.contentResolver.query( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + arrayOf(MediaStore.Images.Media._ID), + MediaStore.Images.Media.DATA + "=? ", + arrayOf(filePath), null) + cursor.use { + return if (cursor != null && cursor.moveToFirst()) { + val id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID)) + val baseUri = Uri.parse("content://media/external/images/media") + Uri.withAppendedPath(baseUri, "" + id) + } else { + if (imageFile.exists()) { + val values = ContentValues() + values.put(MediaStore.Images.Media.DATA, filePath) + context.contentResolver.insert( + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values) + } else { + null + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/PictureSelectorUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/PictureSelectorUtils.java deleted file mode 100644 index 87fc76e2..00000000 --- a/app/src/main/java/per/goweii/wanandroid/utils/PictureSelectorUtils.java +++ /dev/null @@ -1,47 +0,0 @@ -package per.goweii.wanandroid.utils; - -import android.app.Activity; -import android.content.Intent; -import android.support.v4.app.Fragment; - -import com.luck.picture.lib.PictureSelector; -import com.luck.picture.lib.config.PictureConfig; -import com.luck.picture.lib.config.PictureMimeType; -import com.luck.picture.lib.entity.LocalMedia; - -import java.util.List; - -import per.goweii.wanandroid.R; - -/** - * @author CuiZhen - * @date 2019/6/4 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public class PictureSelectorUtils { - - public static void ofImage(Fragment fragment, int requestCode) { - PictureSelector.create(fragment) - .openGallery(PictureMimeType.ofImage()) - .theme(R.style.PictureSelectorStyle) - .selectionMode(PictureConfig.SINGLE) - .enableCrop(true) - .withAspectRatio(1, 1) - .isCamera(false) - .forResult(requestCode); - } - - public static String forResult(int resultCode, Intent data) { - if (resultCode == Activity.RESULT_OK) { - // 图片、视频、音频选择结果回调 - List selectList = PictureSelector.obtainMultipleResult(data); - if (selectList != null && selectList.size() > 0) { - return selectList.get(0).getPath(); - } - } - return null; - } - -} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/PredefinedTaskQueen.kt b/app/src/main/java/per/goweii/wanandroid/utils/PredefinedTaskQueen.kt new file mode 100644 index 00000000..d415a4d1 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/PredefinedTaskQueen.kt @@ -0,0 +1,95 @@ +package per.goweii.wanandroid.utils + +class PredefinedTaskQueen { + private val tasks: MutableList = mutableListOf() + private var index = -1 + + val isCompleted: Boolean + get() = index >= tasks.size + + val isRunning: Boolean + get() = index in 0 until tasks.size + + fun append(task: Task) { + tasks.forEach { + if (it.name == task.name) { + throw IllegalArgumentException("The task name cannot be duplicated") + } + } + tasks.add(task) + } + + fun get(name: String): Task { + return tasks.find { + it.name == name + } ?: throw NullPointerException("The task is not added") + } + + fun start() { + index = 0 + executeCurrTask() + } + + private fun next() { + index++ + executeCurrTask() + } + + private fun currTask(): Task? { + if (index !in 0 until tasks.size) { + return null + } + return tasks[index] + } + + private fun executeCurrTask() { + currTask()?.let { task -> + execute(task) + } + } + + private fun execute(task: Task) { + if (task.isCompleted) { + next() + } else { + task.onCompleted = { + next() + } + task.execute() + } + } + + class Task(internal val name: String) : Completion { + private var runnable: ((Completion) -> Unit)? = null + internal var onCompleted: (() -> Unit)? = null + var isRunning = false + private set + var isCompleted = false + private set + val isRunnable: Boolean + get() = runnable != null + + internal fun execute() { + isRunning = true + runnable?.invoke(this) + } + + fun runnable(runnable: ((Completion) -> Unit)) { + this.runnable = runnable + if (isRunning) { + runnable.invoke(this) + } + } + + override fun complete() { + if (!isCompleted) { + isCompleted = true + onCompleted?.invoke() + } + } + } + + interface Completion { + fun complete() + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/RealmHelper.java b/app/src/main/java/per/goweii/wanandroid/utils/RealmHelper.java deleted file mode 100644 index 0aaa7431..00000000 --- a/app/src/main/java/per/goweii/wanandroid/utils/RealmHelper.java +++ /dev/null @@ -1,127 +0,0 @@ -package per.goweii.wanandroid.utils; - -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; - -import java.util.ArrayList; -import java.util.List; - -import io.realm.Realm; -import io.realm.RealmConfiguration; -import io.realm.RealmResults; -import io.realm.Sort; -import per.goweii.wanandroid.module.mine.model.ReadLaterEntity; - -/** - * @author CuiZhen - * @date 2019/5/26 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public class RealmHelper { - - private final Realm mRealm; - - private RealmHelper() { - RealmConfiguration configuration = new RealmConfiguration.Builder() - .name("wanandroid.realm") - .schemaVersion(1) - .build(); - mRealm = Realm.getInstance(configuration); - } - - public static RealmHelper create() { - return new RealmHelper(); - } - - public void destroy() { - mRealm.close(); - } - - public void add(String title, String link) { - final int userId; - if (UserUtils.getInstance().isLogin()) { - userId = UserUtils.getInstance().getLoginBean().getId(); - } else { - userId = 0; - } - final long time = System.currentTimeMillis(); - ReadLaterEntity entity = mRealm - .where(ReadLaterEntity.class) - .equalTo("userId", userId) - .equalTo("link", link) - .findFirst(); - if (entity != null) { - mRealm.executeTransaction(new Realm.Transaction() { - @Override - public void execute(@NonNull Realm realm) { - entity.setTitle(title); - entity.setTime(time); - } - }); - } else { - mRealm.executeTransaction(new Realm.Transaction() { - @Override - public void execute(@NonNull Realm realm) { - ReadLaterEntity entity = realm.createObject(ReadLaterEntity.class); - entity.setUserId(userId); - entity.setTitle(title); - entity.setLink(link); - entity.setTime(time); - realm.copyToRealm(entity); - } - }); - } - } - - public void delete(String link) { - final RealmResults readLaterEntities = mRealm.where(ReadLaterEntity.class) - .equalTo("link", link) - .findAll(); - if (readLaterEntities.size() == 0) { - return; - } - mRealm.executeTransaction(new Realm.Transaction() { - @Override - public void execute(@NonNull Realm realm) { - readLaterEntities.deleteFromRealm(0); - } - }); - } - - - public RealmResults get() { - final int userId; - if (UserUtils.getInstance().isLogin()) { - userId = UserUtils.getInstance().getLoginBean().getId(); - } else { - userId = 0; - } - RealmResults readLaterEntities = mRealm.where(ReadLaterEntity.class) - .equalTo("userId", userId) - .findAll() - .sort("time", Sort.DESCENDING); - return readLaterEntities; - } - - public List get(@IntRange(from = 0) int page, int perPageCount) { - RealmResults readLaterEntities = get(); - final int firstIndex = perPageCount * (page); - final int lastIndex = firstIndex + perPageCount; - if (readLaterEntities.size() - 1 < firstIndex) { - return new ArrayList<>(0); - } else { - List list = new ArrayList<>(perPageCount); - if (readLaterEntities.size() - 1 <= lastIndex) { - list.addAll(firstIndex, readLaterEntities); - } else { - for (int i = firstIndex; i <= lastIndex; i++) { - list.add(readLaterEntities.get(i)); - } - } - return list; - } - } - -} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/RecommendManager.java b/app/src/main/java/per/goweii/wanandroid/utils/RecommendManager.java new file mode 100644 index 00000000..db8c595b --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/RecommendManager.java @@ -0,0 +1,98 @@ +package per.goweii.wanandroid.utils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +import per.goweii.basic.utils.listener.SimpleCallback; +import per.goweii.rxhttp.core.RxLife; +import per.goweii.rxhttp.request.exception.ExceptionHandle; +import per.goweii.wanandroid.http.RequestListener; +import per.goweii.wanandroid.module.main.model.MainRequest; +import per.goweii.wanandroid.module.main.model.RecommendBean; + +public class RecommendManager { + private static volatile RecommendManager sInstance; + private final RxLife mRxLife; + + private volatile boolean mLoading = false; + private volatile RecommendBean mRecommendBean; + + private final List mCallbacks = new ArrayList<>(); + + private RecommendManager() { + mRxLife = RxLife.create(); + } + + @NonNull + public static RecommendManager getInstance() { + if (sInstance == null) { + synchronized (RecommendManager.class) { + if (sInstance == null) { + sInstance = new RecommendManager(); + } + } + } + return sInstance; + } + + @Nullable + public synchronized RecommendBean getBean() { + load(); + return mRecommendBean; + } + + public synchronized void getBean(@NonNull Callback callback) { + if (mLoading) { + mCallbacks.add(callback); + } else { + if (mRecommendBean != null) { + callback.onResult(mRecommendBean); + } else { + mCallbacks.add(callback); + load(); + } + } + } + + public synchronized void load() { + if (mLoading) { + return; + } + mLoading = true; + MainRequest.getRecommend(mRxLife, new RequestListener() { + @Override + public void onStart() { + } + + @Override + public void onSuccess(int code, RecommendBean data) { + mRecommendBean = data; + } + + @Override + public void onFailed(int code, String msg) { + } + + @Override + public void onError(ExceptionHandle handle) { + } + + @Override + public void onFinish() { + mLoading = false; + if (!mCallbacks.isEmpty()) { + for (Callback callback : mCallbacks) { + callback.onResult(mRecommendBean); + } + } + } + }); + } + + public interface Callback { + void onResult(@Nullable RecommendBean bean); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/RvAnimUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/RvConfigUtils.java similarity index 67% rename from app/src/main/java/per/goweii/wanandroid/utils/RvAnimUtils.java rename to app/src/main/java/per/goweii/wanandroid/utils/RvConfigUtils.java index 3ba47ad2..7de91aac 100644 --- a/app/src/main/java/per/goweii/wanandroid/utils/RvAnimUtils.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/RvConfigUtils.java @@ -1,15 +1,19 @@ package per.goweii.wanandroid.utils; import com.chad.library.adapter.base.BaseQuickAdapter; +import com.chad.library.adapter.base.BaseViewHolder; + +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.widget.BravhLoadMoreView; /** * @author CuiZhen * @date 2019/5/20 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class RvAnimUtils { +@Deprecated +public class RvConfigUtils { public static class RvAnim { public static final int NONE = 0; @@ -26,28 +30,32 @@ public static String getName(int anim) { default: break; case RvAnim.NONE: - name = "无"; + name = ResUtils.getString(R.string.rv_anim_none); break; case RvAnim.ALPHAIN: - name = "渐显"; + name = ResUtils.getString(R.string.rv_anim_alpha_in); break; case RvAnim.SCALEIN: - name = "缩放"; + name = ResUtils.getString(R.string.rv_anim_zoom); break; case RvAnim.SLIDEIN_BOTTOM: - name = "底部滑入"; + name = ResUtils.getString(R.string.rv_anim_slide_in_bottom); break; case RvAnim.SLIDEIN_LEFT: - name = "左侧滑入"; + name = ResUtils.getString(R.string.rv_anim_slide_in_left); break; case RvAnim.SLIDEIN_RIGHT: - name = "右侧滑入"; + name = ResUtils.getString(R.string.rv_anim_slide_in_right); break; } return name; } - public static void setAnim(BaseQuickAdapter adapter, int anim) { + public static void init(BaseQuickAdapter adapter) { + adapter.setLoadMoreView(new BravhLoadMoreView()); + } + + public static void setAnim(BaseQuickAdapter adapter, int anim) { switch (anim) { default: break; diff --git a/app/src/main/java/per/goweii/wanandroid/utils/RvScrollTopUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/RvScrollTopUtils.java index 2d0286ca..da483310 100644 --- a/app/src/main/java/per/goweii/wanandroid/utils/RvScrollTopUtils.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/RvScrollTopUtils.java @@ -1,18 +1,16 @@ package per.goweii.wanandroid.utils; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; /** * @author CuiZhen * @date 2019/5/30 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class RvScrollTopUtils { - public static void smoothScrollTop(RecyclerView rv){ + public static void smoothScrollTop(RecyclerView rv) { if (rv != null) { RecyclerView.LayoutManager layoutManager = rv.getLayoutManager(); if (layoutManager instanceof LinearLayoutManager) { @@ -29,4 +27,7 @@ public static void smoothScrollTop(RecyclerView rv){ } } + public interface ScrollTop { + void scrollTop(); + } } diff --git a/app/src/main/java/per/goweii/wanandroid/utils/SearchHistoryUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/SearchHistoryUtils.java index 93ae24b0..09d702d3 100644 --- a/app/src/main/java/per/goweii/wanandroid/utils/SearchHistoryUtils.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/SearchHistoryUtils.java @@ -12,8 +12,6 @@ /** * @author CuiZhen * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class SearchHistoryUtils { @@ -46,8 +44,9 @@ public List get() { return null; } try { - return mGson.fromJson(json, new TypeToken>(){}.getType()); - } catch (Exception e){ + return mGson.fromJson(json, new TypeToken>() { + }.getType()); + } catch (Exception e) { mSPUtils.clear(); return null; } diff --git a/app/src/main/java/per/goweii/wanandroid/utils/SettingUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/SettingUtils.java index 55a3cada..9214af4a 100644 --- a/app/src/main/java/per/goweii/wanandroid/utils/SettingUtils.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/SettingUtils.java @@ -1,6 +1,6 @@ package per.goweii.wanandroid.utils; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; @@ -9,20 +9,28 @@ import java.util.List; import per.goweii.basic.utils.SPUtils; +import per.goweii.wanandroid.module.explore.DailyNewsPlatform; import per.goweii.wanandroid.module.mine.model.HostEntity; +import per.goweii.wanandroid.utils.ai.DeepSeek; +import per.goweii.wanandroid.utils.web.HostInterceptUtils; /** * @author CuiZhen * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class SettingUtils { private static final String SP_NAME = "setting"; + private static final String KEY_THEME_MODE = "KEY_THEME_MODE"; + @Deprecated + private static final String KEY_SYSTEM_THEME = "KEY_SYSTEM_THEME"; + @Deprecated private static final String KEY_DARK_THEME = "KEY_DARK_THEME"; private static final String KEY_SHOW_READ_LATER = "KEY_SHOW_READ_LATER"; + private static final String KEY_SHOW_READ_LATER_NOTIFICATION = "KEY_SHOW_READ_LATER_NOTIFICATION"; + private static final String KEY_SHOW_READ_RECORD = "KEY_SHOW_READ_RECORD"; + private static final String KEY_SHOW_QA = "KEY_SHOW_QA"; private static final String KEY_SHOW_TOP = "KEY_SHOW_TOP"; private static final String KEY_SHOW_BANNER = "KEY_SHOW_BANNER"; private static final String KEY_HIDE_ABOUT_ME = "KEY_HIDE_ABOUT_ME"; @@ -34,22 +42,50 @@ public class SettingUtils { private static final String KEY_HOST_BLACK = "KEY_HOST_BLACK"; private static final String KEY_SEARCH_HISTORY_MAX_COUNT = "KEY_SEARCH_HISTORY_MAX_COUNT"; private static final String KEY_UPDATE_IGNORE_DURATION = "KEY_UPDATE_IGNORE_DURATION"; + private static final String KEY_DAILY_NEWS_PLATFORM = "KEY_DAILY_NEWS_PLATFORM"; + private static final String KEY_AI_ENABLED = "KEY_AI_ENABLED"; + private static final String KEY_AI_PROVIDER = "KEY_AI_PROVIDER"; + private static final String KEY_AI_MODEL = "KEY_AI_MODEL"; + private static final String KEY_AI_API_KEY = "KEY_AI_API_KEY"; + + public enum ThemeMode { + FOLLOW_SYSTEM(0), LIGHT(1), DARK(2), + ; + + public final int value; + + ThemeMode(int value) { + this.value = value; + } + + @NonNull + public static ThemeMode fromValue(int value) { + for (ThemeMode themeMode : values()) { + if (themeMode.value == value) { + return themeMode; + } + } + return ThemeMode.FOLLOW_SYSTEM; + } + } private final SPUtils mSPUtils = SPUtils.newInstance(SP_NAME); - private boolean mDarkTheme = false; - private boolean mShowReadLater = true; + private ThemeMode mThemeMode = ThemeMode.FOLLOW_SYSTEM; + private boolean mShowReadLaterNotification = true; + private boolean mShowQA = false; private boolean mShowTop = true; private boolean mShowBanner = true; - private boolean mHideAboutMe = false; - private boolean mHideOpen = false; - private boolean mWebSwipeBackEdge = true; - private int mRvAnim = RvAnimUtils.RvAnim.NONE; private int mUrlInterceptType = HostInterceptUtils.TYPE_NOTHING; private final List mHostWhite = new ArrayList<>(); private final List mHostBlack = new ArrayList<>(); - private int mSearchHistoryMaxCount = 20; - private long mUpdateIgnoreDuration = 7 * 24 * 60 * 60 * 1000L; + private int mSearchHistoryMaxCount = 100; + private long mUpdateIgnoreDuration = 1 * 24 * 60 * 60 * 1000L; + private DailyNewsPlatform mDailyNewsPlatform = DailyNewsPlatform.JUEJIN; + private boolean mAiEnabled = false; + private String mAiProvider = DeepSeek.INSTANCE.getId(); + private String mAiModel = DeepSeek.INSTANCE.getModels().get(0).getId(); + private String mAiApiKey = ""; private static class Holder { private static final SettingUtils INSTANCE = new SettingUtils(); @@ -60,17 +96,29 @@ public static SettingUtils getInstance() { } private SettingUtils() { - mDarkTheme = mSPUtils.get(KEY_DARK_THEME, mDarkTheme); - mShowReadLater = mSPUtils.get(KEY_SHOW_READ_LATER, mShowReadLater); + if (mSPUtils.has(KEY_THEME_MODE)) { + mThemeMode = ThemeMode.fromValue(mSPUtils.get(KEY_THEME_MODE, mThemeMode.value)); + } else { + if (mSPUtils.get(KEY_SYSTEM_THEME, false)) { + mThemeMode = ThemeMode.FOLLOW_SYSTEM; + } else if (mSPUtils.get(KEY_DARK_THEME, false)) { + mThemeMode = ThemeMode.DARK; + } else { + mThemeMode = ThemeMode.LIGHT; + } + } + mShowReadLaterNotification = mSPUtils.get(KEY_SHOW_READ_LATER_NOTIFICATION, mShowReadLaterNotification); + mShowQA = mSPUtils.get(KEY_SHOW_QA, mShowQA); mShowTop = mSPUtils.get(KEY_SHOW_TOP, mShowTop); mShowBanner = mSPUtils.get(KEY_SHOW_BANNER, mShowBanner); - mHideAboutMe = mSPUtils.get(KEY_HIDE_ABOUT_ME, mHideAboutMe); - mHideOpen = mSPUtils.get(KEY_HIDE_OPEN, mHideOpen); - mWebSwipeBackEdge = mSPUtils.get(KEY_WEB_SWIPE_BACK_EDGE, mWebSwipeBackEdge); - mRvAnim = mSPUtils.get(KEY_RV_ANIM, mRvAnim); mUrlInterceptType = mSPUtils.get(KEY_URL_INTERCEPT_TYPE, mUrlInterceptType); mSearchHistoryMaxCount = mSPUtils.get(KEY_SEARCH_HISTORY_MAX_COUNT, mSearchHistoryMaxCount); mUpdateIgnoreDuration = mSPUtils.get(KEY_UPDATE_IGNORE_DURATION, mUpdateIgnoreDuration); + mDailyNewsPlatform = DailyNewsPlatform.fromCode(mSPUtils.get(KEY_DAILY_NEWS_PLATFORM, mDailyNewsPlatform.getPlatformCode())); + mAiEnabled = mSPUtils.get(KEY_AI_ENABLED, mAiEnabled); + mAiProvider = mSPUtils.get(KEY_AI_PROVIDER, mAiProvider); + mAiModel = mSPUtils.get(KEY_AI_MODEL, mAiModel); + mAiApiKey = mSPUtils.get(KEY_AI_API_KEY, mAiApiKey); Gson gson = new Gson(); String jsonWhite = mSPUtils.get(KEY_HOST_WHITE, ""); try { @@ -96,22 +144,22 @@ private SettingUtils() { } } - public void setDarkTheme(boolean darkTheme) { - mDarkTheme = darkTheme; - mSPUtils.save(KEY_DARK_THEME, darkTheme); + public void setThemeMode(@NonNull ThemeMode themeMode) { + mThemeMode = themeMode; + mSPUtils.save(KEY_THEME_MODE, themeMode.value); } - public boolean isDarkTheme() { - return mDarkTheme; + public ThemeMode getThemeMode() { + return mThemeMode; } - public void setShowReadLater(boolean showReadLater) { - mShowReadLater = showReadLater; - mSPUtils.save(KEY_SHOW_READ_LATER, showReadLater); + public void setShowReadLaterNotification(boolean showReadLaterNotification) { + mShowReadLaterNotification = showReadLaterNotification; + mSPUtils.save(KEY_SHOW_READ_LATER_NOTIFICATION, showReadLaterNotification); } - public boolean isShowReadLater() { - return mShowReadLater; + public boolean isShowReadLaterNotification() { + return mShowReadLaterNotification; } public void setShowTop(boolean showTop) { @@ -123,6 +171,15 @@ public boolean isShowTop() { return mShowTop; } + public void setShowQA(boolean showQA) { + mShowQA = showQA; + mSPUtils.save(KEY_SHOW_QA, showQA); + } + + public boolean isShowQA() { + return mShowQA; + } + public void setShowBanner(boolean showBanner) { mShowBanner = showBanner; mSPUtils.save(KEY_SHOW_BANNER, showBanner); @@ -132,33 +189,6 @@ public boolean isShowBanner() { return mShowBanner; } - public void setHideAboutMe(boolean hideAboutMe) { - mHideAboutMe = hideAboutMe; - mSPUtils.save(KEY_HIDE_ABOUT_ME, hideAboutMe); - } - - public boolean isHideAboutMe() { - return mHideAboutMe; - } - - public void setHideOpen(boolean hideOpen) { - mHideOpen = hideOpen; - mSPUtils.save(KEY_HIDE_OPEN, hideOpen); - } - - public boolean isHideOpen() { - return mHideOpen; - } - - public void setWebSwipeBackEdge(boolean webSwipeBackEdge) { - mWebSwipeBackEdge = webSwipeBackEdge; - mSPUtils.save(KEY_WEB_SWIPE_BACK_EDGE, webSwipeBackEdge); - } - - public boolean isWebSwipeBackEdge() { - return mWebSwipeBackEdge; - } - public void setUrlInterceptType(int type) { mUrlInterceptType = type; mSPUtils.save(KEY_URL_INTERCEPT_TYPE, type); @@ -190,15 +220,6 @@ public List getHostBlackIntercept() { return mHostBlack; } - public void setRvAnim(int anim) { - mRvAnim = anim; - mSPUtils.save(KEY_RV_ANIM, anim); - } - - public int getRvAnim() { - return mRvAnim; - } - public void setSearchHistoryMaxCount(int count) { mSearchHistoryMaxCount = count; mSPUtils.save(KEY_SEARCH_HISTORY_MAX_COUNT, count); @@ -216,4 +237,49 @@ public void setUpdateIgnoreDuration(long dur) { public long getUpdateIgnoreDuration() { return mUpdateIgnoreDuration; } + + public void setDailyNewsPlatform(@NonNull DailyNewsPlatform platform) { + mDailyNewsPlatform = platform; + mSPUtils.save(KEY_DAILY_NEWS_PLATFORM, platform.getPlatformCode()); + } + + public DailyNewsPlatform getDailyNewsPlatform() { + return mDailyNewsPlatform; + } + + public void setAiEnabled(boolean enabled) { + mAiEnabled = enabled; + mSPUtils.save(KEY_AI_ENABLED, enabled); + } + + public boolean isAiEnabled() { + return mAiEnabled; + } + + public void setAiProvider(String aiProvider) { + mAiProvider = aiProvider; + mSPUtils.save(KEY_AI_PROVIDER, aiProvider); + } + + public String getAiProvider() { + return mAiProvider; + } + + public void setAiModel(String aiModel) { + mAiModel = aiModel; + mSPUtils.save(KEY_AI_MODEL, aiModel); + } + + public String getAiModel() { + return mAiModel; + } + + public void setAiApiKey(String aiApiKey) { + mAiApiKey = aiApiKey; + mSPUtils.save(KEY_AI_API_KEY, aiApiKey); + } + + public String getAiApiKey() { + return mAiApiKey; + } } diff --git a/app/src/main/java/per/goweii/wanandroid/utils/TaskQueen.kt b/app/src/main/java/per/goweii/wanandroid/utils/TaskQueen.kt new file mode 100644 index 00000000..0c4441ab --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/TaskQueen.kt @@ -0,0 +1,52 @@ +package per.goweii.wanandroid.utils + +/** + * @author CuiZhen + * @date 2020/2/15 + * GitHub: https://github.com/goweii + */ +class TaskQueen { + private val tasks: MutableList = mutableListOf() + + fun append(task: Task) { + if (tasks.isEmpty()) { + tasks.add(task) + run(task) + } else { + var index = tasks.size + for (i in tasks.size - 1 downTo 0) { + val t = tasks[i] + if (task.level <= t.level) { + break + } + index = i + } + tasks.add(index, task) + } + } + + private fun next() { + if (tasks.isEmpty()) return + run(tasks[0]) + } + + private fun run(task: Task) { + task.onFinished = { + tasks.remove(task) + next() + } + task.run() + } + + abstract class Task( + val level: Int = 0 + ) { + internal var onFinished: (() -> Unit)? = null + + abstract fun run() + + fun complete() { + onFinished?.invoke() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/ThemeUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/ThemeUtils.java new file mode 100644 index 00000000..e6048ca4 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/ThemeUtils.java @@ -0,0 +1,131 @@ +package per.goweii.wanandroid.utils; + +import android.content.ComponentName; +import android.content.Context; +import android.content.pm.PackageManager; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +import per.goweii.basic.utils.SPUtils; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.module.main.activity.MainActivity; +import per.goweii.wanandroid.module.main.activity.theme.MainActivityGold; +import per.goweii.wanandroid.module.main.activity.theme.MainActivityGreen; +import per.goweii.wanandroid.module.main.activity.theme.MainActivityPink; +import per.goweii.wanandroid.module.main.activity.theme.MainActivityRed; + +public class ThemeUtils { + private static final String THEME_NAME_RED = "red"; + private static final String THEME_NAME_GREEN = "green"; + private static final String THEME_NAME_PINK = "pink"; + private static final String THEME_NAME_GOLD = "gold"; + + private static final ThemeData THEME_DEF; + private static final List THEMES = new ArrayList<>(); + + private static final String SP_NAME = "launcher_theme"; + private static final String SP_KEY_WILL_INSTALL = "willInstall"; + private static SPUtils sSPUtils = null; + + static { + THEME_DEF = new ThemeData(null, 0, MainActivity.class.getName()); + THEMES.add(THEME_DEF); + THEMES.add(new ThemeData(THEME_NAME_RED, R.style.ThemeRed, MainActivityRed.class.getName())); + THEMES.add(new ThemeData(THEME_NAME_GREEN, R.style.ThemeGreen, MainActivityGreen.class.getName())); + THEMES.add(new ThemeData(THEME_NAME_PINK, R.style.ThemePink, MainActivityPink.class.getName())); + THEMES.add(new ThemeData(THEME_NAME_GOLD, R.style.ThemeGold, MainActivityGold.class.getName())); + } + + public static boolean isWillInstall() { + if (sSPUtils == null) return false; + return sSPUtils.get(SP_KEY_WILL_INSTALL, false); + } + + public static void setWillInstall() { + if (sSPUtils == null) sSPUtils = SPUtils.newInstance(SP_NAME); + sSPUtils.save(SP_KEY_WILL_INSTALL, true); + } + + public static void setNotInstall() { + if (sSPUtils == null) sSPUtils = SPUtils.newInstance(SP_NAME); + sSPUtils.save(SP_KEY_WILL_INSTALL, false); + } + + @NonNull + public static ThemeData getThemeData(String themeName) { + if (TextUtils.isEmpty(themeName)) return THEME_DEF; + for (ThemeData themeData : THEMES) { + if (themeName.equals(themeData.themeName)) { + return themeData; + } + } + return THEME_DEF; + } + + public static int getTheme(String themeName) { + return getThemeData(themeName).themeId; + } + + public static boolean isDefLauncher(Context context) { + try { + PackageManager pm = context.getPackageManager(); + ComponentName componentName = new ComponentName(context, THEME_DEF.activityName); + int state = pm.getComponentEnabledSetting(componentName); + return state == PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + } catch (Exception e) { + e.printStackTrace(); + } + return false; + } + + public static void resetLauncher(Context context) { + updateLauncher(context, null); + } + + public static void updateLauncher(Context context, String themeName) { + ThemeData newThemeData = getThemeData(themeName); + PackageManager pm = context.getPackageManager(); + for (ThemeData themeData : THEMES) { + try { + ComponentName componentName = new ComponentName(context, themeData.activityName); + int state = pm.getComponentEnabledSetting(componentName); + if (!TextUtils.equals(themeData.activityName, newThemeData.activityName)) { + if (state != PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { + pm.setComponentEnabledSetting(componentName, + PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + try { + ComponentName componentName = new ComponentName(context, newThemeData.activityName); + int state = pm.getComponentEnabledSetting(componentName); + if (state != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) { + pm.setComponentEnabledSetting(componentName, + PackageManager.COMPONENT_ENABLED_STATE_ENABLED, + PackageManager.DONT_KILL_APP); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + public static class ThemeData { + public final String themeName; + public final int themeId; + public final String activityName; + + public ThemeData(String themeName, int themeId, String activityName) { + this.themeName = themeName; + this.themeId = themeId; + this.activityName = activityName; + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/UpdateUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/UpdateUtils.java index d4b83b2e..b1f7fedf 100644 --- a/app/src/main/java/per/goweii/wanandroid/utils/UpdateUtils.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/UpdateUtils.java @@ -1,55 +1,158 @@ package per.goweii.wanandroid.utils; +import androidx.annotation.NonNull; + import java.util.Date; import per.goweii.basic.utils.AppInfoUtils; import per.goweii.basic.utils.SPUtils; +import per.goweii.wanandroid.BuildConfig; +import per.goweii.wanandroid.module.main.model.UpdateBean; /** * @author CuiZhen * @date 2019/5/19 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class UpdateUtils { - private static final String SP_NAME = "update"; + private static final String KEY_LAST_CHECK_TIME = "KEY_LAST_CHECK_TIME"; private static final String KEY_VERSION_CODE = "KEY_VERSION_CODE"; private static final String KEY_TIME = "KEY_TIME"; - private static final String KEY_LAST_CHECK_TIME = "KEY_LAST_CHECK_TIME"; + private static final String KEY_BETA_VERSION_CODE = "KEY_BETA_VERSION_CODE"; + private static final String KEY_BETA_VERSION_NAME = "KEY_BETA_VERSION_NAME"; + private static final String KEY_BETA_TIME = "KEY_TEST_TIME"; + + private static final String BETA = "beta"; private final SPUtils mSPUtils = SPUtils.newInstance(SP_NAME); - public static UpdateUtils newInstance() { - return new UpdateUtils(); + private static volatile UpdateUtils INSTANCE = null; + + @NonNull + public static UpdateUtils getInstance() { + if (INSTANCE == null) { + synchronized (UpdateUtils.class) { + if (INSTANCE == null) { + INSTANCE = new UpdateUtils(); + } + } + } + return INSTANCE; } private UpdateUtils() { } + public boolean isEnabled() { + return BuildConfig.ENABLE_IN_APP_UPDATE; + } + public void ignore(int versionCode) { mSPUtils.save(KEY_VERSION_CODE, versionCode); mSPUtils.save(KEY_TIME, System.currentTimeMillis()); } - public boolean shouldUpdate(int versionCode) { - if (!isNewest(versionCode)) { - return false; - } + public boolean shouldUpdate(UpdateBean updateBean) { + if (!isEnabled()) return false; + return shouldUpdate(updateBean.getVersion_code(), updateBean.getVersion_name()); + } + + public boolean shouldUpdate(int versionCode, String versionName) { + if (!isEnabled()) return false; + if (!isNewest(versionCode, versionName)) return false; int ignoreCode = mSPUtils.get(KEY_VERSION_CODE, 0); - if (versionCode > ignoreCode) { - return true; - } + if (versionCode > ignoreCode) return true; long currTime = System.currentTimeMillis(); long ignoreTime = mSPUtils.get(KEY_TIME, 0L); long duration = SettingUtils.getInstance().getUpdateIgnoreDuration(); return currTime - ignoreTime > duration; } - public boolean isNewest(int versionCode) { - int currCode = AppInfoUtils.getVersionCode(); - return versionCode > currCode; + public boolean shouldForceUpdate(UpdateBean updateBean) { + if (!isEnabled()) return false; + return shouldForceUpdate( + updateBean.isForce(), + updateBean.getVersion_code(), updateBean.getVersion_name(), + updateBean.getLast_force_version_code(), updateBean.getLast_force_version_name() + ); + } + + public boolean shouldForceUpdate( + boolean netCurrIsForce, + int netCurrVersionCode, String netCurrVersionName, + int netLastForceVersionCode, String netLastForceVersionName + ) { + if (!isEnabled()) return false; + boolean isLastForceNewest = isNewest(netLastForceVersionCode, netLastForceVersionName); + if (isLastForceNewest) return true; + boolean isCurrNewest = isNewest(netCurrVersionCode, netCurrVersionName); + if (isCurrNewest) return netCurrIsForce; + return false; + } + + public void ignoreBeta(String versionName, int versionCode) { + mSPUtils.save(KEY_BETA_VERSION_NAME, versionName); + mSPUtils.save(KEY_BETA_VERSION_CODE, versionCode); + mSPUtils.save(KEY_BETA_TIME, System.currentTimeMillis()); + } + + public boolean shouldUpdateBeta(UpdateBean updateBean) { + if (!isEnabled()) return false; + return shouldUpdateBeta(updateBean.getVersion_code(), updateBean.getVersion_name()); + } + + public boolean shouldUpdateBeta(int versionCode, String versionName) { + if (!isEnabled()) return false; + if (!isNewest(versionCode, versionName)) return false; + int ignoreCode = mSPUtils.get(KEY_BETA_VERSION_CODE, 0); + if (versionCode > ignoreCode) return true; + String ignoreName = mSPUtils.get(KEY_BETA_VERSION_NAME, ""); + int ignoreBetaCode = getBetaCode(ignoreName); + int betaCode = getBetaCode(versionName); + if (betaCode > ignoreBetaCode) return true; + long currTime = System.currentTimeMillis(); + long ignoreTime = mSPUtils.get(KEY_BETA_TIME, 0L); + long duration = SettingUtils.getInstance().getUpdateIgnoreDuration(); + return currTime - ignoreTime > duration; + } + + public boolean isNewest(UpdateBean updateBean) { + int localCode = AppInfoUtils.getVersionCode(); + String localName = AppInfoUtils.getVersionName(); + return isNewest(localCode, localName, updateBean.getVersion_code(), updateBean.getVersion_name()); + } + + public boolean isNewest(int netCode, String netName) { + int localCode = AppInfoUtils.getVersionCode(); + String localName = AppInfoUtils.getVersionName(); + return isNewest(localCode, localName, netCode, netName); + } + + public boolean isNewest(int oldCode, String oldName, int newCode, String newName) { + if (newCode > oldCode) return true; + if (newCode < oldCode) return false; + boolean localBeta = oldName.contains(BETA); + boolean netBeta = newName.contains(BETA); + if (!localBeta) return false; + if (!netBeta) return true; + int localBetaCode = getBetaCode(oldName); + int netBetaCode = getBetaCode(newName); + return netBetaCode > localBetaCode; + } + + private int getBetaCode(String versionName) { + int betaCode = 0; + if (versionName.contains(BETA)) { + String[] betaSub = versionName.split(BETA); + if (betaSub.length == 2) { + try { + betaCode = Integer.parseInt(betaSub[1]); + } catch (Exception ignore) { + } + } + } + return betaCode; } public boolean isTodayChecked() { diff --git a/app/src/main/java/per/goweii/wanandroid/utils/UrlOpenUtils.kt b/app/src/main/java/per/goweii/wanandroid/utils/UrlOpenUtils.kt new file mode 100644 index 00000000..727f9f1c --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/UrlOpenUtils.kt @@ -0,0 +1,82 @@ +package per.goweii.wanandroid.utils + +import android.content.Context +import android.net.Uri +import per.goweii.wanandroid.module.main.activity.ArticleActivity +import per.goweii.wanandroid.module.main.activity.WebActivity +import per.goweii.wanandroid.module.main.model.ArticleBean +import androidx.core.net.toUri + +/** + * @author CuiZhen + * @date 2020/2/23 + */ +class UrlOpenUtils( + private val url: String +) { + private var title: String = "" + + private var articleId: Int = 0 + private var collectId: Int = 0 + private var author: String = "" + private var collected: Boolean = false + + private var userId: Int = 0 + + private var forceWeb: Boolean = false + + companion object { + fun with(url: String?) = UrlOpenUtils(url ?: "") + + fun with(article: ArticleBean?) = with(article?.link).apply { + article?.let { + articleId(if (it.originId != 0) it.originId else it.id) + title(it.title) + collected(it.isCollect) + author(it.author) + userId(it.userId) + } + } + } + + fun title(title: String) = apply { + this.title = title + } + + fun articleId(articleId: Int) = apply { + this.articleId = articleId + } + + fun collectId(collectId: Int) = apply { + this.collectId = collectId + } + + fun author(author: String) = apply { + this.author = author + } + + fun collected(collected: Boolean) = apply { + this.collected = collected + } + + fun userId(userId: Int) = apply { + this.userId = userId + } + + fun forceWeb() = apply { + this.forceWeb = true + } + + fun open(context: Context?) { + context ?: return + var url = this.url + if (url.toUri().host == "www.wanandroid.com") { + url = url.replace("www.wanandroid.com", "wanandroid.com"); + } + if (!forceWeb && articleId > 0) { + ArticleActivity.start(context, url, title, articleId, collected, author, userId) + } else { + WebActivity.start(context, url, title, articleId, collectId, collected) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/UserInfoUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/UserInfoUtils.java deleted file mode 100644 index 2d765b99..00000000 --- a/app/src/main/java/per/goweii/wanandroid/utils/UserInfoUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -package per.goweii.wanandroid.utils; - -import per.goweii.basic.utils.SPUtils; - -/** - * @author CuiZhen - * @date 2019/5/18 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public class UserInfoUtils { - - private static final String SP_NAME = "user_info"; - private static final String KEY_ICON = "KEY_ICON"; - private static final String KEY_BG = "KEY_BG"; - - private final SPUtils mSPUtils = SPUtils.newInstance(SP_NAME); - - private static class Holder { - private static final UserInfoUtils INSTANCE = new UserInfoUtils(); - } - - public static UserInfoUtils getInstance() { - return Holder.INSTANCE; - } - - private UserInfoUtils() { - } - - public void setIcon(String icon) { - mSPUtils.save(KEY_ICON, icon); - } - - public String getIcon() { - return mSPUtils.get(KEY_ICON, ""); - } - - public void setBg(String icon) { - mSPUtils.save(KEY_BG, icon); - } - - public String getBg() { - return mSPUtils.get(KEY_BG, ""); - } -} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/UserUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/UserUtils.java index 9ce2d32f..927f8610 100644 --- a/app/src/main/java/per/goweii/wanandroid/utils/UserUtils.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/UserUtils.java @@ -6,21 +6,20 @@ import com.google.gson.Gson; import per.goweii.basic.utils.SPUtils; -import per.goweii.wanandroid.module.login.activity.LoginActivity; +import per.goweii.wanandroid.module.login.activity.AuthActivity; import per.goweii.wanandroid.module.login.model.LoginBean; +import per.goweii.wanandroid.module.login.model.UserEntity; /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class UserUtils { - private static final String KEY_LOGIN_BEAN = "KEY_LOGIN_BEAN"; + private static final String KEY_LOGIN_USER_ENTITY = "KEY_LOGIN_USER_ENTITY"; - private LoginBean mLoginBean = null; + private UserEntity mUserEntity = null; private static class Holder { private static final UserUtils INSTANCE = new UserUtils(); @@ -31,54 +30,66 @@ public static UserUtils getInstance() { } private UserUtils() { - getLoginBean(); + getLoginUser(); } - public LoginBean getLoginBean() { - if (mLoginBean == null) { - String json = SPUtils.getInstance().get(KEY_LOGIN_BEAN, ""); + public UserEntity getLoginUser() { + if (mUserEntity == null) { + String json = SPUtils.getInstance().get(KEY_LOGIN_USER_ENTITY, ""); if (!TextUtils.isEmpty(json)) { try { - mLoginBean = new Gson().fromJson(json, LoginBean.class); + mUserEntity = new Gson().fromJson(json, UserEntity.class); } catch (Exception ignore) { } } } - return mLoginBean; + return mUserEntity; } public void login(LoginBean loginBean) { - mLoginBean = loginBean; - String json = new Gson().toJson(loginBean); - SPUtils.getInstance().save(KEY_LOGIN_BEAN, json); + mUserEntity = new UserEntity( + loginBean.getEmail(), + loginBean.getUsername(), + loginBean.getId(), + 0, + null, + null, + null + ); + SPUtils.getInstance().save(KEY_LOGIN_USER_ENTITY, new Gson().toJson(mUserEntity)); } public void logout() { - mLoginBean = null; + mUserEntity = null; SPUtils.getInstance().clear(); } - public void update(LoginBean loginBean) { - mLoginBean = loginBean; - SPUtils.getInstance().save(KEY_LOGIN_BEAN, mLoginBean); + public void update(UserEntity userEntity) { + mUserEntity = userEntity; + SPUtils.getInstance().save(KEY_LOGIN_USER_ENTITY, new Gson().toJson(userEntity)); } public boolean isLogin() { - LoginBean loginBean = getLoginBean(); + UserEntity loginBean = getLoginUser(); if (loginBean == null) { return false; } - if (loginBean.getId() > 0) { - return true; + return loginBean.getWanid() > 0; + } + + public int getWanId() { + UserEntity loginBean = getLoginUser(); + if (loginBean == null) { + return 0; } - return false; + return loginBean.getWanid(); } public boolean doIfLogin(Context context) { if (isLogin()) { return true; } else { - LoginActivity.start(context); + AuthActivity.startQuickLogin(context); return false; } } diff --git a/app/src/main/java/per/goweii/wanandroid/utils/WebHolder.java b/app/src/main/java/per/goweii/wanandroid/utils/WebHolder.java deleted file mode 100644 index fb927fda..00000000 --- a/app/src/main/java/per/goweii/wanandroid/utils/WebHolder.java +++ /dev/null @@ -1,398 +0,0 @@ -package per.goweii.wanandroid.utils; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.graphics.Bitmap; -import android.graphics.PixelFormat; -import android.net.Uri; -import android.os.Build; -import android.support.annotation.NonNull; -import android.support.annotation.RequiresApi; -import android.text.TextUtils; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.widget.FrameLayout; - -import com.tencent.smtt.export.external.extension.interfaces.IX5WebSettingsExtension; -import com.tencent.smtt.export.external.interfaces.IX5WebSettings; -import com.tencent.smtt.export.external.interfaces.WebResourceRequest; -import com.tencent.smtt.export.external.interfaces.WebResourceResponse; -import com.tencent.smtt.sdk.CookieManager; -import com.tencent.smtt.sdk.CookieSyncManager; -import com.tencent.smtt.sdk.WebChromeClient; -import com.tencent.smtt.sdk.WebSettings; -import com.tencent.smtt.sdk.WebView; -import com.tencent.smtt.sdk.WebViewClient; - -import java.util.List; - -import me.zhanghai.android.materialprogressbar.MaterialProgressBar; -import okhttp3.Cookie; -import okhttp3.HttpUrl; -import per.goweii.wanandroid.R; -import per.goweii.wanandroid.common.WanApp; -import per.goweii.wanandroid.widget.WebContainer; -import per.goweii.wanandroid.widget.X5WebView; - -/** - * @author CuiZhen - * @date 2019/10/20 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public class WebHolder { - private OnPageTitleCallback mOnPageTitleCallback = null; - private OnPageLoadCallback mOnPageLoadCallback = null; - private OnPageProgressCallback mOnPageProgressCallback = null; - private OnHistoryUpdateCallback mOnHistoryUpdateCallback = null; - - private boolean isProgressShown = false; - private WebView mWebView; - private final MaterialProgressBar mProgressBar; - - public static WebHolder with(Activity activity, WebContainer container) { - return new WebHolder(activity, container); - } - - private static void syncCookiesForWanAndroid(String url) { - if (TextUtils.isEmpty(url)) { - return; - } - String host = Uri.parse(url).getHost(); - if (!TextUtils.equals(host, "www.wanandroid.com")) { - return; - } - List cookies = WanApp.getCookieJar().loadForRequest(HttpUrl.get(url)); - if (cookies == null || cookies.isEmpty()) { - return; - } - CookieManager cookieManager = CookieManager.getInstance(); - cookieManager.setAcceptCookie(true); - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - cookieManager.removeSessionCookie(); - cookieManager.removeExpiredCookie(); - } else { - cookieManager.removeSessionCookies(null); - } - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { - for (Cookie cookie : cookies) { - cookieManager.setCookie(url, cookie.name() + "=" + cookie.value()); - } - CookieSyncManager.createInstance(WanApp.getAppContext()); - CookieSyncManager.getInstance().sync(); - } else { - for (Cookie cookie : cookies) { - cookieManager.setCookie(url, cookie.name() + "=" + cookie.value()); - } - cookieManager.flush(); - } - } - - @SuppressLint("SetJavaScriptEnabled") - private WebHolder(Activity activity, WebContainer container) { - activity.getWindow().setFormat(PixelFormat.TRANSLUCENT); - mWebView = new X5WebView(activity); - mProgressBar = (MaterialProgressBar) LayoutInflater.from(activity).inflate(R.layout.basic_ui_progress_bar, container, false); - mProgressBar.setMax(100); - container.addView(mWebView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT)); - container.addView(mProgressBar, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, - activity.getResources().getDimensionPixelSize(R.dimen.basic_ui_action_bar_loading_bar_height))); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView, true); - } - mWebView.setOverScrollMode(WebView.OVER_SCROLL_NEVER); - mWebView.getView().setOverScrollMode(View.OVER_SCROLL_NEVER); - mWebView.setWebChromeClient(new WanWebChromeClient()); - mWebView.setWebViewClient(new WanWebViewClient()); - WebSettings webSetting = mWebView.getSettings(); - webSetting.setJavaScriptEnabled(true); - webSetting.setJavaScriptCanOpenWindowsAutomatically(false); - webSetting.setAllowFileAccess(true); - webSetting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS); - webSetting.setSupportZoom(false); - webSetting.setBuiltInZoomControls(false); - webSetting.setUseWideViewPort(true); - webSetting.setSupportMultipleWindows(true); - webSetting.setLoadWithOverviewMode(true); - webSetting.setAppCacheEnabled(true); - webSetting.setDomStorageEnabled(true); - webSetting.setGeolocationEnabled(true); - webSetting.setAppCacheMaxSize(Long.MAX_VALUE); - webSetting.setPluginState(WebSettings.PluginState.ON_DEMAND); - webSetting.setRenderPriority(WebSettings.RenderPriority.HIGH); - webSetting.setCacheMode(WebSettings.LOAD_DEFAULT); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - webSetting.setMixedContentMode(android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); - } - IX5WebSettingsExtension ext = mWebView.getSettingsExtension(); - if (ext != null) { - ext.setPageCacheCapacity(IX5WebSettings.DEFAULT_CACHE_CAPACITY); - } - } - - public WebHolder loadUrl(String url) { - mWebView.loadUrl(url); - return this; - } - - @NonNull - public String getUrl() { - String url = mWebView.getUrl(); - return url == null ? "" : url; - } - - @NonNull - public String getTitle() { - String title = mWebView.getTitle(); - return title == null ? "" : title; - } - - public boolean canGoBack() { - return mWebView.canGoBack(); - } - - public boolean canGoForward() { - return mWebView.canGoForward(); - } - - public boolean canGoBackOrForward(int steps) { - return mWebView.canGoBackOrForward(steps); - } - - public void goBack() { - mWebView.goBack(); - } - - public void goForward() { - mWebView.goForward(); - } - - public void goBackOrForward(int steps) { - mWebView.goBackOrForward(steps); - } - - public void reload() { - mWebView.reload(); - } - - public void stopLoading() { - mWebView.stopLoading(); - } - - public void onPause() { - mWebView.onPause(); - } - - public void onResume() { - mWebView.onResume(); - } - - public void onDestroy() { - ViewParent parent = mWebView.getParent(); - if (parent != null) { - ((ViewGroup) parent).removeView(mWebView); - } - mWebView.removeAllViews(); - mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null); - mWebView.stopLoading(); - mWebView.setWebChromeClient(null); - mWebView.setWebViewClient(null); - mWebView.destroy(); - } - - public boolean handleKeyEvent(int keyCode, KeyEvent keyEvent) { - if (keyCode != KeyEvent.KEYCODE_BACK) { - return false; - } - if (mWebView.canGoBack()) { - mWebView.goBack(); - return true; - } - return false; - } - - public WebHolder setOnPageTitleCallback(OnPageTitleCallback onPageTitleCallback) { - mOnPageTitleCallback = onPageTitleCallback; - return this; - } - - public WebHolder setOnPageLoadCallback(OnPageLoadCallback onPageLoadCallback) { - mOnPageLoadCallback = onPageLoadCallback; - return this; - } - - public WebHolder setOnPageProgressCallback(OnPageProgressCallback onPageProgressCallback) { - mOnPageProgressCallback = onPageProgressCallback; - return this; - } - - public WebHolder setOnHistoryUpdateCallback(OnHistoryUpdateCallback onHistoryUpdateCallback) { - mOnHistoryUpdateCallback = onHistoryUpdateCallback; - return this; - } - - public class WanWebChromeClient extends WebChromeClient { - - @Override - public void onReceivedTitle(WebView view, String title) { - super.onReceivedTitle(view, title); - if (mOnPageTitleCallback != null) { - mOnPageTitleCallback.onReceivedTitle(title); - } - } - - @Override - public void onProgressChanged(WebView view, int newProgress) { - super.onProgressChanged(view, newProgress); - if (newProgress < 95) { - if (!isProgressShown) { - isProgressShown = true; - onShowProgress(); - } - onProgressChanged(newProgress); - } else { - onProgressChanged(newProgress); - if (isProgressShown) { - isProgressShown = false; - onHideProgress(); - } - } - } - - private void onShowProgress() { - mProgressBar.setVisibility(View.VISIBLE); - setProgress(0); - if (mOnPageProgressCallback != null) { - mOnPageProgressCallback.onShowProgress(); - } - } - - private void onProgressChanged(int progress) { - setProgress(progress); - if (mOnPageProgressCallback != null) { - mOnPageProgressCallback.onProgressChanged(progress); - } - } - - private void onHideProgress() { - mProgressBar.setVisibility(View.GONE); - setProgress(100); - if (mOnPageProgressCallback != null) { - mOnPageProgressCallback.onHideProgress(); - } - } - - private void setProgress(int progress) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - mProgressBar.setProgress(progress, true); - } else { - mProgressBar.setProgress(progress); - } - } - } - - public class WanWebViewClient extends WebViewClient { - - private boolean shouldInterceptRequest(Uri uri) { - syncCookiesForWanAndroid(uri.toString()); - return false; - } - - private boolean shouldOverrideUrlLoading(Uri uri) { - switch (SettingUtils.getInstance().getUrlInterceptType()) { - default: - case HostInterceptUtils.TYPE_NOTHING: - return false; - case HostInterceptUtils.TYPE_ONLY_WHITE: - return !HostInterceptUtils.isWhiteHost(uri.getHost()); - case HostInterceptUtils.TYPE_INTERCEPT_BLACK: - return HostInterceptUtils.isBlackHost(uri.getHost()); - } - } - - @Override - public WebResourceResponse shouldInterceptRequest(WebView view, String url) { - if (shouldInterceptRequest(Uri.parse(url))) { - return new WebResourceResponse(null, null, null); - } - return super.shouldInterceptRequest(view, url); - } - - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - @Override - public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { - if (shouldInterceptRequest(request.getUrl())) { - return new WebResourceResponse(null, null, null); - } - return super.shouldInterceptRequest(view, request); - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - return shouldOverrideUrlLoading(Uri.parse(url)); - } - - @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - @Override - public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { - return shouldOverrideUrlLoading(request.getUrl()); - } - - @Override - public void onPageStarted(WebView view, String url, Bitmap favicon) { - super.onPageStarted(view, url, favicon); - if (mOnPageTitleCallback != null) { - mOnPageTitleCallback.onReceivedTitle(getUrl()); - } - if (mOnPageLoadCallback != null) { - mOnPageLoadCallback.onPageStarted(); - } - } - - @Override - public void onPageFinished(WebView view, String url) { - super.onPageFinished(view, url); - if (mOnPageTitleCallback != null) { - mOnPageTitleCallback.onReceivedTitle(getTitle()); - } - if (mOnPageLoadCallback != null) { - mOnPageLoadCallback.onPageFinished(); - } - } - - @Override - public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) { - super.doUpdateVisitedHistory(view, url, isReload); - if (mOnHistoryUpdateCallback != null) { - mOnHistoryUpdateCallback.onHistoryUpdate(isReload); - } - } - } - - public interface OnPageTitleCallback { - void onReceivedTitle(@NonNull String title); - } - - public interface OnPageLoadCallback { - void onPageStarted(); - - void onPageFinished(); - } - - public interface OnPageProgressCallback { - void onShowProgress(); - - void onProgressChanged(int progress); - - void onHideProgress(); - } - - public interface OnHistoryUpdateCallback { - void onHistoryUpdate(boolean isReload); - } -} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/ai/AiClient.kt b/app/src/main/java/per/goweii/wanandroid/utils/ai/AiClient.kt new file mode 100644 index 00000000..7b0cb66f --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/ai/AiClient.kt @@ -0,0 +1,108 @@ +package per.goweii.wanandroid.utils.ai + +import com.google.gson.Gson +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.callbackFlow +import okhttp3.* +import okhttp3.sse.EventSource +import okhttp3.sse.EventSourceListener +import okhttp3.sse.EventSources +import per.goweii.basic.utils.LogUtils +import per.goweii.wanandroid.utils.SettingUtils +import java.util.concurrent.TimeUnit + +/** + * AI 客户端,基于 OkHttp SSE 实现流式调用 + */ +object AiClient { + private const val TAG = "AiClient" + + private val gson = Gson() + private val client = OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(60, TimeUnit.SECONDS) + .writeTimeout(60, TimeUnit.SECONDS) + .build() + + private val mediaType = MediaType.get("application/json; charset=utf-8") + + @JvmStatic + fun stream(messages: List): Flow = callbackFlow { + val settings = SettingUtils.getInstance() + val aiProvider = AiProvider.findProvider(settings.aiProvider) + ?: AiProvider.providers.first() + val aiModel = aiProvider.findModel(settings.aiModel) + ?: aiProvider.models.first() + + val request = Request.Builder() + .url("${aiProvider.baseUrl}/chat/completions") + .addHeader("Authorization", "Bearer ${settings.aiApiKey}") + .addHeader("Accept", "text/event-stream") + .post( + RequestBody.create( + mediaType, + gson.toJson( + mapOf( + "model" to aiModel.id, + "stream" to true, + "messages" to messages.map { it.toMap() } + ) + )) + ) + .build() + + LogUtils.d(TAG, "Url: ${request.url()}") + + val factory = EventSources.createFactory(client) + val eventSource = factory.newEventSource(request, object : EventSourceListener() { + override fun onEvent( + eventSource: EventSource, + id: String?, + type: String?, + data: String + ) { + LogUtils.d(TAG, "onEvent: id=$id, type=$type, data=$data") + if (data == "[DONE]") { + close() + return + } + try { + val response = gson.fromJson(data, ChatResponse::class.java) + val content = response.choices?.firstOrNull()?.delta?.content + if (content != null) { + trySend(content) + } + } catch (e: Exception) { + // Ignore malformed JSON + } + } + + override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) { + LogUtils.d(TAG, "onFailure: ${t.toString()}") + close(t ?: Exception("SSE failure")) + } + + override fun onClosed(eventSource: EventSource) { + LogUtils.d(TAG, "onClosed") + close() + } + }) + + awaitClose { + eventSource.cancel() + } + } + + private data class ChatResponse( + val choices: List? + ) + + private data class Choice( + val delta: Delta? + ) + + private data class Delta( + val content: String? + ) +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/ai/AiMessage.kt b/app/src/main/java/per/goweii/wanandroid/utils/ai/AiMessage.kt new file mode 100644 index 00000000..22d65e32 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/ai/AiMessage.kt @@ -0,0 +1,39 @@ +package per.goweii.wanandroid.utils.ai + +sealed class AiMessage( + val role: String, +) { + open fun toMap(): MutableMap { + return mutableMapOf("role" to role) + } +} + +data class UserMessage( + val content: String, +) : AiMessage(role = "user") { + override fun toMap(): MutableMap { + return super.toMap().apply { + this["content"] = content + } + } +} + +data class SystemMessage( + val content: String, +) : AiMessage(role = "system") { + override fun toMap(): MutableMap { + return super.toMap().apply { + this["content"] = content + } + } +} + +data class AssistantMessage( + val content: String, +) : AiMessage(role = "assistant") { + override fun toMap(): MutableMap { + return super.toMap().apply { + this["content"] = content + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/ai/AiProvider.kt b/app/src/main/java/per/goweii/wanandroid/utils/ai/AiProvider.kt new file mode 100644 index 00000000..9b03d94a --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/ai/AiProvider.kt @@ -0,0 +1,80 @@ +package per.goweii.wanandroid.utils.ai + +sealed class AiProvider( + val name: String, + val id: String, + val baseUrl: String, + val models: List, +) { + companion object { + @JvmStatic + val providers: List by lazy { + AiProvider::class.sealedSubclasses + .map { it.objectInstance!! } + .toList() + } + + @JvmStatic + fun findProvider(id: String): AiProvider? { + return providers.firstOrNull { it.id == id } + } + + @JvmStatic + fun getProvider(id: String): AiProvider { + return providers.firstOrNull { it.id == id } ?: providers.first() + } + } + + fun findModel(id: String): AiModel? { + return models.firstOrNull { it.id == id } + } + + fun getModel(id: String): AiModel { + return models.firstOrNull { it.id == id } ?: models.first() + } +} + +object DeepSeek : AiProvider( + name = "DeepSeek", + id = "deepseek", + baseUrl = "https://api.deepseek.com", + models = listOf( + AiModel( + name = "DeepSeek V4 Flash", + id = "deepseek-v4-flash", + ), + AiModel( + name = "DeepSeek V4 Pro", + id = "deepseek-v4-pro", + ), + ), +) + +object Qwen : AiProvider( + name = "Qwen", + id = "qwen", + baseUrl = "https://coding.dashscope.aliyuncs.com/v1", + models = listOf( + AiModel( + name = "Qwen3.6 Flash", + id = "qwen3.6-flash", + ), + AiModel( + name = "Qwen3.6 Plus", + id = "qwen3.6-plus", + ), + AiModel( + name = "Qwen3.7 Plus", + id = "qwen3.7-plus", + ), + AiModel( + name = "Qwen3.7 Max", + id = "qwen3.7-max", + ), + ), +) + +data class AiModel( + val name: String, + val id: String, +) \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/biometric/BiometricHelper.kt b/app/src/main/java/per/goweii/wanandroid/utils/biometric/BiometricHelper.kt new file mode 100644 index 00000000..d5e234a1 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/biometric/BiometricHelper.kt @@ -0,0 +1,78 @@ +package per.goweii.wanandroid.utils.biometric + +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.biometric.BiometricManager +import androidx.biometric.BiometricPrompt +import androidx.fragment.app.FragmentActivity +import per.goweii.wanandroid.R +import javax.crypto.Cipher + +class BiometricHelper( + private val activity: FragmentActivity +) { + private val biometricPrompt = BiometricPrompt(activity, AuthCallback()) + private var keyAlias = "keyForLogin" + + var onAuthSuccess: ((cipher: Cipher) -> Unit)? = null + var onAuthFailure: (() -> Unit)? = null + var onAuthError: ((errorCode: Int, errString: CharSequence) -> Unit)? = null + + @RequiresApi(Build.VERSION_CODES.M) + fun canAuth(): Boolean { + val biometricManager = BiometricManager.from(activity) + val canAuthenticate = biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) + return canAuthenticate == BiometricManager.BIOMETRIC_SUCCESS + } + + @RequiresApi(Build.VERSION_CODES.M) + fun authForEncode( + title: String = activity.getString(R.string.turn_on_biometric_authentication_to_sign_in), + cancelBtn: String = activity.getString(R.string.cancel) + ) { + val promptInfo = BiometricPrompt.PromptInfo.Builder().apply { + setTitle(title) + setNegativeButtonText(cancelBtn) + setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG) + setConfirmationRequired(false) + }.build() + val cipher = CipherUtils.getEncryptCipher(keyAlias) + val cryptoObject = BiometricPrompt.CryptoObject(cipher) + biometricPrompt.authenticate(promptInfo, cryptoObject) + } + + @RequiresApi(Build.VERSION_CODES.M) + fun authForDecode( + iv: ByteArray, + title: String = activity.getString(R.string.sign_in_with_biometric_authentication), + cancelBtn: String = activity.getString(R.string.cancel) + ) { + val promptInfo = BiometricPrompt.PromptInfo.Builder().apply { + setTitle(title) + setNegativeButtonText(cancelBtn) + setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG) + setConfirmationRequired(false) + }.build() + val cipher = CipherUtils.getDecryptCipher(keyAlias, iv) + val cryptoObject = BiometricPrompt.CryptoObject(cipher) + biometricPrompt.authenticate(promptInfo, cryptoObject) + } + + private inner class AuthCallback : BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + super.onAuthenticationSucceeded(result) + val cipher = result.cryptoObject?.cipher ?: throw RuntimeException("cipher is null") + onAuthSuccess?.invoke(cipher) + } + + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + super.onAuthenticationError(errorCode, errString) + onAuthError?.invoke(errorCode, errString) + } + + override fun onAuthenticationFailed() { + super.onAuthenticationFailed() + onAuthFailure?.invoke() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/biometric/CipherUtils.kt b/app/src/main/java/per/goweii/wanandroid/utils/biometric/CipherUtils.kt new file mode 100644 index 00000000..28de4e21 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/biometric/CipherUtils.kt @@ -0,0 +1,48 @@ +package per.goweii.wanandroid.utils.biometric + +import android.os.Build +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties +import androidx.annotation.RequiresApi +import java.security.Key +import java.security.KeyStore +import javax.crypto.Cipher +import javax.crypto.KeyGenerator +import javax.crypto.spec.IvParameterSpec + +@RequiresApi(Build.VERSION_CODES.M) +object CipherUtils { + private const val ANDROID_KEYSTORE = "AndroidKeyStore" + private const val ENCRYPTION_BLOCK_MODE = KeyProperties.BLOCK_MODE_CBC + private const val ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7 + private const val ENCRYPTION_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES + private const val TRANSFORMATION = "$ENCRYPTION_ALGORITHM/$ENCRYPTION_BLOCK_MODE/$ENCRYPTION_PADDING" + + fun getEncryptCipher(keyAlias: String): Cipher { + val key = getKey(keyAlias) + val cipher = Cipher.getInstance(TRANSFORMATION) + cipher.init(Cipher.ENCRYPT_MODE, key) + return cipher + } + + fun getDecryptCipher(keyAlias: String, iv: ByteArray): Cipher { + val key = getKey(keyAlias) + val cipher = Cipher.getInstance(TRANSFORMATION) + cipher.init(Cipher.DECRYPT_MODE, key, IvParameterSpec(iv)) + return cipher + } + + private fun getKey(keyAlias: String): Key { + val keyStore = KeyStore.getInstance(ANDROID_KEYSTORE) + keyStore.load(null) + keyStore.getKey(keyAlias, null)?.let { return it } + val keyGenerator = KeyGenerator.getInstance(ENCRYPTION_ALGORITHM, ANDROID_KEYSTORE) + val builder = KeyGenParameterSpec.Builder(keyAlias, + KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT) + .setBlockModes(ENCRYPTION_BLOCK_MODE) + .setUserAuthenticationRequired(false) + .setEncryptionPaddings(ENCRYPTION_PADDING) + keyGenerator.init(builder.build()) + return keyGenerator.generateKey() + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/cdkey/CDKey.java b/app/src/main/java/per/goweii/wanandroid/utils/cdkey/CDKey.java new file mode 100644 index 00000000..13bbddf1 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/cdkey/CDKey.java @@ -0,0 +1,15 @@ +package per.goweii.wanandroid.utils.cdkey; + +import androidx.annotation.NonNull; + +/** + * @author CuiZhen + * @date 2020/1/1 + * GitHub: https://github.com/goweii + */ +public interface CDKey { + @NonNull + String create(@NonNull String userId); + + boolean check(@NonNull String userId, @NonNull String cdkey); +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/cdkey/CDKeyUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/cdkey/CDKeyUtils.java new file mode 100644 index 00000000..d531bf2f --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/cdkey/CDKeyUtils.java @@ -0,0 +1,82 @@ +package per.goweii.wanandroid.utils.cdkey; + +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import per.goweii.basic.utils.SPUtils; +import per.goweii.wanandroid.BuildConfig; +import per.goweii.wanandroid.utils.UserUtils; + +/** + * @author CuiZhen + * @date 2020/1/1 + * GitHub: https://github.com/goweii + */ +public class CDKeyUtils { + private static final String SP_NAME = "cdkey"; + private static final String KEY_USERID = "userid"; + private static final String KEY_CDKEY = "cdkey"; + + private static class Holder { + private static final CDKeyUtils sInstance = new CDKeyUtils(); + } + + public static CDKeyUtils getInstance() { + return Holder.sInstance; + } + + @Nullable + private final CDKey mCDKey; + + private CDKeyUtils() { + mCDKey = newCDKey(); + } + + public boolean isActive() { + String id = String.valueOf(UserUtils.getInstance().getWanId()); + return check(id, get()); + } + + public void set(String cdKey) { + String id = String.valueOf(UserUtils.getInstance().getWanId()); + SPUtils.newInstance(SP_NAME) + .save(KEY_USERID, id) + .save(KEY_CDKEY, cdKey); + } + + @NonNull + public String get() { + if (!UserUtils.getInstance().isLogin()) { + return ""; + } + String currId = String.valueOf(UserUtils.getInstance().getWanId()); + String saveId = SPUtils.newInstance(SP_NAME).get(KEY_USERID, ""); + if (!TextUtils.equals(currId, saveId)) { + return ""; + } + return SPUtils.newInstance(SP_NAME).get(KEY_CDKEY, ""); + } + + @NonNull + public String create(@NonNull String userId) { + if (mCDKey == null) return ""; + return mCDKey.create(userId); + } + + public boolean check(@NonNull String userId, @NonNull String cdkey) { + if (mCDKey == null) return false; + return mCDKey.check(userId, cdkey); + } + + private CDKey newCDKey() { + try { + Class clazz = Class.forName(BuildConfig.CDKEY_CLASS); + return (CDKey) clazz.newInstance(); + } catch (Throwable e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/recreate_anim/RecreateAnimActivity.kt b/app/src/main/java/per/goweii/wanandroid/utils/recreate_anim/RecreateAnimActivity.kt new file mode 100644 index 00000000..6c0283e5 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/recreate_anim/RecreateAnimActivity.kt @@ -0,0 +1,92 @@ +package per.goweii.wanandroid.utils.recreate_anim + +import android.app.Activity +import android.content.Intent +import android.graphics.Bitmap +import android.os.Bundle +import android.widget.ImageView +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.doOnPreDraw +import per.goweii.statusbarcompat.StatusBarCompat +import per.goweii.swipeback.SwipeBackAbility + +class RecreateAnimActivity : AppCompatActivity(), SwipeBackAbility.Direction { + companion object { + private var recreateSource: RecreateSource? = null + + internal fun launch(activity: Activity, action: Runnable) { + if (recreateSource != null) { + return + } + + val window = activity.window + val decorView = window.decorView + decorView.isDrawingCacheEnabled = true + val b = decorView.drawingCache + val bitmap = Bitmap.createBitmap(b) + decorView.isDrawingCacheEnabled = false + + recreateSource = RecreateSource( + bitmap = bitmap, + action = action, + ) + + val intent = Intent(activity, RecreateAnimActivity::class.java) + activity.startActivity(intent) + activity.overridePendingTransition(0, 0) + } + } + + private var imageView: ImageView? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + StatusBarCompat.transparent(this) + + if (recreateSource == null) { + finish() + return + } + + val recreateSource = recreateSource!! + + val imageView = ImageView(this) + imageView.scaleType = ImageView.ScaleType.FIT_XY + imageView.setImageBitmap(recreateSource.bitmap) + + this.imageView = imageView + + setContentView(imageView) + + imageView.doOnPreDraw { + recreateSource.action.run() + imageView.post { + imageView.post { + doAnim(imageView) + } + } + } + } + + private fun doAnim(imageView: ImageView) { + imageView.animate() + .setDuration(500) + .alpha(0F) + .withEndAction { + finish() + recreateSource?.recycle() + recreateSource = null + } + .start() + } + + override fun swipeBackDirection(): Int { + return 0 + } + + override fun finish() { + super.finish() + overridePendingTransition(0, 0) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/recreate_anim/RecreateAnimation.kt b/app/src/main/java/per/goweii/wanandroid/utils/recreate_anim/RecreateAnimation.kt new file mode 100644 index 00000000..d12fa79c --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/recreate_anim/RecreateAnimation.kt @@ -0,0 +1,12 @@ +package per.goweii.wanandroid.utils.recreate_anim + +import android.app.Activity + +class RecreateAnimation( + private val activity: Activity, + private val trigger: Runnable, +) { + fun start() { + RecreateAnimActivity.launch(activity, trigger) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/recreate_anim/RecreateSource.kt b/app/src/main/java/per/goweii/wanandroid/utils/recreate_anim/RecreateSource.kt new file mode 100644 index 00000000..65fa3c76 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/recreate_anim/RecreateSource.kt @@ -0,0 +1,12 @@ +package per.goweii.wanandroid.utils.recreate_anim + +import android.graphics.Bitmap + +internal data class RecreateSource( + val bitmap: Bitmap, + val action: Runnable, +) { + fun recycle() { + bitmap.recycle() + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/router/Param.java b/app/src/main/java/per/goweii/wanandroid/utils/router/Param.java new file mode 100644 index 00000000..fca2d744 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/router/Param.java @@ -0,0 +1,16 @@ +package per.goweii.wanandroid.utils.router; + +/** + * @author CuiZhen + * @date 2019/12/28 + * GitHub: https://github.com/goweii + */ +public class Param { + public final String key; + public final String value; + + public Param(String key, String value) { + this.key = key; + this.value = value; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/router/Router.java b/app/src/main/java/per/goweii/wanandroid/utils/router/Router.java new file mode 100644 index 00000000..c8280a54 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/router/Router.java @@ -0,0 +1,79 @@ +package per.goweii.wanandroid.utils.router; + +import android.content.Intent; +import android.net.Uri; +import android.text.TextUtils; + +import androidx.annotation.Nullable; + +import per.goweii.basic.utils.LogUtils; + +/** + * @author CuiZhen + * @date 2019/12/28 + * GitHub: https://github.com/goweii + */ +public class Router { + public static final String SCHEME = "wana"; + public static final String HOST = "wanandroid.com"; + + static final String PARAM_URL = "ROUTER_URL"; + + public static String createUrlByPath(String path) { + StringBuilder urlBuilder = new StringBuilder(); + urlBuilder.append(Router.SCHEME); + urlBuilder.append("://"); + urlBuilder.append(Router.HOST); + if (!TextUtils.isEmpty(path)) { + if (!path.startsWith("/")) { + urlBuilder.append("/"); + } + urlBuilder.append(path); + } + return urlBuilder.toString(); + } + + public static void routeTo(String url) { + LogUtils.d("Router", "url=" + url); + if (url == null) return; + final Uri uri = Uri.parse(url); + if (checkHost(uri)) { + RouterMap routerMap = RouterMap.from(uri); + if (routerMap.isExist()) { + routerMap.navigation(url); + } else { + if (isHttpOrHttps(uri)) { + RouterMap.WEB.navigation(url); + } + } + } else { + if (isHttpOrHttps(uri)) { + RouterMap.WEB.navigation(url); + } + } + } + + @Nullable + public static Uri getUriFrom(Intent intent) { + String url = intent.getStringExtra(PARAM_URL); + if (TextUtils.isEmpty(url)) { + return null; + } + return Uri.parse(url); + } + + private static boolean checkHost(Uri uri) { + final String host = uri.getHost(); + return TextUtils.equals(HOST, host); + } + + private static boolean checkScheme(Uri uri) { + final String scheme = uri.getScheme(); + return TextUtils.equals(SCHEME, scheme); + } + + private static boolean isHttpOrHttps(Uri uri) { + final String scheme = uri.getScheme(); + return TextUtils.equals("http", scheme) || TextUtils.equals("https", scheme); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/router/RouterMap.java b/app/src/main/java/per/goweii/wanandroid/utils/router/RouterMap.java new file mode 100644 index 00000000..4f3b8174 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/router/RouterMap.java @@ -0,0 +1,95 @@ +package per.goweii.wanandroid.utils.router; + +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import per.goweii.basic.utils.Utils; +import per.goweii.wanandroid.module.home.activity.UserPageActivity; +import per.goweii.wanandroid.module.main.activity.ArticleListActivity; +import per.goweii.wanandroid.module.main.activity.WebActivity; +import per.goweii.wanandroid.module.mine.activity.AboutMeActivity; +import per.goweii.wanandroid.module.mine.activity.SettingActivity; + +/** + * @author CuiZhen + * @date 2019/12/28 + * GitHub: https://github.com/goweii + */ +public enum RouterMap { + NULL(null, null), + WEB("/main/web", WebActivity.class), + USER_PAGE("/main/user_page", UserPageActivity.class), + SETTING("/mine/setting", SettingActivity.class), + ABOUT_ME("/mine/about_me", AboutMeActivity.class), + ARTICLE_LIST("/main/article_list", ArticleListActivity.class); + + private final String path; + private final Class clazz; + + RouterMap(String path, Class clazz) { + this.path = path; + this.clazz = clazz; + } + + @NonNull + public static RouterMap from(String path) { + if (!TextUtils.isEmpty(path)) { + for (RouterMap routerMap : RouterMap.values()) { + if (TextUtils.equals(routerMap.path, path)) { + return routerMap; + } + } + } + return NULL; + } + + @NonNull + public static RouterMap from(Uri uri) { + return from(uri.getPath()); + } + + public boolean isExist() { + return this != NULL; + } + + public boolean isNull() { + return this == NULL; + } + + public boolean isWeb() { + return this == WEB; + } + + public String url() { + return Router.createUrlByPath(path); + } + + public String url(Param... param) { + StringBuilder s = new StringBuilder(url()); + for (Param p : param) { + if (s.indexOf("?") != -1) s.append("?"); + else s.append("&"); + s.append(p.key).append("=").append(p.value); + } + return s.toString(); + } + + public void navigation(Param... param) { + navigation(url(param)); + } + + void navigation(String url) { + if (clazz == null) return; + try { + Intent intent = new Intent(Utils.getAppContext(), clazz); + intent.putExtra(Router.PARAM_URL, url); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Utils.getAppContext().startActivity(intent); + } catch (Exception ignore) { + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsClient.kt b/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsClient.kt new file mode 100644 index 00000000..c41bd4b3 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsClient.kt @@ -0,0 +1,145 @@ +package per.goweii.wanandroid.utils.tts + +import android.os.Bundle +import android.speech.tts.TextToSpeech +import android.speech.tts.UtteranceProgressListener +import per.goweii.basic.utils.LogUtils +import per.goweii.basic.utils.Utils +import java.util.Locale +import java.util.UUID + +object TtsClient { + private const val TAG = "TtsClient" + + private var textToSpeech: TextToSpeech + + private val buffer = StringBuilder() + + val isSpeaking: Boolean + get() = textToSpeech.isSpeaking + + private val textCache = mutableMapOf() + + private val stateChangeListeners = arrayListOf() + + private var currentSpeakId = -1 + + init { + textToSpeech = TextToSpeech(Utils.getAppContext()) { status -> + if (status == TextToSpeech.SUCCESS) { + textToSpeech.language = Locale.CHINESE + textToSpeech.setSpeechRate(1.0f) + textToSpeech.setPitch(1.0f) + } + } + textToSpeech.setOnUtteranceProgressListener(object : UtteranceProgressListener() { + // 开始朗读 + override fun onStart(id: String?) { + val index = id?.toIntOrNull() ?: -1 + currentSpeakId = index + val text = id?.let { textCache[it] } + LogUtils.d(TAG, "onStart: id=${id}, text=${text}") + if (text != null) { + stateChangeListeners.forEach { it.onStart(id, text) } + } + } + + // 朗读正常结束 + override fun onDone(id: String?) { + val text = id?.let { textCache[it] } + LogUtils.d(TAG, "onDone: id=${id}, text=${text}") + if (text != null) { + stateChangeListeners.forEach { it.onDone(id, text) } + } + if (buffer.isNotEmpty()) { + val text = buffer.toString() + buffer.clear() + speakNow(text, null) + } + } + + // 朗读被中断/出错 + @Deprecated("Deprecated in Java") + override fun onError(id: String?) { + val index = id?.toIntOrNull() ?: -1 + val text = id?.let { textCache[it] } + LogUtils.d(TAG, "onError: id=${id}, text=${text}") + if (text != null) { + stateChangeListeners.forEach { it.onError(id, text) } + } + } + + // 朗读被中断/出错 + override fun onError(id: String?, errorCode: Int) { + val index = id?.toIntOrNull() ?: -1 + val text = id?.let { textCache[it] } + LogUtils.d(TAG, "onError: id=${id}, text=${text}") + if (text != null) { + stateChangeListeners.forEach { it.onError(id, text) } + } + } + }) + } + + fun addStateChangeListener(listener: OnTtsStateChangeListener) { + stateChangeListeners.add(listener) + } + + fun removeStateChangeListener(listener: OnTtsStateChangeListener) { + stateChangeListeners.remove(listener) + } + + // 流式逐段播报 + // textChunk == null 表示结束添加文本 + fun speak( + textChunk: String?, + id: String? = null, + useBuffer: Boolean = false, + ) { + if (textChunk == null) { + if (buffer.isNotEmpty()) { + val text = buffer.toString() + buffer.clear() + speakNow(text, id) + } + return + } + if (useBuffer) { + buffer.append(textChunk) + if (buffer.contains("。") + || buffer.contains("!") + || buffer.contains("?") + || buffer.contains(";") + || buffer.contains("\\n") + ) { + val text = buffer.toString() + buffer.clear() + speakNow(text, id) + } + } else { + speakNow(textChunk, id) + } + } + + fun stop() { + buffer.clear() + currentSpeakId = -1 + textCache.clear() + textToSpeech.stop() + } + + private fun speakNow(text: String, id: String?) { + val id = id ?: UUID.randomUUID().toString() + textCache[id] = text + val params = Bundle().apply { + putString(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, id) + } + textToSpeech.speak(text, TextToSpeech.QUEUE_ADD, params, id) + } + + interface OnTtsStateChangeListener { + fun onStart(id: String, text: String) + fun onDone(id: String, text: String) + fun onError(id: String, text: String) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsManager.kt b/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsManager.kt new file mode 100644 index 00000000..6e1cbfdc --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsManager.kt @@ -0,0 +1,227 @@ +package per.goweii.wanandroid.utils.tts + +import android.annotation.SuppressLint +import android.content.Context +import per.goweii.wanandroid.R + +class TtsManager(private val context: Context) { + companion object { + @SuppressLint("StaticFieldLeak") + private var instance: TtsManager? = null + + @JvmStatic + fun getInstance(context: Context): TtsManager { + if (instance == null) { + synchronized(TtsManager::class) { + if (instance == null) { + instance = TtsManager(context.applicationContext) + } + } + } + return instance!! + } + } + + private val notification = TtsNotification(context) + private val playback: MutableList = arrayListOf() + private var playable: TtsPlayable? = null + + val isSpeaking: Boolean + get() = playable?.state?.let { + it == TtsPlayable.TtsState.THINKING || it == TtsPlayable.TtsState.SPEAKING + } ?: false + + fun addSource(source: TtsSource) { + if (!playback.contains(source)) { + playback.add(source) + if (playable != null) { + notification.update( + playbackIndex = playback.indexOf(playable!!.source) + 1, + playableCount = playback.size, + ) + } + } + } + + fun removeSource(source: TtsSource) { + val index = playback.indexOf(source) + if (index == -1) { + return + } + playback.removeAt(index) + if (playable != null) { + notification.update( + playbackIndex = playback.indexOf(playable!!.source) + 1, + playableCount = playback.size, + ) + if (playable!!.source == source) { + if (index <= playback.lastIndex) { + playSource(playback[index]) + } + } + } + } + + fun clearPlayback() { + playback.clear() + if (playable != null) { + playable?.stop() + playable = null + } + notification.cancel() + } + + fun playSource(source: TtsSource) { + if (!playback.contains(source)) { + playback.add(source) + } + if (playable?.source != source) { + playable?.stop() + playable = null + } + if (playable == null) { + playable = TtsPlayable( + source = source, + onPlayStateChangeListener = object : TtsPlayable.OnPlayStateChangeListener { + override fun onIdle() { + notification.show(source) + notification.update( + isPlaying = false, + playbackIndex = playback.indexOf(source) + 1, + playableCount = playback.size, + currentSpeakText = source.url, + ) + } + + override fun onThinking() { + notification.update( + isPlaying = true, + currentSpeakText = context.getString(R.string.thinking), + ) + } + + override fun onSpeaking() { + notification.update( + isPlaying = true, + ) + } + + override fun onStopped(completed: Boolean) { + notification.update( + isPlaying = false, + currentSpeakText = source.url, + ) + if (completed) { + val index = playback.indexOf(source) + if (index < playback.lastIndex) { + playSource(playback[index + 1]) + } + } + } + + override fun onReleased() { + playable = null + notification.update( + isPlaying = false, + currentSpeakText = source.url, + ) + } + + override fun onErrored() { + notification.update( + isPlaying = false, + currentSpeakText = source.url, + ) + } + }, + onSpeakTextChangeListener = object : TtsPlayable.OnSpeakTextChangeListener { + override fun onSpeakStart( + text: String, + index: Int, + total: Int + ) { + notification.update( + currentSpeakText = text, + ) + } + + override fun onSpeakDone( + text: String, + index: Int, + total: Int + ) { + notification.update( + currentSpeakText = text, + ) + } + + override fun onSpeakError( + text: String, + index: Int, + total: Int + ) { + notification.update( + currentSpeakText = text, + ) + } + }, + ) + playable!!.start() + } + } + + fun pause() { + playable?.stop() + notification.update( + isPlaying = false, + currentSpeakText = playable?.source?.url ?: "", + ) + } + + fun resume() { + playable?.start() + } + + fun prev() { + if (playback.isEmpty()) { + return + } + if (playable != null) { + val index = playback.indexOf(playable!!.source) + if (index == -1 || index == 0) { + playSource(playback.last()) + } else { + playSource(playback[index - 1]) + } + } else { + playSource(playback.last()) + } + } + + fun next() { + if (playback.isEmpty()) { + return + } + if (playable != null) { + val index = playback.indexOf(playable!!.source) + if (index == -1 || index == playback.lastIndex) { + playSource(playback.first()) + } else { + playSource(playback[index + 1]) + } + } else { + playSource(playback.first()) + } + } + + fun stop() { + playable?.stop() + } + + fun release() { + notification.cancel() + playable?.release() + playable = null + instance = null + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsNotification.kt b/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsNotification.kt new file mode 100644 index 00000000..b505d9c5 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsNotification.kt @@ -0,0 +1,231 @@ +package per.goweii.wanandroid.utils.tts + +import android.Manifest +import android.annotation.SuppressLint +import android.app.Notification +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.os.Build +import android.view.View +import android.widget.RemoteViews +import androidx.core.app.ActivityCompat +import androidx.core.app.NotificationChannelCompat +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import androidx.core.net.toUri +import com.bumptech.glide.Glide +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import per.goweii.wanandroid.R +import per.goweii.wanandroid.module.main.activity.RouterActivity + +class TtsNotification(val context: Context) { + private val manager = NotificationManagerCompat.from(context) + + private var source: TtsSource? = null + private var coverBitmap: Bitmap? = null + private var isPlaying: Boolean = false + private var currentSpeakText: String? = null + private var playbackIndex: Int = 0 + private var playableCount: Int = 0 + + fun show(source: TtsSource) { + createNotificationChannel() + this.source = source + update() + if (source.cover?.isNotEmpty() == true && hasPermission()) { + loadCover() + } + } + + @SuppressLint("MissingPermission") + fun update( + source: TtsSource? = null, + isPlaying: Boolean? = null, + currentSpeakText: String? = null, + playbackIndex: Int? = null, + playableCount: Int? = null, + ) { + this.source = source ?: this.source + this.isPlaying = isPlaying ?: this.isPlaying + this.currentSpeakText = currentSpeakText ?: this.currentSpeakText + this.playbackIndex = playbackIndex ?: this.playbackIndex + this.playableCount = playableCount ?: this.playableCount + if (this.source != null && hasPermission()) { + val notification = buildNotification() + manager.notify(R.string.tts_notification_channel_id, notification) + } + } + + fun cancel() { + manager.cancel(R.string.tts_notification_channel_id) + coverBitmap?.recycle() + source = null + coverBitmap = null + isPlaying = false + currentSpeakText = null + playbackIndex = 0 + playableCount = 0 + } + + private fun hasPermission(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + ActivityCompat.checkSelfPermission( + context, + Manifest.permission.POST_NOTIFICATIONS + ) == PackageManager.PERMISSION_GRANTED + } else { + true + } + } + + private fun createNotificationChannel() { + val channel = NotificationChannelCompat.Builder( + context.getString(R.string.tts_notification_channel_id), + NotificationManagerCompat.IMPORTANCE_DEFAULT, + ).setName(context.getString(R.string.tts_notification_channel_name)) + .setDescription(context.getString(R.string.tts_notification_channel_desc)) + .setShowBadge(false) + .setVibrationEnabled(false) + .setLightsEnabled(false) + .build() + manager.createNotificationChannel(channel) + } + + private fun loadCover() { + Glide.with(context) + .asBitmap() + .load(source?.cover) + .override(200) + .centerCrop() + .into(object : CustomTarget() { + override fun onResourceReady( + bitmap: Bitmap, + transition: Transition? + ) { + coverBitmap = bitmap + update() + } + + override fun onLoadCleared(placeholder: Drawable?) { + coverBitmap = null + update() + } + }) + } + + private fun buildNotification(): Notification { + val intent = Intent(context, RouterActivity::class.java).apply { + action = Intent.ACTION_VIEW + data = source?.url?.toUri() + } + val openAppIntent = PendingIntent.getActivity( + context, + 0, + intent, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + ) + + val playIntent = PendingIntent.getBroadcast( + context, + R.id.iv_play, + Intent(context, TtsNotificationReceiver::class.java).apply { + action = TtsNotificationReceiver.ACTION_PLAY(context) + }, + PendingIntent.FLAG_UPDATE_CURRENT or + PendingIntent.FLAG_IMMUTABLE + ) + + val pauseIntent = PendingIntent.getBroadcast( + context, + R.id.iv_pause, + Intent(context, TtsNotificationReceiver::class.java).apply { + action = TtsNotificationReceiver.ACTION_PAUSE(context) + }, + PendingIntent.FLAG_UPDATE_CURRENT or + PendingIntent.FLAG_IMMUTABLE + ) + + val prevIntent = PendingIntent.getBroadcast( + context, + R.id.iv_prev, + Intent(context, TtsNotificationReceiver::class.java).apply { + action = TtsNotificationReceiver.ACTION_PREV(context) + }, + PendingIntent.FLAG_UPDATE_CURRENT or + PendingIntent.FLAG_IMMUTABLE + ) + + val nextIntent = PendingIntent.getBroadcast( + context, + R.id.iv_next, + Intent(context, TtsNotificationReceiver::class.java).apply { + action = TtsNotificationReceiver.ACTION_NEXT(context) + }, + PendingIntent.FLAG_UPDATE_CURRENT or + PendingIntent.FLAG_IMMUTABLE + ) + + val smallView = + RemoteViews(context.packageName, R.layout.layout_tts_notification_small).apply { + if (coverBitmap?.isRecycled == false) { + setImageViewBitmap(R.id.iv_cover, coverBitmap!!) + } else { + setImageViewResource(R.id.iv_cover, R.drawable.logo_512) + } + setTextViewText(R.id.tv_title, source?.title?.trim()) + setTextViewText(R.id.tv_desc, currentSpeakText?.trim()) + if (isPlaying) { + setViewVisibility(R.id.iv_play, View.GONE) + setViewVisibility(R.id.iv_pause, View.VISIBLE) + } else { + setViewVisibility(R.id.iv_play, View.VISIBLE) + setViewVisibility(R.id.iv_pause, View.GONE) + } + setOnClickPendingIntent(R.id.iv_play, playIntent) + setOnClickPendingIntent(R.id.iv_pause, pauseIntent) + } + + val bigView = + RemoteViews(context.packageName, R.layout.layout_tts_notification_big).apply { + if (coverBitmap?.isRecycled == false) { + setImageViewBitmap(R.id.iv_cover, coverBitmap!!) + } else { + setImageViewResource(R.id.iv_cover, R.drawable.logo_512) + } + setTextViewText(R.id.tv_title, source?.title?.trim()) + setTextViewText(R.id.tv_desc, currentSpeakText?.trim()) + if (isPlaying) { + setViewVisibility(R.id.iv_play, View.GONE) + setViewVisibility(R.id.iv_pause, View.VISIBLE) + } else { + setViewVisibility(R.id.iv_play, View.VISIBLE) + setViewVisibility(R.id.iv_pause, View.GONE) + } + if (playbackIndex > 0 && playableCount > 0) { + setTextViewText(R.id.tv_index, "${playbackIndex}/${playableCount}") + } + setOnClickPendingIntent(R.id.iv_play, playIntent) + setOnClickPendingIntent(R.id.iv_pause, pauseIntent) + setOnClickPendingIntent(R.id.iv_prev, prevIntent) + setOnClickPendingIntent(R.id.iv_next, nextIntent) + } + + return NotificationCompat.Builder( + context, + context.getString(R.string.tts_notification_channel_id) + ).setContentIntent(openAppIntent) + .setSmallIcon(R.drawable.ic_icon) + .setBadgeIconType(NotificationCompat.BADGE_ICON_NONE) + .setCustomContentView(smallView) + .setCustomBigContentView(bigView) + .setShowWhen(false) + .setAllowSystemGeneratedContextualActions(false) + .setOngoing(isPlaying) + .build() + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsNotificationReceiver.kt b/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsNotificationReceiver.kt new file mode 100644 index 00000000..e75eeb0e --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsNotificationReceiver.kt @@ -0,0 +1,31 @@ +package per.goweii.wanandroid.utils.tts + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import per.goweii.basic.utils.LogUtils + +@Suppress("FunctionName") +class TtsNotificationReceiver : BroadcastReceiver() { + companion object { + const val TAG = "TtsNotificationReceiver" + + fun ACTION_PLAY(context: Context) = "${context.packageName}.android.ACTION_PLAY" + fun ACTION_PAUSE(context: Context) = "${context.packageName}.android.ACTION_PAUSE" + fun ACTION_PREV(context: Context) = "${context.packageName}.android.ACTION_PREV" + fun ACTION_NEXT(context: Context) = "${context.packageName}.android.ACTION_NEXT" + } + + override fun onReceive(context: Context, intent: Intent) { + LogUtils.d(TAG, "onReceive: ${intent.action}") + if (intent.action == ACTION_PLAY(context)) { + TtsManager.getInstance(context).resume() + } else if (intent.action == ACTION_PAUSE(context)) { + TtsManager.getInstance(context).pause() + } else if (intent.action == ACTION_PREV(context)) { + TtsManager.getInstance(context).prev() + } else if (intent.action == ACTION_NEXT(context)) { + TtsManager.getInstance(context).next() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsPlayable.kt b/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsPlayable.kt new file mode 100644 index 00000000..1006de4b --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsPlayable.kt @@ -0,0 +1,269 @@ +package per.goweii.wanandroid.utils.tts + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.MainScope +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import per.goweii.wanandroid.utils.SettingUtils +import per.goweii.wanandroid.utils.ai.AiClient +import per.goweii.wanandroid.utils.ai.AssistantMessage +import per.goweii.wanandroid.utils.ai.UserMessage +import per.goweii.wanandroid.utils.web.utils.WebReadability + +class TtsPlayable( + val source: TtsSource, + val onPlayStateChangeListener: OnPlayStateChangeListener, + val onSpeakTextChangeListener: OnSpeakTextChangeListener, +) { + private val prompt = """ +你是一个专业的AI助理,我会给你提供一个网页 HTML 源码,你需要帮我总结并生成一份纯文本格式文章总结。 + +你需要: +- 使用中文 +- 尽量保持原文输出,不要过于压缩总结 +- 只输出文章总结,不要输出任何无关的内容 +- 不要输出类似“根据所提供的HTML源码”等前缀回复 +- 纯文本格式,方便朗读 + +你不能: +- 不要压缩过多 +- 不能输出富文本合适或者markdown格式 +- 如果文章包含代码块,你不能输出大面积代码 +""".trim() + + private val scope = MainScope() + + private val listener = object : TtsClient.OnTtsStateChangeListener { + override fun onStart(id: String, text: String) { + state = TtsState.SPEAKING + index = id.toInt() + onSpeakTextChangeListener.onSpeakStart(text, index, cache.size) + } + + override fun onDone(id: String, text: String) { + index = id.toInt() + onSpeakTextChangeListener.onSpeakDone(text, index, cache.size) + if (index == cache.lastIndex && job?.isActive != true) { + state = TtsState.STOPPED + onPlayStateChangeListener.onStopped(true) + } + } + + override fun onError(id: String, text: String) { + index = id.toInt() + onSpeakTextChangeListener.onSpeakError(text, index, cache.size) + if (index == cache.lastIndex && job?.isActive != true) { + state = TtsState.STOPPED + onPlayStateChangeListener.onStopped(true) + } + } + } + + var state: TtsState = TtsState.IDLE + private set + + private var job: Job? = null + + private val cache = arrayListOf() + private var index: Int = -1 + private val buffer = StringBuilder() + + fun start() { + if (state == TtsState.RECYCLED) return + if (state == TtsState.THINKING) return + if (state == TtsState.SPEAKING) return + + if (state == TtsState.IDLE) { + onPlayStateChangeListener.onIdle() + TtsClient.addStateChangeListener(listener) + startNewJob() + return + } + + if (state == TtsState.ERROR) { + startNewJob() + return + } + + if (state == TtsState.STOPPED) { + if (cache.isNotEmpty()) { + state = TtsState.SPEAKING + onPlayStateChangeListener.onSpeaking() + index = index.coerceIn(cache.indices).takeIf { it != cache.lastIndex } ?: 0 + for (i in index..cache.lastIndex) { + TtsClient.speak(cache[i], useBuffer = false, id = i.toString()) + } + } else { + state = TtsState.THINKING + onPlayStateChangeListener.onThinking() + } + return + } + } + + fun stop() { + if (state == TtsState.RECYCLED) return + if (state == TtsState.IDLE) return + if (state == TtsState.STOPPED) return + state = TtsState.STOPPED + onPlayStateChangeListener.onStopped(false) + TtsClient.stop() + } + + fun release() { + if (state == TtsState.RECYCLED) return + stop() + TtsClient.removeStateChangeListener(listener) + scope.cancel() + state = TtsState.RECYCLED + onPlayStateChangeListener.onReleased() + } + + private fun startNewJob() { + job?.cancel() + job = scope.launch { + TtsClient.stop() + if (SettingUtils.getInstance().isAiEnabled && SettingUtils.getInstance().aiApiKey.isNotEmpty()) { + startByAI() + } else { + startByLocal() + } + }.apply { + invokeOnCompletion { + job = null + } + } + } + + private suspend fun startByLocal() = withContext(Dispatchers.Default) { + state = TtsState.THINKING + onPlayStateChangeListener.onThinking() + + try { + val readability = WebReadability(source.url, source.html, true) + val md = readability.toMarkdown() + buffer.append(md) + processBuffer(isEnd = true) + if (state == TtsState.RECYCLED) return@withContext + if (state == TtsState.STOPPED) return@withContext + if (state != TtsState.SPEAKING) { + state = TtsState.SPEAKING + onPlayStateChangeListener.onSpeaking() + } + cache.forEachIndexed { index, string -> + TtsClient.speak(string, useBuffer = false, id = index.toString()) + } + } catch (_: Exception) { + state = TtsState.ERROR + onPlayStateChangeListener.onErrored() + } + } + + private suspend fun startByAI() { + state = TtsState.THINKING + onPlayStateChangeListener.onThinking() + + val readability = WebReadability(source.url, source.html, false) + val md = readability.toMarkdown() + val flow = AiClient.stream( + listOf( + AssistantMessage(prompt), + UserMessage(md), + ) + ) + flow.catch { + state = TtsState.ERROR + onPlayStateChangeListener.onErrored() + }.collect { chunk -> + if (chunk.isEmpty()) return@collect + buffer.append(chunk) + val oldSize = cache.size + processBuffer() + val newSize = cache.size + if (state == TtsState.RECYCLED) return@collect + if (state == TtsState.STOPPED) return@collect + if (state != TtsState.SPEAKING) { + state = TtsState.SPEAKING + onPlayStateChangeListener.onSpeaking() + } + for (i in oldSize until newSize) { + TtsClient.speak(cache[i], useBuffer = false, id = i.toString()) + } + } + val oldSize = cache.size + processBuffer(isEnd = true) + val newSize = cache.size + kotlin.run { + if (state == TtsState.RECYCLED) return@run + if (state == TtsState.STOPPED) return@run + for (i in oldSize until newSize) { + TtsClient.speak(cache[i], useBuffer = false, id = i.toString()) + } + TtsClient.speak(null, useBuffer = false) + } + } + + private fun processBuffer(isEnd: Boolean = false) { + if (buffer.isEmpty()) return + val lines = buffer.lines() + buffer.clear() + if (lines.isEmpty()) return + + fun String.texts(): List { + return split("(?<=[,。!?;:])".toRegex()) + .filter { it.isNotBlank() } + } + + lines + .take(lines.size - 1) + .forEach { line -> + line.texts() + .forEach { text -> + cache.add(text) + } + } + + val line = lines.last() + if (line.isEmpty()) return + val texts = line.texts() + if (texts.isEmpty()) return + texts + .take(texts.size - 1) + .forEach { text -> + cache.add(text) + } + val text = texts.last() + if (isEnd) { + cache.add(text) + } else { + buffer.append(text) + } + } + + enum class TtsState { + IDLE, + THINKING, + SPEAKING, + STOPPED, + ERROR, + RECYCLED, + } + + interface OnPlayStateChangeListener { + fun onIdle() + fun onThinking() + fun onSpeaking() + fun onStopped(completed: Boolean) + fun onReleased() + fun onErrored() + } + + interface OnSpeakTextChangeListener { + fun onSpeakStart(text: String, index: Int, total: Int) + fun onSpeakDone(text: String, index: Int, total: Int) + fun onSpeakError(text: String, index: Int, total: Int) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsSource.kt b/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsSource.kt new file mode 100644 index 00000000..e5740ec4 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/tts/TtsSource.kt @@ -0,0 +1,19 @@ +package per.goweii.wanandroid.utils.tts + +data class TtsSource( + val url: String, + val cover: String?, + val title: String?, + val html: String, +) { + override fun hashCode(): Int { + return url.hashCode() + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + other as TtsSource + return url == other.url + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/AboutMeWanPwd.java b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/AboutMeWanPwd.java new file mode 100644 index 00000000..19c18702 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/AboutMeWanPwd.java @@ -0,0 +1,35 @@ +package per.goweii.wanandroid.utils.wanpwd; + +import androidx.annotation.Nullable; + +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.utils.router.RouterMap; + +/** + * @author CuiZhen + * @date 2019/12/28 + * GitHub: https://github.com/goweii + */ +public class AboutMeWanPwd implements IWanPwd { + @Nullable + @Override + public Runnable getRunnable() { + return new Runnable() { + @Override + public void run() { + RouterMap.ABOUT_ME.navigation(); + } + }; + } + + @Override + public String getShowText() { + return ResUtils.getString(R.string.let_s_have_a_cup_of_coffee); + } + + @Override + public String getBtnText() { + return ResUtils.getString(R.string.go_for_a_treat); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/CDKeyWanPwd.java b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/CDKeyWanPwd.java new file mode 100644 index 00000000..23091485 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/CDKeyWanPwd.java @@ -0,0 +1,54 @@ +package per.goweii.wanandroid.utils.wanpwd; + +import androidx.annotation.Nullable; + +import per.goweii.basic.ui.toast.ToastMaker; +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.utils.UserUtils; +import per.goweii.wanandroid.utils.cdkey.CDKeyUtils; + +/** + * @author CuiZhen + * @date 2020/1/1 + * GitHub: https://github.com/goweii + */ +public class CDKeyWanPwd implements IWanPwd { + + private final Runnable mRunnable; + + public CDKeyWanPwd(String content) { + mRunnable = new Runnable() { + @Override + public void run() { + if (!UserUtils.getInstance().isLogin()) { + ToastMaker.showShort(ResUtils.getString(R.string.please_sign_in_first)); + return; + } + int id = UserUtils.getInstance().getWanId(); + if (CDKeyUtils.getInstance().check(String.valueOf(id), content)) { + CDKeyUtils.getInstance().set(content); + ToastMaker.showShort(ResUtils.getString(R.string.the_activation_was_successful)); + } else { + ToastMaker.showShort(ResUtils.getString(R.string.the_activation_code_is_invalid)); + } + } + }; + } + + @Nullable + @Override + public Runnable getRunnable() { + return mRunnable; + } + + @Override + public String getShowText() { + return ResUtils.getString(R.string.you_ve_found_an_activation_code); + } + + @Override + public String getBtnText() { + return ResUtils.getString(R.string.activate); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/CreateCDKeyWanPwd.java b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/CreateCDKeyWanPwd.java new file mode 100644 index 00000000..ce3b5a4d --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/CreateCDKeyWanPwd.java @@ -0,0 +1,64 @@ +package per.goweii.wanandroid.utils.wanpwd; + +import android.text.TextUtils; + +import androidx.annotation.Nullable; + +import per.goweii.basic.ui.toast.ToastMaker; +import per.goweii.basic.utils.CopyUtils; +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.BuildConfig; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.utils.UserUtils; +import per.goweii.wanandroid.utils.cdkey.CDKeyUtils; + +/** + * @author CuiZhen + * @date 2020/1/1 + * GitHub: https://github.com/goweii + */ +public class CreateCDKeyWanPwd implements IWanPwd { + + private final Runnable mRunnable; + + public CreateCDKeyWanPwd(String content) { + mRunnable = new Runnable() { + @SuppressWarnings("StringBufferReplaceableByString") + @Override + public void run() { + if (!UserUtils.getInstance().isLogin()) { + ToastMaker.showShort(ResUtils.getString(R.string.please_sign_in_to_use_this_feature)); + return; + } + int id = UserUtils.getInstance().getWanId(); + if (!TextUtils.equals(String.valueOf(id), BuildConfig.DEVELOPER_ID)) { + ToastMaker.showShort(ResUtils.getString(R.string.this_feature_is_only_available_to_developer_accounts)); + return; + } + String cdkey = CDKeyUtils.getInstance().create(content); + StringBuilder s = new StringBuilder(); + s.append(ResUtils.getString(R.string.this_is_an_activation_code_passcode)); + s.append(String.format(BuildConfig.WANPWD_FORMAT, BuildConfig.WANPWD_TYPE_CDKEY, cdkey)); + s.append(ResUtils.getString(R.string.open_the_most_beautiful_android_client_to_activate)); + CopyUtils.copyText(s.toString()); + ToastMaker.showShort(ResUtils.getString(R.string.copied)); + } + }; + } + + @Nullable + @Override + public Runnable getRunnable() { + return mRunnable; + } + + @Override + public String getShowText() { + return ResUtils.getString(R.string.activation_code_generation_warning); + } + + @Override + public String getBtnText() { + return ResUtils.getString(R.string.copy); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/IWanPwd.java b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/IWanPwd.java new file mode 100644 index 00000000..07d02043 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/IWanPwd.java @@ -0,0 +1,17 @@ +package per.goweii.wanandroid.utils.wanpwd; + +import androidx.annotation.Nullable; + +/** + * @author CuiZhen + * @date 2019/12/28 + * GitHub: https://github.com/goweii + */ +public interface IWanPwd { + @Nullable + Runnable getRunnable(); + + String getShowText(); + + String getBtnText(); +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/QQWanPwd.java b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/QQWanPwd.java new file mode 100644 index 00000000..bc39990a --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/QQWanPwd.java @@ -0,0 +1,57 @@ +package per.goweii.wanandroid.utils.wanpwd; + +import androidx.annotation.Nullable; + +import per.goweii.basic.utils.AppInfoUtils; +import per.goweii.basic.utils.AppOpenUtils; +import per.goweii.basic.utils.ResUtils; +import per.goweii.basic.utils.Utils; +import per.goweii.wanandroid.R; + +/** + * @author CuiZhen + * @date 2019/12/28 + * GitHub: https://github.com/goweii + */ +public class QQWanPwd implements IWanPwd { + + private final String content; + + public QQWanPwd(String content) { + this.content = content; + } + + @Nullable + @Override + public Runnable getRunnable() { + return new Runnable() { + @Override + public void run() { + if (AppInfoUtils.isAppInstalled(Utils.getAppContext(), AppInfoUtils.PackageName.QQ)) { + AppOpenUtils.openQQChat(Utils.getAppContext(), getQQ()); + } + } + }; + } + + @Override + public String getShowText() { + return String.format(ResUtils.getString(R.string.you_have_found_a_qq_number), getQQ()); + } + + @Override + public String getBtnText() { + return ResUtils.getString(R.string.add_friends); + } + + private String getQQ() { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < content.length(); i++) { + char c = content.charAt(i); + if (c >= '0' && c <= '9') { + s.append(c); + } + } + return s.toString(); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/UnknownWanPwd.java b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/UnknownWanPwd.java new file mode 100644 index 00000000..e2105d26 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/UnknownWanPwd.java @@ -0,0 +1,35 @@ +package per.goweii.wanandroid.utils.wanpwd; + +import androidx.annotation.Nullable; + +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.utils.router.RouterMap; + +/** + * @author CuiZhen + * @date 2019/12/28 + * GitHub: https://github.com/goweii + */ +public class UnknownWanPwd implements IWanPwd { + @Nullable + @Override + public Runnable getRunnable() { + return new Runnable() { + @Override + public void run() { + RouterMap.SETTING.navigation(); + } + }; + } + + @Override + public String getShowText() { + return ResUtils.getString(R.string.you_ve_discovered_a_mysterious_password); + } + + @Override + public String getBtnText() { + return ResUtils.getString(R.string.go_to_update); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/UserPageWanPwd.java b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/UserPageWanPwd.java new file mode 100644 index 00000000..b0163fd6 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/UserPageWanPwd.java @@ -0,0 +1,66 @@ +package per.goweii.wanandroid.utils.wanpwd; + +import androidx.annotation.Nullable; + +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.utils.router.Param; +import per.goweii.wanandroid.utils.router.RouterMap; + +/** + * @author CuiZhen + * @date 2019/12/28 + * GitHub: https://github.com/goweii + */ +public class UserPageWanPwd implements IWanPwd { + + private final String content; + private String mShowText; + private String mBtnText; + private Runnable mRunnable; + + public UserPageWanPwd(String content) { + this.content = content; + parse(); + } + + @Nullable + @Override + public Runnable getRunnable() { + return mRunnable; + } + + @Override + public String getShowText() { + return mShowText; + } + + @Override + public String getBtnText() { + return mBtnText; + } + + private void parse() { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < content.length(); i++) { + char c = content.charAt(i); + if (c >= '0' && c <= '9') { + stringBuilder.append(c); + } + } + int userId = -1; + try { + userId = Integer.parseInt(stringBuilder.toString()); + } catch (Exception ignore) { + } + mShowText = ResUtils.getString(R.string.you_ve_discovered_a_mystery_user); + mBtnText = ResUtils.getString(R.string.go_to_the_homepage); + int finalUserId = userId; + mRunnable = new Runnable() { + @Override + public void run() { + RouterMap.USER_PAGE.navigation(new Param("id", "" + finalUserId)); + } + }; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/WanPwdParser.java b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/WanPwdParser.java new file mode 100644 index 00000000..df3c7a7a --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/WanPwdParser.java @@ -0,0 +1,160 @@ +package per.goweii.wanandroid.utils.wanpwd; + +import android.text.TextUtils; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import per.goweii.basic.utils.LogUtils; +import per.goweii.wanandroid.BuildConfig; + +/** + * @author CuiZhen + * @date 2019/12/28 + * GitHub: https://github.com/goweii + */ +public class WanPwdParser { + + private static final Pattern PATTERN_PASSWORD = Pattern.compile(BuildConfig.WANPWD_PATTERN); + + private final Pwd mPwd; + private final IWanPwd mWanPwd; + + private WanPwdParser(Pwd pwd) { + this.mPwd = pwd; + switch (mPwd.type) { + default: + case UNKNOWN: + mWanPwd = new UnknownWanPwd(); + break; + case CDKEY: + mWanPwd = new CDKeyWanPwd(mPwd.content); + break; + case QQ: + mWanPwd = new QQWanPwd(mPwd.content); + break; + case USERPAGE: + mWanPwd = new UserPageWanPwd(mPwd.content); + break; + case WEB: + mWanPwd = new WebWanPwd(mPwd.content); + break; + case ABOUTME: + mWanPwd = new AboutMeWanPwd(); + break; + case CREATE_CDKEY: + mWanPwd = new CreateCDKeyWanPwd(mPwd.content); + break; + } + } + + @NonNull + public IWanPwd getWanPwd() { + return mWanPwd; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + WanPwdParser parser = (WanPwdParser) o; + return Objects.equals(mPwd, parser.mPwd); + } + + @Override + public int hashCode() { + return Objects.hash(mPwd); + } + + public static WanPwdParser match(String text) { + List ps = findPasswords(text); + if (ps == null || ps.size() != 1) { + return null; + } + String pw = ps.get(0); + return new WanPwdParser(parsePassword(pw)); + } + + private static List findPasswords(String text) { + Matcher m = PATTERN_PASSWORD.matcher(text); + List strs = new ArrayList<>(); + while (m.find()) { + strs.add(m.group()); + } + return strs; + } + + private static WanPwdParser.Pwd parsePassword(String pwd) { + if (!PATTERN_PASSWORD.matcher(pwd).matches()) { + return new WanPwdParser.Pwd(); + } + String split = String.format(BuildConfig.WANPWD_FORMAT, "", "").substring(1, 2); + String[] strs = pwd.substring(1, pwd.length() - 1).split(split); + for (String str : strs) { + LogUtils.d("CopiedTextProcessor", "parsePassword=" + str); + } + if (strs.length != 2) { + return new WanPwdParser.Pwd(); + } + String type = strs[0]; + String content = strs[1]; + return new WanPwdParser.Pwd(type, content); + } + + public static class Pwd { + public final WanPwdParser.Type type; + public final String content; + + Pwd() { + this(null, null); + } + + Pwd(String type, String content) { + this.type = WanPwdParser.Type.find(type); + this.content = content; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Pwd pwd = (Pwd) o; + return type == pwd.type && TextUtils.equals(content, pwd.content); + } + + @Override + public int hashCode() { + return Objects.hash(type, content); + } + } + + public enum Type { + UNKNOWN(""), + QQ(BuildConfig.WANPWD_TYPE_QQ), + USERPAGE(BuildConfig.WANPWD_TYPE_USERPAGE), + CDKEY(BuildConfig.WANPWD_TYPE_CDKEY), + WEB(BuildConfig.WANPWD_TYPE_WEB), + ABOUTME(BuildConfig.WANPWD_TYPE_ABOUTME), + CREATE_CDKEY(BuildConfig.WANPWD_TYPE_CREATE_CDKEY); + + private final String type; + + Type(String type) { + this.type = type; + } + + public static Type find(String type) { + for (Type t : Type.values()) { + if (TextUtils.equals(t.type, type)) { + return t; + } + } + return UNKNOWN; + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/WebWanPwd.java b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/WebWanPwd.java new file mode 100644 index 00000000..a8fd92a8 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/wanpwd/WebWanPwd.java @@ -0,0 +1,55 @@ +package per.goweii.wanandroid.utils.wanpwd; + +import androidx.annotation.Nullable; + +import java.net.URLDecoder; + +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.utils.router.Router; + +/** + * @author CuiZhen + * @date 2019/12/28 + * GitHub: https://github.com/goweii + */ +public class WebWanPwd implements IWanPwd { + + private final String content; + private String mShowText; + private String mBtnText; + private Runnable mRunnable; + + public WebWanPwd(String content) { + this.content = content; + parse(); + } + + @Nullable + @Override + public Runnable getRunnable() { + return mRunnable; + } + + @Override + public String getShowText() { + return mShowText; + } + + @Override + public String getBtnText() { + return mBtnText; + } + + private void parse() { + mShowText = ResUtils.getString(R.string.you_ve_found_a_link_to_a_web_page); + mBtnText = ResUtils.getString(R.string.open); + final String url = URLDecoder.decode(content); + mRunnable = new Runnable() { + @Override + public void run() { + Router.routeTo(url); + } + }; + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/HitResult.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/HitResult.kt new file mode 100644 index 00000000..c8516f7e --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/HitResult.kt @@ -0,0 +1,45 @@ +package per.goweii.wanandroid.utils.web + +import android.webkit.WebView + +/** + * @author CuiZhen + * @date 2020/3/14 + */ +data class HitResult( + private val result: WebView.HitTestResult +) { + + fun getType(): Type { + return when (result.type) { + WebView.HitTestResult.ANCHOR_TYPE -> Type.ANCHOR_TYPE + WebView.HitTestResult.PHONE_TYPE -> Type.PHONE_TYPE + WebView.HitTestResult.GEO_TYPE -> Type.GEO_TYPE + WebView.HitTestResult.EMAIL_TYPE -> Type.EMAIL_TYPE + WebView.HitTestResult.IMAGE_TYPE -> Type.IMAGE_TYPE + WebView.HitTestResult.IMAGE_ANCHOR_TYPE -> Type.IMAGE_ANCHOR_TYPE + WebView.HitTestResult.SRC_ANCHOR_TYPE -> Type.SRC_ANCHOR_TYPE + WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE -> Type.SRC_IMAGE_ANCHOR_TYPE + WebView.HitTestResult.EDIT_TEXT_TYPE -> Type.EDIT_TEXT_TYPE + WebView.HitTestResult.UNKNOWN_TYPE -> Type.UNKNOWN_TYPE + else -> Type.UNKNOWN_TYPE + } + } + + fun getResult(): String { + return result.extra ?: "" + } + + enum class Type { + UNKNOWN_TYPE, + ANCHOR_TYPE, + PHONE_TYPE, + GEO_TYPE, + EMAIL_TYPE, + IMAGE_TYPE, + IMAGE_ANCHOR_TYPE, + SRC_ANCHOR_TYPE, + SRC_IMAGE_ANCHOR_TYPE, + EDIT_TEXT_TYPE + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/HostInterceptUtils.java b/app/src/main/java/per/goweii/wanandroid/utils/web/HostInterceptUtils.java similarity index 93% rename from app/src/main/java/per/goweii/wanandroid/utils/HostInterceptUtils.java rename to app/src/main/java/per/goweii/wanandroid/utils/web/HostInterceptUtils.java index 3a21a5c6..4fe6737c 100644 --- a/app/src/main/java/per/goweii/wanandroid/utils/HostInterceptUtils.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/HostInterceptUtils.java @@ -1,16 +1,17 @@ -package per.goweii.wanandroid.utils; +package per.goweii.wanandroid.utils.web; import android.text.TextUtils; import java.util.List; +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.R; import per.goweii.wanandroid.module.mine.model.HostEntity; +import per.goweii.wanandroid.utils.SettingUtils; /** * @author CuiZhen * @date 2019/9/4 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class HostInterceptUtils { @@ -25,13 +26,13 @@ public static String getName(int type) { default: break; case TYPE_NOTHING: - name = "不拦截"; + name = ResUtils.getString(R.string.do_not_intercept); break; case TYPE_ONLY_WHITE: - name = "仅白名单"; + name = ResUtils.getString(R.string.whitelist_only); break; case TYPE_INTERCEPT_BLACK: - name = "拦截黑名单"; + name = ResUtils.getString(R.string.block_blacklists); break; } return name; @@ -58,9 +59,9 @@ public static boolean isBlackHost(String host) { } public static final String[] WHITE_HOST = new String[]{ - "www.wanandroid.com", + "wanandroid.com", "study.163.com", - "juejin.im", + "juejin.cn", "www.jianshu.com", "mp.weixin.qq.com", "blog.csdn.net", diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/WebHolder.java b/app/src/main/java/per/goweii/wanandroid/utils/web/WebHolder.java new file mode 100644 index 00000000..fb803fa4 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/WebHolder.java @@ -0,0 +1,884 @@ +package per.goweii.wanandroid.utils.web; + +import android.animation.Animator; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.PixelFormat; +import android.net.Uri; +import android.os.Build; +import android.text.TextUtils; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.webkit.CookieManager; +import android.webkit.CookieSyncManager; +import android.webkit.DownloadListener; +import android.webkit.ValueCallback; +import android.webkit.WebChromeClient; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.FrameLayout; +import android.widget.ProgressBar; + +import androidx.annotation.FloatRange; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; +import androidx.appcompat.view.ContextThemeWrapper; +import androidx.webkit.WebSettingsCompat; +import androidx.webkit.WebViewFeature; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import me.zhanghai.android.materialprogressbar.MaterialProgressBar; +import okhttp3.Cookie; +import per.goweii.anylayer.DecorLayer; +import per.goweii.anylayer.ext.NullAnimatorCreator; +import per.goweii.basic.utils.LogUtils; +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.R; +import per.goweii.wanandroid.common.WanApp; +import per.goweii.wanandroid.module.main.dialog.ImagePreviewDialog; +import per.goweii.wanandroid.utils.CookieUtils; +import per.goweii.wanandroid.utils.DarkModeUtils; +import per.goweii.wanandroid.utils.SettingUtils; +import per.goweii.wanandroid.utils.web.js.JsInjector; +import per.goweii.wanandroid.utils.web.view.ResuableWebView; +import per.goweii.wanandroid.utils.web.view.WebContainer; + +/** + * @author CuiZhen + * @date 2019/10/20 + * GitHub: https://github.com/goweii + */ +public class WebHolder { + private static final String TAG = WebHolder.class.getSimpleName(); + + private OnPageScrollChangeListener mOnPageScrollChangeListener = null; + private OnPageScrollEndListener mOnPageScrollEndListener = null; + private OnPageTitleCallback mOnPageTitleCallback = null; + private OnPageLoadCallback mOnPageLoadCallback = null; + private OnPageProgressCallback mOnPageProgressCallback = null; + private OnHistoryUpdateCallback mOnHistoryUpdateCallback = null; + private OverrideUrlInterceptor mOverrideUrlInterceptor = null; + private InterceptUrlInterceptor mInterceptUrlInterceptor = null; + + private final Activity mActivity; + private final WebContainer mWebContainer; + private final ResuableWebView mWebView; + private final ProgressBar mProgressBar; + private final String mUserAgentString; + + private boolean allowOpenOtherApp = true; + private boolean allowOpenDownload = true; + private boolean allowRedirect = true; + + private final JsInjector jsInjector; + + private boolean useInstanceCache = false; + private boolean isProgressShown = false; + private boolean isPageScrollEnd = false; + private String mCurrentLink = null; + private String mCurrentTitle = null; + + public static WebHolder with(Activity activity, WebContainer container, ProgressBar progressBar) { + return new WebHolder(activity, container, progressBar); + } + + public static WebHolder with(Activity activity, WebContainer container) { + return new WebHolder(activity, container, null); + } + + private static void syncCookiesForWanAndroid(String url) { + if (TextUtils.isEmpty(url)) { + return; + } + String host = Uri.parse(url).getHost(); + if (TextUtils.isEmpty(host) || !host.endsWith("wanandroid.com")) { + return; + } + List cookies = CookieUtils.INSTANCE.loadForUrl(url); + if (cookies.isEmpty()) { + return; + } + CookieManager cookieManager = CookieManager.getInstance(); + cookieManager.setAcceptCookie(true); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + cookieManager.removeSessionCookie(); + cookieManager.removeExpiredCookie(); + } else { + cookieManager.removeSessionCookies(null); + } + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + for (Cookie cookie : cookies) { + cookieManager.setCookie(url, cookie.name() + "=" + cookie.value()); + } + CookieSyncManager.createInstance(WanApp.getAppContext()); + CookieSyncManager.getInstance().sync(); + } else { + for (Cookie cookie : cookies) { + cookieManager.setCookie(url, cookie.name() + "=" + cookie.value()); + } + cookieManager.flush(); + } + } + + @SuppressLint("SetJavaScriptEnabled") + private WebHolder(Activity activity, WebContainer container, ProgressBar progressBar) { + activity.getWindow().setFormat(PixelFormat.TRANSLUCENT); + mActivity = activity; + mWebContainer = container; + int color = ResUtils.getThemeColor(mWebContainer, R.attr.colorSurface); + mWebContainer.setBackgroundColor(color); + Resources.Theme theme = mActivity.getTheme(); + ContextThemeWrapper contextThemeWrapper = new ContextThemeWrapper(mActivity, theme); + if (useInstanceCache) { + mWebView = WebInstance.getInstance(mActivity).obtain(contextThemeWrapper); + } else { + mWebView = WebInstance.getInstance(mActivity).create(contextThemeWrapper); + } + container.addView(mWebView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT)); + if (progressBar == null) { + mProgressBar = (MaterialProgressBar) LayoutInflater.from(activity) + .inflate(R.layout.basic_ui_progress_bar, container, false); + container.addView(mProgressBar, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, + activity.getResources().getDimensionPixelSize(R.dimen.basic_ui_action_bar_loading_bar_height))); + } else { + mProgressBar = progressBar; + } + mProgressBar.setMax(100); + mWebView.setWebChromeClient(new WanWebChromeClient()); + mWebView.setWebViewClient(new WanWebViewClient()); + mWebView.setDownloadListener(new DownloadListener() { + @Override + public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) { + LogUtils.i(TAG, "onDownloadStart:url=" + url); + LogUtils.i(TAG, "onDownloadStart:userAgent=" + userAgent); + LogUtils.i(TAG, "onDownloadStart:contentDisposition=" + contentDisposition); + LogUtils.i(TAG, "onDownloadStart:mimeType=" + mimeType); + LogUtils.i(TAG, "onDownloadStart:contentLength=" + contentLength); + if (!allowOpenDownload) return; + try { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.addCategory(Intent.CATEGORY_BROWSABLE); + intent.setData(Uri.parse(url)); + mWebView.getContext().startActivity(intent); + } catch (Exception ignore) { + } + } + }); + mWebView.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + WebView.HitTestResult hitTestResult = mWebView.getHitTestResult(); + HitResult result = new HitResult(hitTestResult); + switch (result.getType()) { + case IMAGE_TYPE: + case IMAGE_ANCHOR_TYPE: + case SRC_IMAGE_ANCHOR_TYPE: + new ImagePreviewDialog(mActivity, result.getResult()).show(); + return true; + default: + return false; + } + } + }); + mWebView.addOnScrollChangeListener(new ResuableWebView.OnScrollChangeListener() { + @Override + public void onScrollChange(@NonNull ResuableWebView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) { + if (isProgressShown) return; + float percent = getPercent(); + if (mOnPageScrollChangeListener != null) { + mOnPageScrollChangeListener.onPageScrolled(percent); + } + if (!isPageScrollEnd && percent >= 0.95) { + isPageScrollEnd = true; + if (mOnPageScrollEndListener != null) { + mOnPageScrollEndListener.onPageScrollEnd(); + } + } + } + }); + WebSettings webSetting = mWebView.getSettings(); + mUserAgentString = webSetting.getUserAgentString(); + boolean isAppDarkMode = DarkModeUtils.isDarkMode(activity); + if (WebViewFeature.isFeatureSupported(WebViewFeature.ALGORITHMIC_DARKENING)) { + WebSettingsCompat.setAlgorithmicDarkeningAllowed(webSetting, isAppDarkMode); + } + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + if (isAppDarkMode) { + WebSettingsCompat.setForceDark(webSetting, WebSettingsCompat.FORCE_DARK_ON); + } else { + WebSettingsCompat.setForceDark(webSetting, WebSettingsCompat.FORCE_DARK_OFF); + } + } + jsInjector = new JsInjector(mWebView); + jsInjector.attach(); + } + + public WebHolder loadUrl(String url) { + mWebView.loadUrl(url); + return this; + } + + @NonNull + public String getUrl() { + String url = mWebView.getUrl(); + return url == null ? "" : url; + } + + @FloatRange(from = 0.0, to = 1.0) + public float getPercent() { + if (isProgressShown) return 0f; + float contentHeight = mWebView.getContentHeight() * mWebView.getScale(); + if (contentHeight <= 0) return 0f; + float webViewScrollY = mWebView.getScrollY(); + float webViewHeight = mWebView.getHeight(); + float percent = (float) (webViewScrollY + webViewHeight) / (float) contentHeight; + percent = Math.max(0F, percent); + percent = Math.min(1F, percent); + return percent; + } + + public void getShareInfo(OnShareInfoCallback callback) { + String js = "javascript:(" + + "function getShareInfo() {" + + " var map = {};" + + " map[\"title\"] = document.title;" + + " map[\"desc\"] = document.querySelector('meta[name=\"description\"]').getAttribute('content');" + + " var imgElements = document.getElementsByTagName(\"img\");" + + " var imgs = [];" + + " for(var i = 0 ; i < imgElements.length; i++){" + + " var imgEle = imgElements[i];" + + " var w = imgEle.naturalWidth;" + + " var h = imgEle.naturalHeight;" + + " if(w > 200 && h > 100){" + + " imgs.push(imgEle.src);" + + " }" + + " }" + + " map[\"imgs\"] = imgs;" + + " return map;" + + "}" + + ")()"; + mWebView.evaluateJavascript(js, new ValueCallback() { + @Override + public void onReceiveValue(String s) { + String title = ""; + String desc = ""; + List imgs = new ArrayList<>(); + try { + JSONObject jsonObject = new JSONObject(s); + title = jsonObject.optString("title"); + desc = jsonObject.optString("desc"); + JSONArray imgArr = jsonObject.optJSONArray("imgs"); + if (imgArr != null) { + for (int i = 0; i < imgArr.length(); i++) { + String img = imgArr.optString(i); + if (!TextUtils.isEmpty(img)) { + if (!imgs.contains(img)) { + imgs.add(img); + } + } + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + if (TextUtils.isEmpty(title)) { + title = getTitle(); + } + if (TextUtils.isEmpty(desc)) { + desc = getUrl(); + } + if (callback != null) { + callback.onShareInfo(getUrl(), imgs, title, desc); + } + } + }); + } + + @NonNull + public String getTitle() { + String title = mWebView.getTitle(); + return title == null ? "" : title; + } + + @Nullable + public WebView.HitTestResult getHitTestResult() { + return mWebView.getHitTestResult(); + } + + @NonNull + public String getUserAgent() { + return mUserAgentString; + } + + public boolean canGoBack() { + return mWebView.canGoBack(); + } + + public boolean canGoForward() { + return mWebView.canGoForward(); + } + + public boolean canGoBackOrForward(int steps) { + return mWebView.canGoBackOrForward(steps); + } + + public void goBack() { + mWebView.goBack(); + } + + public void goForward() { + mWebView.goForward(); + } + + public void goBackOrForward(int steps) { + mWebView.goBackOrForward(steps); + } + + public void reload() { + mWebView.reload(); + } + + public void stopLoading() { + mWebView.stopLoading(); + } + + public void goTop() { + String js = "javascript:(" + + "function(){\n" + + " var timer = null;\n" + + " cancelAnimationFrame(timer);\n" + + " var startTime = +new Date(); \n" + + " var b = document.body.scrollTop || document.documentElement.scrollTop;\n" + + " var d = 500;\n" + + " var c = b;\n" + + " var timer = requestAnimationFrame(function func(){\n" + + " var t = d - Math.max(0,startTime - (+new Date()) + d);\n" + + " document.documentElement.scrollTop = document.body.scrollTop = t * (-c) / d + b;\n" + + " timer = requestAnimationFrame(func);\n" + + " if(t == d){\n" + + " cancelAnimationFrame(timer);\n" + + " }\n" + + " });\n" + + "}" + + ")()"; + mWebView.evaluateJavascript(js, null); + } + + public void scrollToKeywords(List keywordList) { + String keywords = TextUtils.join(",", keywordList); + String js = "javascript:(\n" + + " function(){\n" + + " var keywords = '" + keywords + "'.split(\",\");\n" + + " var pElements = document.getElementsByTagName('p');\n" + + " for(var i = 0; i < pElements.length; i++) {\n" + + " var pEle = pElements[i];\n" + + " var pText = pEle.innerHTML;\n" + + " var match = true;\n" + + " for (var j = 0; j < keywords.length; j++) {\n" + + " var index = pText.indexOf(keywords[j]);\n" + + " if (index === -1) {\n" + + " match = false;\n" + + " break;\n" + + " }\n" + + " }\n" + + " if (match) {\n" + + " pEle.scrollIntoView();\n" + + " break;\n" + + " }\n" + + " }\n" + + " }\n" + + ")()"; + mWebView.evaluateJavascript(js, null); + } + + public void getHtml(OnHtmlCallback callback) { + mWebView.evaluateJavascript("javascript:document.documentElement.outerHTML", new ValueCallback() { + @Override + public void onReceiveValue(String value) { + if (callback != null) { + String html = value; + if (html != null && html.startsWith("\"") && html.endsWith("\"")) { + html = html.substring(1, html.length() - 1); + html = html.replace("\\u003C", "<") + .replace("\\u003E", ">") + .replace("\\\"", "\"") + .replace("\\\\", "\\"); + } + callback.onHtml(getUrl(), html == null ? "" : html); + } + } + }); + } + + public void onResume() { + mWebView.resumeTimers(); + mWebView.onResume(); + } + + public void onPause() { + mWebView.pauseTimers(); + mWebView.onPause(); + } + + public void onDestroy(boolean destroyOrRecycle) { + jsInjector.detach(); + mProgressBar.clearAnimation(); + if (useInstanceCache) { + if (destroyOrRecycle) { + WebInstance.getInstance(mActivity).destroy(mWebView); + } else { + WebInstance.getInstance(mActivity).recycle(mWebView); + } + } else { + WebInstance.getInstance(mActivity).destroy(mWebView); + } + } + + public boolean handleKeyEvent(int keyCode, KeyEvent keyEvent) { + if (keyCode != KeyEvent.KEYCODE_BACK) { + return false; + } + if (mWebView.canGoBack()) { + mWebView.goBack(); + return true; + } + return false; + } + + public WebHolder setLoadCacheElseNetwork(boolean loadCacheElseNetwork) { + WebSettings webSetting = mWebView.getSettings(); + if (loadCacheElseNetwork) { + webSetting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); + } else { + webSetting.setCacheMode(WebSettings.LOAD_DEFAULT); + } + return this; + } + + public WebHolder setUseInstanceCache(boolean useInstanceCache) { + this.useInstanceCache = useInstanceCache; + return this; + } + + public WebHolder setAllowOpenOtherApp(boolean allowOpenOtherApp) { + this.allowOpenOtherApp = allowOpenOtherApp; + return this; + } + + public WebHolder setAllowOpenDownload(boolean allowOpenDownload) { + this.allowOpenDownload = allowOpenDownload; + return this; + } + + public WebHolder setAllowRedirect(boolean allowRedirect) { + this.allowRedirect = allowRedirect; + return this; + } + + public WebHolder setOnPageTitleCallback(OnPageTitleCallback onPageTitleCallback) { + mOnPageTitleCallback = onPageTitleCallback; + return this; + } + + public WebHolder setOnPageScrollChangeListener(OnPageScrollChangeListener mOnPageScrollChangeListener) { + this.mOnPageScrollChangeListener = mOnPageScrollChangeListener; + return this; + } + + public WebHolder setOnPageScrollEndListener(OnPageScrollEndListener onPageScrollEndListener) { + mOnPageScrollEndListener = onPageScrollEndListener; + return this; + } + + public WebHolder setOnPageLoadCallback(OnPageLoadCallback onPageLoadCallback) { + mOnPageLoadCallback = onPageLoadCallback; + return this; + } + + public WebHolder setOnPageProgressCallback(OnPageProgressCallback onPageProgressCallback) { + mOnPageProgressCallback = onPageProgressCallback; + return this; + } + + public WebHolder setOnHistoryUpdateCallback(OnHistoryUpdateCallback onHistoryUpdateCallback) { + mOnHistoryUpdateCallback = onHistoryUpdateCallback; + return this; + } + + public WebHolder setOverrideUrlInterceptor(OverrideUrlInterceptor overrideUrlInterceptor) { + mOverrideUrlInterceptor = overrideUrlInterceptor; + return this; + } + + public WebHolder setInterceptUrlInterceptor(InterceptUrlInterceptor interceptUrlInterceptor) { + mInterceptUrlInterceptor = interceptUrlInterceptor; + return this; + } + + public class WanWebChromeClient extends WebChromeClient { + + @Override + public void onReceivedTitle(WebView view, String title) { + super.onReceivedTitle(view, title); + if (updateCurrentTitle(view.getUrl(), title) && mOnPageTitleCallback != null) { + mOnPageTitleCallback.onReceivedTitle(title); + } + } + + @Override + public void onProgressChanged(WebView view, int newProgress) { + super.onProgressChanged(view, newProgress); + isPageScrollEnd = false; + jsInjector.onProgressChanged(newProgress); + if (newProgress < 30) { + if (!isProgressShown) { + isProgressShown = true; + onShowProgress(); + } + onProgressChanged(newProgress); + } else if (newProgress > 80) { + onProgressChanged(newProgress); + if (isProgressShown) { + isProgressShown = false; + onHideProgress(); + } + } else { + onProgressChanged(newProgress); + } + } + + private void onShowProgress() { + showProgress(); + if (mOnPageProgressCallback != null) { + mOnPageProgressCallback.onShowProgress(); + } + } + + private void onProgressChanged(int progress) { + setProgress(progress); + if (mOnPageProgressCallback != null) { + mOnPageProgressCallback.onProgressChanged(progress); + } + } + + private void onHideProgress() { + hideProgress(); + if (mOnPageProgressCallback != null) { + mOnPageProgressCallback.onHideProgress(); + } + } + + private void setProgress(int progress) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + mProgressBar.setProgress(progress, true); + } else { + mProgressBar.setProgress(progress); + } + } + + private void showProgress() { + mProgressBar.animate() + .alpha(1F) + .setDuration(200) + .setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + mProgressBar.setVisibility(View.VISIBLE); + setProgress(0); + } + + @Override + public void onAnimationEnd(Animator animation) { + + } + + @Override + public void onAnimationCancel(Animator animation) { + + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }).start(); + } + + private void hideProgress() { + mProgressBar.animate() + .alpha(0F) + .setDuration(200) + .setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + setProgress(100); + mProgressBar.setVisibility(View.GONE); + } + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + }).start(); + } + + private DecorLayer mCustomViewLayer = null; + private WebChromeClient.CustomViewCallback mCustomViewCallback = null; + private int mOldActivityOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + + @Override + public void onShowCustomView(View view, WebChromeClient.CustomViewCallback customViewCallback) { + if (mCustomViewLayer != null) { + mCustomViewLayer.dismiss(); + mCustomViewLayer = null; + } + if (mCustomViewCallback != null) { + mCustomViewCallback.onCustomViewHidden(); + mCustomViewCallback = null; + } + mCustomViewCallback = customViewCallback; + mCustomViewLayer = new DecorLayer(mActivity); + mCustomViewLayer.setLevel(Integer.MAX_VALUE); + mCustomViewLayer.setAnimator(new NullAnimatorCreator()); + view.setLayoutParams(new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + )); + mCustomViewLayer.setChild(view); + mCustomViewLayer.show(); + mOldActivityOrientation = mActivity.getRequestedOrientation(); + mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + + @Override + public void onHideCustomView() { + if (mCustomViewLayer != null) { + mCustomViewLayer.dismiss(); + mCustomViewLayer = null; + } + if (mCustomViewCallback != null) { + mCustomViewCallback.onCustomViewHidden(); + mCustomViewCallback = null; + } + mActivity.setRequestedOrientation(mOldActivityOrientation); + mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + } + } + + public class WanWebViewClient extends WebViewClient { + private WebResourceResponse shouldInterceptRequest(@NonNull Uri reqUri, + @Nullable Map reqHeaders, + @Nullable String reqMethod) { + String url = reqUri.toString(); + LogUtils.d(TAG, "shouldInterceptRequest:url=" + url); + LogUtils.d(TAG, "shouldInterceptRequest:headers=" + reqHeaders); + LogUtils.d(TAG, "shouldInterceptRequest:method=" + reqMethod); + syncCookiesForWanAndroid(url); + if (mInterceptUrlInterceptor == null) return null; + return mInterceptUrlInterceptor.onInterceptUrl(reqUri, reqHeaders, reqMethod); + } + + private boolean shouldOverrideUrlLoading(WebView view, Uri uri) { + LogUtils.i(TAG, "shouldOverrideUrlLoading=" + uri.toString()); + String url = view.getUrl(); + String originalUrl = view.getOriginalUrl(); + WebView.HitTestResult hit = view.getHitTestResult(); + if (hit.getType() == WebView.HitTestResult.UNKNOWN_TYPE || TextUtils.isEmpty(hit.getExtra())) { + LogUtils.i(TAG, "Redirect:url=" + url); + LogUtils.i(TAG, "Redirect:originalUrl=" + originalUrl); + if (!allowRedirect) { + if (!TextUtils.isEmpty(originalUrl) && (originalUrl.startsWith("https://") || originalUrl.startsWith("https://"))) { + return true; + } + } + } + String scheme = uri.getScheme(); + if (!(TextUtils.equals(scheme, "http") || TextUtils.equals(scheme, "https"))) { + if (allowOpenOtherApp) { + try { + Context context = view.getContext(); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + context.startActivity(intent); + } catch (Exception e) { + e.printStackTrace(); + } + } + return true; + } + if (mOverrideUrlInterceptor != null) { + if (mOverrideUrlInterceptor.onOverrideUrl(uri.toString())) { + return true; + } + } + switch (SettingUtils.getInstance().getUrlInterceptType()) { + case HostInterceptUtils.TYPE_ONLY_WHITE: + if (!HostInterceptUtils.isWhiteHost(uri.getHost())) { + return true; + } + break; + case HostInterceptUtils.TYPE_INTERCEPT_BLACK: + if (HostInterceptUtils.isBlackHost(uri.getHost())) { + return true; + } + break; + case HostInterceptUtils.TYPE_NOTHING: + default: + break; + } + return false; + } + + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, String url) { + return shouldInterceptRequest(Uri.parse(url), null, null); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { + Map reqHeaders = request.getRequestHeaders(); + String reqMethod = request.getMethod(); + return shouldInterceptRequest(request.getUrl(), reqHeaders, reqMethod); + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, String url) { + return shouldOverrideUrlLoading(view, Uri.parse(url)); + } + + @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + return shouldOverrideUrlLoading(view, request.getUrl()); + } + + @Override + public void onPageStarted(WebView view, String url, Bitmap favicon) { + LogUtils.d(TAG, "onPageStarted:url=" + url); + jsInjector.onPageStarted(); + super.onPageStarted(view, url, favicon); + mCurrentLink = url; + mCurrentTitle = null; + if (updateCurrentTitle(url, url) && mOnPageTitleCallback != null) { + mOnPageTitleCallback.onReceivedTitle(getUrl()); + } + if (mOnPageLoadCallback != null) { + mOnPageLoadCallback.onPageStarted(); + } + } + + @Override + public void onPageFinished(WebView view, String url) { + super.onPageFinished(view, url); + LogUtils.d(TAG, "onPageFinished:url=" + url); + if (updateCurrentTitle(url, getTitle()) && mOnPageTitleCallback != null) { + mOnPageTitleCallback.onReceivedTitle(getTitle()); + } + if (mOnPageLoadCallback != null) { + mOnPageLoadCallback.onPageFinished(); + } + } + + @Override + public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) { + super.doUpdateVisitedHistory(view, url, isReload); + if (mOnHistoryUpdateCallback != null) { + mOnHistoryUpdateCallback.onHistoryUpdate(isReload); + } + } + } + + private boolean updateCurrentTitle(String url, String title) { + if (TextUtils.equals(url, mCurrentLink)) { + if (!TextUtils.equals(title, mCurrentTitle)) { + mCurrentTitle = title; + return true; + } + } + return false; + } + + public interface OnHtmlCallback { + void onHtml(@NonNull String url, @NonNull String html); + } + + public interface OnShareInfoCallback { + void onShareInfo(@NonNull String url, + @NonNull List covers, + @NonNull String title, + @NonNull String desc); + } + + public interface OnPageScrollChangeListener { + void onPageScrolled(@FloatRange(from = 0.0, to = 1.0) float percent); + } + + public interface OnPageScrollEndListener { + void onPageScrollEnd(); + } + + public interface OnLongClickHitTestResult { + boolean onHitTestResult(@NonNull HitResult result); + } + + public interface OnPageTitleCallback { + void onReceivedTitle(@NonNull String title); + } + + public interface OnPageLoadCallback { + void onPageStarted(); + + void onPageFinished(); + } + + public interface OnPageProgressCallback { + void onShowProgress(); + + void onProgressChanged(int progress); + + void onHideProgress(); + } + + public interface OnHistoryUpdateCallback { + void onHistoryUpdate(boolean isReload); + } + + public interface OverrideUrlInterceptor { + boolean onOverrideUrl(String url); + } + + public interface InterceptUrlInterceptor { + @Nullable + WebResourceResponse onInterceptUrl(@NonNull Uri reqUri, + @Nullable Map reqHeaders, + @Nullable String reqMethod); + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/WebInstance.java b/app/src/main/java/per/goweii/wanandroid/utils/web/WebInstance.java new file mode 100644 index 00000000..2dea3ede --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/WebInstance.java @@ -0,0 +1,149 @@ +package per.goweii.wanandroid.utils.web; + +import android.annotation.SuppressLint; +import android.app.Application; +import android.content.Context; +import android.content.MutableContextWrapper; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.webkit.CookieManager; +import android.webkit.WebChromeClient; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.util.ArrayList; +import java.util.List; + +import per.goweii.wanandroid.utils.web.view.ResuableWebView; + +public class WebInstance { + private static WebInstance sInstance = null; + + private final Application mApplication; + private final List mCache = new ArrayList<>(1); + + private final WebChromeClient mEmptyWebChromeClient = new WebChromeClient(); + private final WebViewClient mEmptyWebViewClient = new WebViewClient(); + + private WebInstance(@NonNull Application application) { + mApplication = application; + ResuableWebView webView = create(application); + mCache.add(webView); + } + + @NonNull + public static WebInstance getInstance(@NonNull Context context) { + if (sInstance == null) { + sInstance = new WebInstance((Application) context.getApplicationContext()); + } + return sInstance; + } + + @SuppressLint("SetJavaScriptEnabled") + @NonNull + public ResuableWebView obtain(@Nullable Context context) { + if (mCache.isEmpty()) { + return create(context); + } + ResuableWebView webView = mCache.remove(0); + if (context != null) { + webView.setBaseContext(context); + } + WebSettings webSetting = webView.getSettings(); + webSetting.setJavaScriptEnabled(true); + webView.clearHistory(); + webView.resumeTimers(); + return webView; + } + + public void recycle(@NonNull ResuableWebView webView) { + webView.setBaseContext(mApplication); + ViewParent parent = webView.getParent(); + if (parent != null) { + ((ViewGroup) parent).removeView(webView); + } + try { + webView.stopLoading(); + int step = 0; + while (webView.canGoBackOrForward(step - 1)) step--; + webView.goBackOrForward(step); + webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null); + webView.clearHistory(); + WebSettings webSetting = webView.getSettings(); + webSetting.setJavaScriptEnabled(false); + webView.pauseTimers(); + webView.setWebChromeClient(mEmptyWebChromeClient); + webView.setWebViewClient(mEmptyWebViewClient); + clearListeners(webView); + } catch (Exception ignore) { + } finally { + if (!mCache.contains(webView)) { + mCache.add(webView); + } + } + } + + public void destroy(@NonNull ResuableWebView webView) { + recycle(webView); + try { + webView.removeAllViews(); + webView.destroy(); + } catch (Exception ignore) { + } finally { + mCache.remove(webView); + } + } + + @SuppressLint("SetJavaScriptEnabled") + public ResuableWebView create(@Nullable Context context) { + ResuableWebView webView = new ResuableWebView(new MutableContextWrapper(context != null ? context : mApplication)); + webView.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + webView.setBackgroundColor(0); + webView.getBackground().setAlpha(0); + webView.setOverScrollMode(WebView.OVER_SCROLL_NEVER); + WebSettings webSetting = webView.getSettings(); + webSetting.setJavaScriptEnabled(true); + webSetting.setJavaScriptCanOpenWindowsAutomatically(false); + webSetting.setAllowFileAccess(true); + webSetting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS); + webSetting.setSupportZoom(false); + webSetting.setBuiltInZoomControls(false); + webSetting.setUseWideViewPort(true); + webSetting.setLoadWithOverviewMode(true); + webSetting.setDomStorageEnabled(true); + webSetting.setGeolocationEnabled(true); + webSetting.setPluginState(WebSettings.PluginState.ON_DEMAND); + webSetting.setCacheMode(WebSettings.LOAD_DEFAULT); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + webSetting.setMixedContentMode(android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true); + } + return webView; + } + + @SuppressLint("ClickableViewAccessibility") + private void clearListeners(@NonNull ResuableWebView webView) { + webView.setDownloadListener(null); + webView.setFindListener(null); + webView.setPictureListener(null); + webView.setOnKeyListener(null); + webView.setOnTouchListener(null); + webView.setOnClickListener(null); + webView.setOnLongClickListener(null); + webView.setOnDragListener(null); + webView.setLayoutAnimationListener(null); + webView.setOnFocusChangeListener(null); + webView.setOnApplyWindowInsetsListener(null); + webView.setOnSystemUiVisibilityChangeListener(null); + webView.clearOnScrollChangeListeners(); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/WebScrollableUtils.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/WebScrollableUtils.kt new file mode 100644 index 00000000..b1575330 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/WebScrollableUtils.kt @@ -0,0 +1,99 @@ +package per.goweii.wanandroid.utils.web + +import android.webkit.ValueCallback +import per.goweii.basic.utils.LogUtils +import per.goweii.wanandroid.utils.web.view.ResuableWebView +import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReentrantLock + +object WebScrollableUtils { + const val DIRECTION_UP = 1 + const val DIRECTION_DOWN = 2 + const val DIRECTION_LEFT = 4 + const val DIRECTION_RIGHT = 8 + + fun getScrollableDirections(webView: ResuableWebView, x: Float, y: Float): Int { + if (true) return 0 + + val density: Float = webView.resources.displayMetrics.density + val wx: Float = (x + webView.scrollX) / density + val wy: Float = (y + webView.scrollY) / density + val js = """javascript:( + function getScrollableDirections() { + let touchX = $wx; + let touchY = $wy; + + let directions = 0; + + // 检查触摸点是否在可滚动的元素内 + const elements = document.querySelectorAll('*'); // 获取页面中的所有元素 + + for (var i = 0; i < elements.length; i++) { + var element = elements[i]; + + // 获取元素的位置和大小 + var rect = element.getBoundingClientRect(); + + // 检查触摸点是否在当前元素内 + if (touchX >= rect.left && touchX <= rect.right && touchY >= rect.top && touchY <= rect.bottom) { + // 垂直滚动判断 + if (element.scrollHeight > element.clientHeight) { + if (element.scrollTop > 0) { + console.log('可以向上滚动:', directions); + directions |= $DIRECTION_UP; // 可以向上滚动 + } + if (element.scrollTop + element.clientHeight < element.scrollHeight) { + console.log('可以向下滚动:', directions); + directions |= $DIRECTION_DOWN; // 可以向下滚动 + } + } + + // 水平滚动判断 + if (element.scrollWidth > element.clientWidth) { + if (element.scrollLeft > 0 || element.scrollWidth > element.clientWidth) { + console.log('可以向左滚动:', directions); + directions |= $DIRECTION_LEFT; // 可以向左滚动 + } + if (element.scrollLeft + element.clientWidth < element.scrollWidth) { + console.log('可以向右滚动:', directions); + directions |= $DIRECTION_RIGHT; // 可以向右滚动 + } + } + } + } + + console.log('touchX:', touchX, 'touchY:', touchY, 'directions:', directions); + + return directions; + } + )()""".trimIndent() + + val l = ReentrantLock() + val c = l.newCondition() + + var directions = 0 + + webView.evaluateJavascript(js, ValueCallback { s -> + l.lock() + try { + directions = Integer.parseInt(s.trim()) + LogUtils.d("WebScrollableUtils", "Scrollable direction: $directions") + c.signalAll() + } catch (e: NumberFormatException) { + LogUtils.e("WebScrollableUtils", "Failed to parse directions: $s") + } finally { + l.unlock() + } + }) + + l.lock() + try { + c.await(500, TimeUnit.MILLISECONDS) + } catch (ignore: InterruptedException) { + } finally { + l.unlock() + } + + return directions + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/cache/HtmlCacheManager.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/cache/HtmlCacheManager.kt new file mode 100644 index 00000000..10b5e538 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/cache/HtmlCacheManager.kt @@ -0,0 +1,102 @@ +package per.goweii.wanandroid.utils.web.cache + +import com.jakewharton.disklrucache.DiskLruCache +import kotlinx.coroutines.* +import per.goweii.basic.utils.coder.MD5Coder +import per.goweii.basic.utils.file.CacheUtils +import per.goweii.wanandroid.utils.web.interceptor.WebHttpClient +import per.goweii.wanandroid.utils.web.interceptor.WebHttpClient.stringRespBody +import java.io.File +import java.io.IOException + +object HtmlCacheManager : CoroutineScope by GlobalScope { + private const val diskMaxSize = 10 * 1024 * 1024L + private var diskLruCache: DiskLruCache? = null + private val submitJobs = hashMapOf() + + private fun openDiskLruCache() { + diskLruCache?.let { + if (it.isClosed) { + diskLruCache = null + } + } + if (diskLruCache == null) { + val file = File(CacheUtils.getCacheDir(), "web/html") + if (!file.exists()) { + file.mkdirs() + } + try { + diskLruCache = DiskLruCache.open(file, 1, 1, diskMaxSize) + } catch (e: IOException) { + } + } + } + + @Suppress("UNREACHABLE_CODE") + fun submit(url: String) { + // 暂时屏蔽自动缓存列表文章html + return + synchronized(this) { + launch(Dispatchers.IO) { + if (!hasSaved(url)) { + val html = WebHttpClient.request(url, "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36", null, "GET") + .stringRespBody() + if (html != null) { + save(url, html) + } + } + }.apply { + submitJobs[url] = this + this.invokeOnCompletion { + submitJobs.remove(url) + } + } + } + } + + fun get(url: String): String? { + synchronized(this) { + submitJobs[url]?.cancel() + openDiskLruCache() + val diskLruCache = diskLruCache ?: return@synchronized + val key = MD5Coder.encode(url) + try { + diskLruCache.get(key)?.let { + val html = it.getString(0) + if (html.isNotEmpty()) { + return html + } + } + } catch (e: IOException) { + } + } + return null + } + + fun save(url: String, html: String) { + synchronized(this) { + openDiskLruCache() + val diskLruCache = diskLruCache ?: return@synchronized + val key = MD5Coder.encode(url) + try { + val editor: DiskLruCache.Editor = diskLruCache.edit(key) + editor[0] = html + editor.commit() + diskLruCache.flush() + } catch (e: IOException) { + } + } + } + + private fun hasSaved(url: String): Boolean { + openDiskLruCache() + val key = MD5Coder.encode(url) + try { + diskLruCache?.get(key)?.let { + return true + } + } catch (e: IOException) { + } + return false + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/cache/ImageCacheManager.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/cache/ImageCacheManager.kt new file mode 100644 index 00000000..e22589db --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/cache/ImageCacheManager.kt @@ -0,0 +1,41 @@ +package per.goweii.wanandroid.utils.web.cache + +import android.net.Uri +import android.webkit.WebResourceResponse +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.request.RequestOptions +import per.goweii.basic.utils.Utils +import java.io.InputStream + +object ImageCacheManager { + private val supportExts = arrayListOf( + "jpg", "jpeg", + "png", "apng", + "webp", "awebp", + "gif", + ) + + fun get( + uri: Uri, + userAgent: String?, + reqHeaders: Map?, + reqMethod: String? + ): WebResourceResponse? { + val lastPathSegment = uri.lastPathSegment ?: return null + val lastIndexOf = lastPathSegment.lastIndexOf(".") + if (lastIndexOf == -1) return null + val ext = lastPathSegment.substring(lastIndexOf + 1) + if (ext.isBlank()) return null + supportExts.find { it.equals(ext, true) } ?: return null + return Glide.with(Utils.getAppContext()).asFile() + .apply(RequestOptions().diskCacheStrategy(DiskCacheStrategy.DATA)) + .load(uri.toString()).submit().get() + .inputStream() + .toWebResourceResponse("image/$ext") + } + + private fun InputStream.toWebResourceResponse(mimeType: String): WebResourceResponse { + return WebResourceResponse(mimeType, Charsets.UTF_8.name(), this) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/cache/ReadingModeManager.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/cache/ReadingModeManager.kt new file mode 100644 index 00000000..e11672cc --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/cache/ReadingModeManager.kt @@ -0,0 +1,28 @@ +package per.goweii.wanandroid.utils.web.cache + +import per.goweii.rxhttp.request.RxRequest +import per.goweii.wanandroid.http.WanApi +import per.goweii.wanandroid.module.main.model.WebArticleUrlRegexBean + +object ReadingModeManager { + private val urlRegexList = arrayListOf() + + fun setup() { + RxRequest.create(WanApi.api().webArticleUrlRegex) + .request(object : RxRequest.ResultCallback> { + override fun onSuccess(code: Int, data: List) { + urlRegexList.clear() + urlRegexList.addAll(data) + } + + override fun onFailed(code: Int, msg: String) { + } + }) + } + + fun getUrlRegexBeanForHost(host: String?): WebArticleUrlRegexBean? { + if (urlRegexList.isEmpty()) return null + host ?: return null + return urlRegexList.firstOrNull { host.contains(it.host) } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/cache/ResCacheManager.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/cache/ResCacheManager.kt new file mode 100644 index 00000000..0c826fb9 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/cache/ResCacheManager.kt @@ -0,0 +1,115 @@ +package per.goweii.wanandroid.utils.web.cache + +import android.net.Uri +import android.util.LruCache +import android.webkit.WebResourceResponse +import com.jakewharton.disklrucache.DiskLruCache +import per.goweii.basic.utils.coder.MD5Coder +import per.goweii.basic.utils.file.CacheUtils +import per.goweii.wanandroid.utils.web.interceptor.WebHttpClient +import per.goweii.wanandroid.utils.web.interceptor.WebResUrlInterceptor.resp +import java.io.File +import java.io.IOException + +object ResCacheManager { + private const val memoryMaxSize = 100 + private const val diskMaxSize = 10 * 1024 * 1024L + + private val memoryLruCache: LruCache = LruCache(memoryMaxSize) + private var diskLruCache: DiskLruCache? = null + + private val supportExts = arrayListOf( + "css", + ) + + private fun openDiskLruCache() { + diskLruCache?.let { + if (it.isClosed) { + diskLruCache = null + } + } + if (diskLruCache == null) { + val file = File(CacheUtils.getCacheDir(), "web/res") + if (!file.exists()) { + file.mkdirs() + } + try { + diskLruCache = DiskLruCache.open(file, 1, 1, diskMaxSize) + } catch (e: IOException) { + } + } + } + + fun get( + uri: Uri, + userAgent: String?, + reqHeaders: Map?, + reqMethod: String? + ): WebResourceResponse? { + val lastPathSegment = uri.lastPathSegment ?: return null + val lastIndexOf = lastPathSegment.lastIndexOf(".") + if (lastIndexOf == -1) return null + val ext = lastPathSegment.substring(lastIndexOf + 1) + if (ext.isBlank()) return null + val url = uri.toString() + supportExts.find { it.equals(ext, true) } ?: return null + var res = getFromCache(url) + if (res.isNullOrEmpty()) { + res = WebHttpClient.request(url, userAgent, reqHeaders, reqMethod).resp() + if (!res.isNullOrEmpty()) { + saveToCache(url, res) + } + } + return res?.toWebResourceResponse("text/$ext") + } + + private fun String.toWebResourceResponse(mimeType: String): WebResourceResponse { + return WebResourceResponse( + mimeType, + Charsets.UTF_8.name(), + this.byteInputStream(Charsets.UTF_8) + ) + } + + private fun getFromCache(url: String): String? { + memoryLruCache.get(url)?.let { + if (it.isNotEmpty()) { + return it + } else { + memoryLruCache.remove(url) + } + } + synchronized(this) { + openDiskLruCache() + val diskLruCache = diskLruCache ?: return@synchronized + val key = MD5Coder.encode(url) + try { + diskLruCache.get(key)?.let { + val res = it.getString(0) + if (res.isNotEmpty()) { + memoryLruCache.put(url, res) + return res + } + } + } catch (e: IOException) { + } + } + return null + } + + private fun saveToCache(url: String, res: String) { + memoryLruCache.put(url, res) + synchronized(this) { + openDiskLruCache() + val diskLruCache = diskLruCache ?: return@synchronized + val key = MD5Coder.encode(url) + try { + val editor: DiskLruCache.Editor = diskLruCache.edit(key) + editor[0] = res + editor.commit() + diskLruCache.flush() + } catch (e: IOException) { + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/css/CssStyleManager.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/css/CssStyleManager.kt new file mode 100644 index 00000000..b2737d7c --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/css/CssStyleManager.kt @@ -0,0 +1,201 @@ +package per.goweii.wanandroid.utils.web.css + +import per.goweii.basic.utils.file.CacheUtils +import per.goweii.wanandroid.common.Constant +import per.goweii.wanandroid.utils.web.interceptor.WebHttpClient +import java.io.File +import java.util.regex.Pattern + +/** + * @author CuiZhen + * @date 2020/7/12 + */ +object CssStyleManager { + private const val headRegex = "()([\\s\\S]*?)()" + private const val scriptRegex = "()([\\s\\S]*?)()" + private const val styleRegex = "()([\\s\\S]*?)()" + private const val url = "https://goweii/web/css/" + + private val disableJsModifyCssScript = """ +// 将 style 属性设置为只读 +Object.defineProperty(HTMLElement.prototype, 'style', { + get: function() { + return this.attributes.style; + }, + set: function(value) { + throw new Error('CSS modification is not allowed.'); + } +}); + +// 或者监听DOM mutation events来阻止修改 +document.addEventListener('DOMSubtreeModified', function(e) { + var target = e.target; + if (target instanceof HTMLElement && target.style !== target.getAttribute('style')) { + throw new Error('CSS modification is not allowed.'); + } +}, true); + """.trimIndent(); + + private val cache = hashMapOf() + private val updateTime = hashMapOf() + + fun addStyle(html: String, name: String): String { + val pattern = Pattern.compile(headRegex) + val m = pattern.matcher(html) + return if (m.find()) { + val start = m.start(0) + val end = m.end(0) + val sb = StringBuilder() + sb.append(html.substring(0 until start)) + sb.append(m.group(1)) + sb.append("\n") + sb.append("") + sb.append("\n") + get(name)?.let { css -> + sb.append("") + sb.append("\n") + } + sb.append(m.group(2)) + sb.append(m.group(3)) + sb.append(html.substring(end until html.length)) + sb.toString() + } else { + html + } + } + + fun disableJsModifyCss(html: String): String { + val pattern = Pattern.compile(scriptRegex) + val m = pattern.matcher(html) + return if (m.find()) { + val start = m.start(0) + val end = m.end(0) + val sb = StringBuilder() + sb.append(html.substring(0 until start)) + sb.append(m.group(1)) + sb.append(m.group(2)) + sb.append("\n") + sb.append( + """ + Object.defineProperty(HTMLElement.prototype, 'style', { + get: function() { + return this.attributes.style; + }, + set: function(value) { + throw new Error('CSS modification is not allowed.'); + } + }); + """.trimIndent() + ) + sb.append("\n") + sb.append(m.group(3)) + sb.append(html.substring(end until html.length)) + sb.toString() + } else { + html + } + } + + fun appendCssOnFirstStyle(html: String, name: String): String { + val css = get(name) ?: return html + val pattern = Pattern.compile(styleRegex) + val m = pattern.matcher(html) + return if (m.find()) { + val start = m.start(0) + val end = m.end(0) + val sb = StringBuilder() + sb.append(html.substring(0 until start)) + sb.append(m.group(1)) + sb.append(m.group(2)) + sb.append("\n") + sb.append(css) + sb.append("\n") + sb.append(m.group(3)) + sb.append(html.substring(end until html.length)) + sb.toString() + } else { + html + } + } + + fun get(name: String): String? { + var css = cache[name] + if (!css.isNullOrEmpty()) { + return css + } + css = getFromFile(name) + if (!css.isNullOrEmpty()) { + cache[name] = css + return css + } + css = getFromNet(name) + if (!css.isNullOrEmpty()) { + writeToFile(name, css) + return css + } + return null + } + + private fun getFromFile(name: String): String? { + val lastTime = updateTime[name] ?: 0 + val currTime = System.currentTimeMillis() + if (currTime - lastTime > 24 * 60 * 60 * 1000L) { + updateTime[name] = currTime + return null + } + val cssFile = getCacheFile(name) + return try { + cssFile.readText() + } catch (e: Exception) { + e.printStackTrace() + null + } + } + + private fun writeToFile(name: String, css: String) { + val cssFile = getCacheFile(name) + if (cssFile.exists()) { + cssFile.delete() + } else { + if (cssFile.parentFile?.exists() != true) { + cssFile.parentFile?.mkdirs() + } + } + try { + cssFile.writeText(css) + updateTime[name] = System.currentTimeMillis() + } catch (e: Exception) { + e.printStackTrace() + } + } + + private fun getCacheFile(name: String): File { + return File(CacheUtils.getFilesDir(), "web/css/${name}.css") + } + + private fun getFromNet(name: String): String? { + val call = WebHttpClient.request( + url = "${url}${name}.css", + userAgent = Constant.BROWSER_UA, + ) + return try { + val response = call.execute() + if (response.isSuccessful) { + response.body()?.string() + } else { + null + } + } catch (e: Exception) { + e.printStackTrace() + null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/BaseWebUrlInterceptor.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/BaseWebUrlInterceptor.kt new file mode 100644 index 00000000..a8534624 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/BaseWebUrlInterceptor.kt @@ -0,0 +1,24 @@ +package per.goweii.wanandroid.utils.web.interceptor + +import okhttp3.Call +import per.goweii.wanandroid.utils.web.interceptor.WebHttpClient.stringRespBody + +/** + * @author CuiZhen + * @date 2020/2/25 + */ +abstract class BaseWebUrlInterceptor : WebUrlInterceptor { + private val callList = arrayListOf() + + fun Call.resp(): String? { + callList.add(this) + val resp = stringRespBody() + callList.remove(this) + return resp + } + + override fun cancel() { + callList.forEach { it.cancel() } + callList.clear() + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/WebHttpClient.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/WebHttpClient.kt new file mode 100644 index 00000000..929348a7 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/WebHttpClient.kt @@ -0,0 +1,52 @@ +package per.goweii.wanandroid.utils.web.interceptor + +import okhttp3.Call +import okhttp3.OkHttpClient +import okhttp3.Request +import per.goweii.rxhttp.core.RxHttp +import per.goweii.wanandroid.http.GoweiiHostInterceptor + +/** + * @author CuiZhen + * @date 2020/2/25 + */ +object WebHttpClient { + private val okHttpClient: OkHttpClient = OkHttpClient.Builder() + .addInterceptor(GoweiiHostInterceptor()) + .build() + + fun request( + url: String, + userAgent: String? = null, + headers: Map? = null, + method: String? = null + ): Call { + val requestBuilder = Request.Builder().url(url) + method?.let { + requestBuilder.method(method, null) + } + requestBuilder.addHeader("USER-AGENT", + userAgent + ?: "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Mobile Safari/537.36" + ) + headers?.forEach { + requestBuilder.addHeader(it.key, it.value) + } + val request = requestBuilder.build() + return okHttpClient.newCall(request) + } + + fun Call.stringRespBody(): String? { + return try { + val response = execute() + if (response.isSuccessful) { + response.body()?.string() + } else { + null + } + } catch (e: Exception) { + null + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/WebReadingModeInterceptor.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/WebReadingModeInterceptor.kt new file mode 100644 index 00000000..226051d5 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/WebReadingModeInterceptor.kt @@ -0,0 +1,45 @@ +package per.goweii.wanandroid.utils.web.interceptor + +import android.net.Uri +import android.webkit.WebResourceResponse +import per.goweii.wanandroid.utils.web.cache.HtmlCacheManager +import per.goweii.wanandroid.utils.web.cache.ReadingModeManager +import per.goweii.wanandroid.utils.web.css.CssStyleManager +import java.io.ByteArrayInputStream +import java.util.regex.Pattern + +object WebReadingModeInterceptor : BaseWebUrlInterceptor() { + override fun intercept( + pageUri: Uri, + uri: Uri, + userAgent: String?, + reqHeaders: Map?, + reqMethod: String? + ): WebResourceResponse? { + if (reqMethod != "GET") return null + val urlRegexBean = ReadingModeManager.getUrlRegexBeanForHost(uri.host) ?: return null + val url = uri.toString() + val pattern = Pattern.compile(urlRegexBean.regex) + val matcher = pattern.matcher(url) + if (!matcher.find()) { + return null + } + var html = HtmlCacheManager.get(url) + if (html == null) { + html = WebHttpClient.request(url, userAgent, reqHeaders, reqMethod).resp()?.also { + HtmlCacheManager.save(url, it) + } + } + html ?: return null + if (html.isNotEmpty()) { + // val style = Element("style") + // .attr("type", "text/css") + // .text(CssStyleManager.get(urlRegexBean.name)) + // val doc = Jsoup.parse(html, url) + // doc.head().appendChild(style) + // html = doc.html() ?: return null + html = CssStyleManager.addStyle(html, urlRegexBean.name) + } + return WebResourceResponse("text/html", "utf-8", ByteArrayInputStream(html.toByteArray())) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/WebResUrlInterceptor.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/WebResUrlInterceptor.kt new file mode 100644 index 00000000..404978e9 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/WebResUrlInterceptor.kt @@ -0,0 +1,24 @@ +package per.goweii.wanandroid.utils.web.interceptor + +import android.net.Uri +import android.webkit.WebResourceResponse +import per.goweii.wanandroid.utils.web.cache.ImageCacheManager +import per.goweii.wanandroid.utils.web.cache.ResCacheManager + +/** + * @author CuiZhen + * @date 2020/2/25 + */ +object WebResUrlInterceptor : BaseWebUrlInterceptor() { + override fun intercept( + pageUri: Uri, + uri: Uri, + userAgent: String?, + reqHeaders: Map?, + reqMethod: String? + ): WebResourceResponse? { + ResCacheManager.get(uri, userAgent, reqHeaders, reqMethod)?.let { return it } + ImageCacheManager.get(uri, userAgent, reqHeaders, reqMethod)?.let { return it } + return null + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/WebUrlInterceptor.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/WebUrlInterceptor.kt new file mode 100644 index 00000000..489d41aa --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/interceptor/WebUrlInterceptor.kt @@ -0,0 +1,18 @@ +package per.goweii.wanandroid.utils.web.interceptor + +import android.net.Uri +import android.webkit.WebResourceResponse + +/** + * @author CuiZhen + * @date 2020/2/25 + */ +interface WebUrlInterceptor { + fun intercept(pageUri: Uri, + uri: Uri, + userAgent: String?, + reqHeaders: Map?, + reqMethod: String?): WebResourceResponse? + + fun cancel() +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/js/BaseJsInterface.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/js/BaseJsInterface.kt new file mode 100644 index 00000000..d8f61f6b --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/js/BaseJsInterface.kt @@ -0,0 +1,65 @@ +package per.goweii.wanandroid.utils.web.js + +import android.webkit.WebView +import per.goweii.basic.utils.DebugUtils +import per.goweii.basic.utils.Utils + +/** + * @author CuiZhen + * @date 2020/3/7 + */ +abstract class BaseJsInterface(val jsName: String) { + + protected lateinit var webView: WebView + private val jsString: String? by lazy { + val jsStr = loadJsFromString() + if (!jsStr.isNullOrEmpty()) { + return@lazy jsStr + } + val assetsName = loadJsFromAssets() + if (assetsName.isNullOrEmpty()) { + return@lazy null + } + try { + Utils.getAppContext().resources.assets.open(assetsName).use { + try { + val buffer = ByteArray(it.available()) + it.read(buffer) + String(buffer) + } catch (e: Throwable) { + null + } + } + } catch (e: Throwable) { + null + } + } + private val jsInitCode: String? by lazy { + loadJsInitCode() + } + + abstract fun loadJsFromString(): String? + abstract fun loadJsFromAssets(): String? + abstract fun loadJsInitCode(): String? + open fun onlyDebug(): Boolean = false + + fun attach(webView: WebView) { + this.webView = webView + } + + fun inject() { + if (!DebugUtils.isDebug()) return + if (!jsString.isNullOrEmpty()) { + webView.loadUrl("javascript: $jsString") + } + if (!jsInitCode.isNullOrEmpty()) { + val name = this::class.java.simpleName + webView.loadUrl(""" + javascript: var init$name = document.createElement("script"); + init$name.id= "init$name"; + init$name.text= "$jsInitCode"; + document.body.appendChild(init$name); + """.trimIndent()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/js/ImageClickJsInterface.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/js/ImageClickJsInterface.kt new file mode 100644 index 00000000..25ed4dd8 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/js/ImageClickJsInterface.kt @@ -0,0 +1,69 @@ +package per.goweii.wanandroid.utils.web.js + +import android.os.Handler +import android.os.Looper +import android.os.Message +import android.util.Patterns +import android.webkit.JavascriptInterface +import per.goweii.wanandroid.module.main.dialog.ImagePreviewDialog +import java.util.regex.Matcher + + +class ImageClickJsInterface : BaseJsInterface("IMAGE_CLICK_INJECT") { + + private val mainHandler = object : Handler(Looper.getMainLooper()) { + override fun handleMessage(msg: Message) { + val url = msg.obj as String + ImagePreviewDialog(webView.context, url).show() + //ImageListPreviewDialog(webView.context, urls, index).show() + } + } + + override fun loadJsFromAssets(): String? = null + override fun loadJsFromString(): String? = """ + (function(){ + var objs = document.getElementsByTagName("img"); + var imghtmls = new Array(); + for(var i = 0; i < objs.length; i++){ + imghtmls[i] = objs[i].outerHTML; + objs[i].onclick = function(){ + window.IMAGE_CLICK_INJECT.previewImage(this.src, this.outerHTML, imghtmls); + } + } + })() + """.trimIndent() + + override fun loadJsInitCode(): String? = null + + @JavascriptInterface + fun previewImage(imgurl: String?, imghtml: String?, imghtmls: Array) { + var url: String? = null + if (isImgUrl(imgurl)) { + url = imgurl + } else { + val img = findUrlFromHtml(imghtml) + if (isImgUrl(img)) { + url = img + } + } + if (url.isNullOrEmpty()) return + val msg = mainHandler.obtainMessage() + msg.obj = url + mainHandler.sendMessage(msg) + } + + private fun isImgUrl(imgurl: String?): Boolean { + if (imgurl.isNullOrEmpty()) return false + return imgurl.startsWith("http") + } + + private fun findUrlFromHtml(imghtml: String?): String? { + if (imghtml.isNullOrEmpty()) return null + val matcher: Matcher = Patterns.WEB_URL.matcher(imghtml) + return if (matcher.find()) { + matcher.group() + } else { + null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/js/JsInjector.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/js/JsInjector.kt new file mode 100644 index 00000000..2a2a94f5 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/js/JsInjector.kt @@ -0,0 +1,70 @@ +package per.goweii.wanandroid.utils.web.js + +import android.os.Handler +import android.os.Message +import android.webkit.JavascriptInterface +import android.webkit.WebView + +/** + * @author CuiZhen + * @date 2020/3/7 + */ +class JsInjector( + private val webView: WebView +) : Handler() { + private val JS_NAME = JsInjector::class.java.simpleName + + private val jsInterfaces = mutableListOf() + + @Volatile + private var injected = false + + fun attach() { + webView.addJavascriptInterface(this, JS_NAME) + } + + fun detach() { + removeCallbacksAndMessages(null) + } + + fun addJsInterface(jsInterface: BaseJsInterface) { + webView.addJavascriptInterface(jsInterface, jsInterface.jsName) + jsInterface.attach(webView) + jsInterfaces.add(jsInterface) + } + + fun onPageStarted() { + injected = false + removeCallbacksAndMessages(null) + } + + fun onProgressChanged(p: Int) { + if (injected) return + checkHtmlLoadFinish() + } + + private fun checkHtmlLoadFinish() { + webView.loadUrl(""" + javascript: window.$JS_NAME.htmlBody(document.getElementsByTagName('body')[0].innerHTML); + """.trimIndent()) + } + + @JavascriptInterface + fun htmlBody(body: String?) { + if (body.isNullOrEmpty()) return + sendEmptyMessage(0) + } + + override fun handleMessage(msg: Message) { + removeCallbacksAndMessages(null) + if (injected) return + injected = true + inject() + } + + private fun inject() { + jsInterfaces.forEach { + it.inject() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/utils/WebFetch.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/utils/WebFetch.kt new file mode 100644 index 00000000..b69a798b --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/utils/WebFetch.kt @@ -0,0 +1,154 @@ +package per.goweii.wanandroid.utils.web.utils + +import android.annotation.SuppressLint +import android.content.Context +import android.text.TextUtils +import android.view.View +import android.webkit.WebChromeClient +import android.webkit.WebView +import android.webkit.WebViewClient +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withContext +import org.json.JSONException +import org.json.JSONObject +import per.goweii.wanandroid.utils.web.WebInstance +import per.goweii.wanandroid.utils.web.view.ResuableWebView +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException + +class WebFetch(private val context: Context) { + companion object { + private const val TAG = "WebFetch" + + private const val FETCH_JS = + """javascript:( +function getShareInfo() { +var map = {}; +map["title"] = document.title; +map["desc"] = document.querySelector('meta[name="description"]').getAttribute('content'); +var imgElements = document.getElementsByTagName("img"); +var imgs = []; +for(var i = 0 ; i < imgElements.length; i++){ + var imgEle = imgElements[i]; + var w = imgEle.naturalWidth; + var h = imgEle.naturalHeight; + if(w > 200 && h > 100) { + imgs.push(imgEle.src); + } +} +map["imgs"] = imgs; +map["html"] = document.documentElement.outerHTML; +return map; +} +)()""" + + @SuppressLint("StaticFieldLeak") + private var instance: WebFetch? = null + + fun getInstance(context: Context): WebFetch { + if (instance == null) { + synchronized(WebFetch::class) { + if (instance == null) { + instance = WebFetch(context.applicationContext) + } + } + } + return instance!! + } + } + + suspend fun fetch(url: String): WebFetchResult { + return withContext(Dispatchers.Main) { + suspendCancellableCoroutine { continuation -> + val webView = prepareWebView() + webView.webChromeClient = object : WebChromeClient() {} + webView.webViewClient = object : WebViewClient() { + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + if (continuation.isActive) { + webView.fetch( + onSuccess = { e -> + if (continuation.isActive) { + continuation.resume(e) + } + }, + onFailure = { e -> + if (continuation.isActive) { + continuation.resumeWithException(e) + } + }, + ) + } + } + } + webView.loadUrl(url) + continuation.invokeOnCancellation { + WebInstance.getInstance(context).recycle(webView) + } + } + } + } + + private fun WebView.fetch( + onSuccess: (WebFetchResult) -> Unit, + onFailure: (Exception) -> Unit, + ) { + evaluateJavascript(FETCH_JS) { value -> + try { + val jsonObject = JSONObject(value) + val title = jsonObject.optString("title") ?: "" + val desc = jsonObject.optString("desc") ?: "" + val imgs = jsonObject.optJSONArray("imgs")?.let { arr -> + val imgs = arrayListOf() + for (i in 0..") + .replace("\\\"", "\"") + .replace("\\\\", "\\") + } + onSuccess( + WebFetchResult( + title = title, + desc = desc, + images = imgs, + html = html, + ) + ) + } catch (e: JSONException) { + onFailure(e) + } + } + } + + private fun prepareWebView(): ResuableWebView { + val webView = WebInstance.getInstance(context).obtain(context) + val width = 1080 + val height = 1920 + webView.measure( + View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY), + View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY) + ) + webView.layout(0, 0, width, height) + return webView + } + + data class WebFetchResult( + val title: String, + val desc: String, + val images: List, + val html: String, + ) +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/utils/WebReadability.kt b/app/src/main/java/per/goweii/wanandroid/utils/web/utils/WebReadability.kt new file mode 100644 index 00000000..4a382118 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/utils/WebReadability.kt @@ -0,0 +1,89 @@ +package per.goweii.wanandroid.utils.web.utils + +import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter +import com.vladsch.flexmark.util.data.MutableDataSet +import net.dankito.readability4j.Article +import net.dankito.readability4j.extended.Readability4JExtended +import org.jsoup.Jsoup +import kotlin.text.replace + +class WebReadability( + val url: String, + val html: String, + val preText: Boolean, +) { + private val cleanHtml: String by lazy { + val doc = Jsoup.parse(html, url) + doc.outputSettings().prettyPrint(false) + + if (preText) { + // 移除代码块 + doc.select("pre,code,blockquote,table,img,iframe,hr").remove() + // 移除导航、空标签、脚本样式 + doc.select("script,style,noscript,p:empty,div:empty").remove() + // 清理空白元素 + doc.allElements.forEach { + if (it.text().trim().isEmpty() && it.childrenSize() == 0) it.remove() + } + } + + var html = doc.html() + + html = html.replace("""\\n""", "") + html = html.replace("""\n""", "") + html = html.replace("""\\r""", "") + html = html.replace("""\r""", "") + + html + } + + private val readability: Readability4JExtended by lazy { + Readability4JExtended(url, cleanHtml) + } + private val article: Article by lazy { + readability.parse() + } + + private val mdConverter: FlexmarkHtmlConverter by lazy { + val options = MutableDataSet() + FlexmarkHtmlConverter.builder(options).build() + } + + fun toReadabilityHtml(): String { + var contentHtml = article.contentWithUtf8Encoding ?: cleanHtml + if (preText) { + contentHtml = contentHtml.replace(Regex("(.+?)"), "$1") + } + return contentHtml; + } + + /** + * 网页源码提取正文并转为 Markdown + */ + fun toMarkdown(): String { + val contentHtml = toReadabilityHtml() + var md = mdConverter.convert(contentHtml) + + md = md.replace("""\\n""", "") + md = md.replace("""\n""", "") + md = md.replace("""\\r""", "") + md = md.replace("""\r""", "") + + if (preText) { + // 清除markdown标记:# * - > ` 等朗读无用符号 + md = md.replace(Regex("^\\s*#{1,6}\\s*"), "") + md = md.replace(Regex("^\\s*[*\\-]\\s*"), "") + md = md.replace(Regex("`{1,3}[\\s\\S]*?`{1,3}"), "") + md = md.replace(Regex("\\[.+?]\\(.+?\\)"), "") + md = md.replace(Regex("!\\[.*?]\\(.*?\\)"), "") + // 统一清理换行与空格 + md = md.replace("\r", "") + md = md.replace(Regex("\\n+"), "\n") + md = md.lines().joinToString(" ") { it.trim() } + md = md.replace(Regex("\\s+"), " ") + md = md.trim() + } + + return md + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/utils/web/view/ResuableWebView.java b/app/src/main/java/per/goweii/wanandroid/utils/web/view/ResuableWebView.java new file mode 100644 index 00000000..04704ea2 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/view/ResuableWebView.java @@ -0,0 +1,173 @@ +package per.goweii.wanandroid.utils.web.view; + +import android.content.Context; +import android.content.MutableContextWrapper; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.webkit.WebView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.ScrollingView; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +import per.goweii.wanandroid.utils.web.WebScrollableUtils; + +/** + * @author CuiZhen + * @date 2019/11/30 + * GitHub: https://github.com/goweii + */ +public class ResuableWebView extends WebView implements ScrollingView { + private final List mOnScrollChangeListeners = new LinkedList<>(); + private Float touchX = null; + private Float touchY = null; + + public ResuableWebView(@NonNull Context context) { + super(new MutableContextWrapper(context)); + } + + public ResuableWebView(@NonNull Context context, @Nullable AttributeSet attrs) { + super(new MutableContextWrapper(context), attrs); + } + + public ResuableWebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(new MutableContextWrapper(context), attrs, defStyleAttr); + } + + public ResuableWebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(new MutableContextWrapper(context), attrs, defStyleAttr, defStyleRes); + } + + public ResuableWebView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, boolean privateBrowsing) { + super(new MutableContextWrapper(context), attrs, defStyleAttr, privateBrowsing); + } + + public void setBaseContext(@NonNull Context context) { + final MutableContextWrapper contextWrapper = (MutableContextWrapper) this.getContext(); + contextWrapper.setBaseContext(context); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + int action = ev.getAction(); + switch (action) { + case MotionEvent.ACTION_DOWN: + touchX = ev.getX(); + touchY = ev.getY(); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + touchX = null; + touchY = null; + break; + } + return super.dispatchTouchEvent(ev); + } + + @Override + public void scrollBy(int x, int y) { + super.scrollBy(x, y); + } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + for (int i = 0; i < mOnScrollChangeListeners.size(); i++) { + OnScrollChangeListener listener = mOnScrollChangeListeners.get(i); + if (listener != null) { + listener.onScrollChange(this, l, t, oldl, oldt); + } + } + } + + public void addOnScrollChangeListener(OnScrollChangeListener l) { + if (l != null) { + mOnScrollChangeListeners.add(l); + } + } + + public void removeOnScrollChangeListener(OnScrollChangeListener l) { + if (l != null) { + mOnScrollChangeListeners.remove(l); + } + } + + public void clearOnScrollChangeListeners() { + mOnScrollChangeListeners.clear(); + } + + @Override + public boolean canScrollHorizontally(int direction) { + if (super.canScrollHorizontally(direction)) { + return true; + } + if (touchX == null || touchY == null) { + return false; + } + int directions = WebScrollableUtils.INSTANCE.getScrollableDirections(this, touchX, touchY); + if (direction < 0 && (directions & WebScrollableUtils.DIRECTION_LEFT) != 0) { + return true; + } + if (direction > 0 && (directions & WebScrollableUtils.DIRECTION_RIGHT) != 0) { + return true; + } + return false; + } + + @Override + public boolean canScrollVertically(int direction) { + if (super.canScrollVertically(direction)) { + return true; + } + if (touchX == null || touchY == null) { + return false; + } + int directions = WebScrollableUtils.INSTANCE.getScrollableDirections(this, touchX, touchY); + if (direction < 0 && (directions & WebScrollableUtils.DIRECTION_UP) != 0) { + return true; + } + if (direction > 0 && (directions & WebScrollableUtils.DIRECTION_DOWN) != 0) { + return true; + } + return false; + } + + @Override + public int computeHorizontalScrollRange() { + return super.computeHorizontalScrollRange(); + } + + @Override + public int computeHorizontalScrollOffset() { + return super.computeHorizontalScrollOffset(); + } + + @Override + public int computeHorizontalScrollExtent() { + return super.computeHorizontalScrollExtent(); + } + + @Override + public int computeVerticalScrollRange() { + return super.computeVerticalScrollRange(); + } + + @Override + public int computeVerticalScrollOffset() { + return super.computeVerticalScrollOffset(); + } + + @Override + public int computeVerticalScrollExtent() { + return super.computeVerticalScrollExtent(); + } + + public interface OnScrollChangeListener { + void onScrollChange(@NonNull ResuableWebView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/widget/WebContainer.java b/app/src/main/java/per/goweii/wanandroid/utils/web/view/WebContainer.java similarity index 58% rename from app/src/main/java/per/goweii/wanandroid/widget/WebContainer.java rename to app/src/main/java/per/goweii/wanandroid/utils/web/view/WebContainer.java index 9f3ae8cb..163fa399 100644 --- a/app/src/main/java/per/goweii/wanandroid/widget/WebContainer.java +++ b/app/src/main/java/per/goweii/wanandroid/utils/web/view/WebContainer.java @@ -1,14 +1,10 @@ -package per.goweii.wanandroid.widget; +package per.goweii.wanandroid.utils.web.view; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; -import android.graphics.Color; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; @@ -16,32 +12,38 @@ import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.view.animation.OvershootInterpolator; +import android.webkit.WebView; import android.widget.FrameLayout; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.view.ScrollingView; + +import com.donkingliang.consecutivescroller.IConsecutiveScroller; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Random; -import per.goweii.basic.utils.ColorUtils; +import per.goweii.basic.utils.ResUtils; import per.goweii.heartview.HeartView; import per.goweii.wanandroid.R; -import per.goweii.wanandroid.utils.SettingUtils; /** * @author CuiZhen * @date 2019/8/31 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class WebContainer extends FrameLayout { +public class WebContainer extends FrameLayout implements IConsecutiveScroller { private final float mTouchSlop; private final long mTapTimeout; private final long mDoubleTapTimeout; - private final boolean mDarkTheme; - - private int mMaskColor = Color.TRANSPARENT; private long mDownTime = 0L; private float mDownX = 0; @@ -63,6 +65,8 @@ public void run() { } }; + private final Map methodCache = new HashMap<>(); + public WebContainer(@NonNull Context context) { this(context, null); } @@ -73,14 +77,11 @@ public WebContainer(@NonNull Context context, @Nullable AttributeSet attrs) { public WebContainer(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - mTouchSlop = ViewConfiguration.getTouchSlop(); + setBackgroundColor(0); + ViewConfiguration viewConfiguration = ViewConfiguration.get(context); + mTouchSlop = viewConfiguration.getScaledTouchSlop(); mTapTimeout = ViewConfiguration.getTapTimeout(); mDoubleTapTimeout = ViewConfiguration.getDoubleTapTimeout(); - - mDarkTheme = SettingUtils.getInstance().isDarkTheme(); - if (mDarkTheme) { - mMaskColor = ColorUtils.alphaColor(ContextCompat.getColor(getContext(), R.color.background), 0.4F); - } } public void setOnDoubleClickListener(OnDoubleClickListener onDoubleClickListener) { @@ -94,8 +95,8 @@ public void setOnTouchDownListener(OnTouchDownListener onTouchDownListener) { @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); - mDownX = getWidth() / 2; - mDownY = getHeight() / 2; + mDownX = getWidth() / 2F; + mDownY = getHeight() / 2F; } @Override @@ -142,9 +143,6 @@ public boolean dispatchTouchEvent(MotionEvent event) { @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); - if (mDarkTheme) { - canvas.drawColor(mMaskColor); - } } @Override @@ -257,14 +255,182 @@ private View createHeartView() { HeartView heartView = new HeartView(getContext()); heartView.setCenter(-0.5f, -0.5f); heartView.setRadiusPercent(0.6F); - heartView.setColor(ContextCompat.getColor(getContext(), R.color.heart_center)); - heartView.setEdgeColor(ContextCompat.getColor(getContext(), R.color.accent)); + heartView.setColor(ResUtils.getThemeColor(getContext(), R.attr.colorHeartCenter)); + heartView.setEdgeColor(ResUtils.getThemeColor(getContext(), R.attr.colorHeartOuter)); heartView.setStrokeWidthDp(0); int size = (int) (getWidth() * 0.27F); heartView.setLayoutParams(new LayoutParams(size, size)); return heartView; } + @Override + public int computeHorizontalScrollRange() { + View view = getCurrentWebView(); + if (view != null) { + if (view instanceof ScrollingView) { + return ((ScrollingView) view).computeHorizontalScrollRange(); + } else { + Integer i = invokeViewMethod(view, "computeHorizontalScrollRange"); + if (i != null) { + return i; + } + } + } + return super.computeHorizontalScrollRange(); + } + + @Override + public int computeHorizontalScrollOffset() { + View view = getCurrentWebView(); + if (view != null) { + if (view instanceof ScrollingView) { + return ((ScrollingView) view).computeHorizontalScrollOffset(); + } else { + Integer i = invokeViewMethod(view, "computeHorizontalScrollOffset"); + if (i != null) { + return i; + } + } + } + return super.computeHorizontalScrollOffset(); + } + + @Override + public int computeHorizontalScrollExtent() { + View view = getCurrentWebView(); + if (view != null) { + if (view instanceof ScrollingView) { + return ((ScrollingView) view).computeHorizontalScrollExtent(); + } else { + Integer i = invokeViewMethod(view, "computeHorizontalScrollExtent"); + if (i != null) { + return i; + } + } + } + return super.computeHorizontalScrollExtent(); + } + + @Override + public int computeVerticalScrollRange() { + View view = getCurrentWebView(); + if (view != null) { + if (view instanceof ScrollingView) { + return ((ScrollingView) view).computeVerticalScrollRange(); + } else { + Integer i = invokeViewMethod(view, "computeVerticalScrollRange"); + if (i != null) { + return i; + } + } + } + return super.computeVerticalScrollRange(); + } + + @Override + public int computeVerticalScrollOffset() { + View view = getCurrentWebView(); + if (view != null) { + if (view instanceof ScrollingView) { + return ((ScrollingView) view).computeVerticalScrollOffset(); + } else { + Integer i = invokeViewMethod(view, "computeVerticalScrollOffset"); + if (i != null) { + return i; + } + } + } + return super.computeVerticalScrollOffset(); + } + + @Override + public int computeVerticalScrollExtent() { + View view = getCurrentWebView(); + if (view != null) { + if (view instanceof ScrollingView) { + return ((ScrollingView) view).computeVerticalScrollExtent(); + } else { + Integer i = invokeViewMethod(view, "computeVerticalScrollExtent"); + if (i != null) { + return i; + } + } + } + return super.computeVerticalScrollExtent(); + } + + @Override + public boolean canScrollHorizontally(int direction) { + View view = getCurrentWebView(); + if (view != null) { + return view.canScrollHorizontally(direction); + } + return super.canScrollHorizontally(direction); + } + + @Override + public boolean canScrollVertically(int direction) { + View view = getCurrentWebView(); + if (view != null) { + return view.canScrollVertically(direction); + } + return super.canScrollVertically(direction); + } + + @Override + public View getCurrentScrollerView() { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof WebView) { + return child; + } + } + return null; + } + + @Override + public List getScrolledViews() { + View view = getCurrentScrollerView(); + if (view == null) { + return Collections.emptyList(); + } + List views = new ArrayList<>(); + views.add(view); + return views; + } + + public View getCurrentWebView() { + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof WebView) { + return child; + } else if (child instanceof ResuableWebView) { + return child; + } + } + return null; + } + + @Nullable + private Integer invokeViewMethod(View view, String methodName) { + try { + Method method = methodCache.get(methodName); + if (method == null) { + method = View.class.getDeclaredMethod(methodName); + method.setAccessible(true); + methodCache.put(methodName, method); + } + Object o = method.invoke(view); + if (o != null) { + return (int) o; + } + } catch (Throwable e) { + //noinspection CallToPrintStackTrace + e.printStackTrace(); + } + return null; + } + public interface OnDoubleClickListener { void onDoubleClick(float x, float y); } diff --git a/app/src/main/java/per/goweii/wanandroid/widget/AccountInputView.java b/app/src/main/java/per/goweii/wanandroid/widget/AccountInputView.java index d21431a1..52b78b51 100644 --- a/app/src/main/java/per/goweii/wanandroid/widget/AccountInputView.java +++ b/app/src/main/java/per/goweii/wanandroid/widget/AccountInputView.java @@ -7,13 +7,12 @@ import android.view.View; import android.widget.ImageView; +import per.goweii.basic.utils.ResUtils; import per.goweii.wanandroid.R; /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class AccountInputView extends InputView { @@ -36,7 +35,8 @@ public AccountInputView(Context context, AttributeSet attrs, int defStyleAttr) { @Override protected void initViews(AttributeSet attrs) { super.initViews(attrs); - getEditText().setHint("请输入用户名"); + getEditText().setHint(R.string.please_enter_your_username); + changeFocusMode(false); } @Override @@ -49,11 +49,12 @@ protected ImageView[] getLeftIcons() { @Override protected ImageView[] getRightIcons() { - int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getContext().getResources().getDisplayMetrics()); + int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getContext().getResources().getDisplayMetrics()); mIvDeleteIcon = new ImageView(getContext()); mIvDeleteIcon.setVisibility(INVISIBLE); mIvDeleteIcon.setPadding(padding, padding, padding, padding); mIvDeleteIcon.setScaleType(ImageView.ScaleType.FIT_CENTER); + mIvDeleteIcon.setColorFilter(ResUtils.getThemeColor(getContext(), R.attr.colorIconThird)); mIvDeleteIcon.setImageResource(R.drawable.ic_delete); mIvDeleteIcon.setOnClickListener(new OnClickListener() { @Override @@ -67,7 +68,11 @@ public void onClick(View v) { @Override public void onFocusChange(View v, boolean hasFocus) { super.onFocusChange(v, hasFocus); - if (hasFocus) { + changeFocusMode(hasFocus); + } + + private void changeFocusMode(boolean focus) { + if (focus) { if (isEmpty()) { mIvDeleteIcon.setVisibility(INVISIBLE); } else { diff --git a/app/src/main/java/per/goweii/wanandroid/widget/BravhLoadMoreView.java b/app/src/main/java/per/goweii/wanandroid/widget/BravhLoadMoreView.java new file mode 100644 index 00000000..40b4f464 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/BravhLoadMoreView.java @@ -0,0 +1,28 @@ +package per.goweii.wanandroid.widget; + +import com.chad.library.adapter.base.loadmore.LoadMoreView; + +import per.goweii.wanandroid.R; + +public final class BravhLoadMoreView extends LoadMoreView { + + @Override + public int getLayoutId() { + return R.layout.layout_brvah_load_more; + } + + @Override + protected int getLoadingViewId() { + return R.id.load_more_loading_view; + } + + @Override + protected int getLoadFailViewId() { + return R.id.load_more_load_fail_view; + } + + @Override + protected int getLoadEndViewId() { + return R.id.load_more_load_end_view; + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/widget/CollectView.java b/app/src/main/java/per/goweii/wanandroid/widget/CollectView.java index de57417f..00760e97 100644 --- a/app/src/main/java/per/goweii/wanandroid/widget/CollectView.java +++ b/app/src/main/java/per/goweii/wanandroid/widget/CollectView.java @@ -3,7 +3,6 @@ import android.content.Context; import android.content.res.TypedArray; import android.util.AttributeSet; -import android.view.MotionEvent; import android.view.View; import per.goweii.heartview.HeartView; @@ -14,11 +13,9 @@ /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ -public class CollectView extends RevealLayout implements View.OnTouchListener { +public class CollectView extends RevealLayout { private OnClickListener mOnClickListener = null; private int mUncheckedColor; @@ -43,11 +40,18 @@ protected void initAttr(AttributeSet attrs) { typedArray.recycle(); setCheckWithExpand(true); setUncheckWithExpand(false); - setCheckedLayoutId(R.layout.layout_collect_view_checked); - setUncheckedLayoutId(R.layout.layout_collect_view_unchecked); setAnimDuration(500); setAllowRevert(true); - setOnTouchListener(this); + } + + @Override + protected int getCheckedLayoutId() { + return R.layout.layout_collect_view_checked; + } + + @Override + protected int getUncheckedLayoutId() { + return R.layout.layout_collect_view_unchecked; } @Override @@ -63,24 +67,20 @@ protected View createUncheckedView() { return view; } - @Override - public boolean onTouch(View v, MotionEvent event) { - switch (event.getAction()) { - default: - break; - case MotionEvent.ACTION_UP: + public void setOnClickListener(OnClickListener onClickListener) { + mOnClickListener = onClickListener; + setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { if (UserUtils.getInstance().doIfLogin(v.getContext())) { if (mOnClickListener != null) { - mOnClickListener.onClick(this); + mOnClickListener.onClick(CollectView.this); } + } else { + setChecked(false); } - break; - } - return !UserUtils.getInstance().isLogin(); - } - - public void setOnClickListener(OnClickListener onClickListener) { - mOnClickListener = onClickListener; + } + }); } public interface OnClickListener { diff --git a/app/src/main/java/per/goweii/wanandroid/widget/ColorCircleBackgroundView.java b/app/src/main/java/per/goweii/wanandroid/widget/ColorCircleBackgroundView.java new file mode 100644 index 00000000..0d9d04c3 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/ColorCircleBackgroundView.java @@ -0,0 +1,45 @@ +package per.goweii.wanandroid.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.Canvas; +import android.graphics.Color; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.Nullable; + +import per.goweii.wanandroid.R; + +public class ColorCircleBackgroundView extends View { + private int color1 = Color.TRANSPARENT; + private int color2 = Color.TRANSPARENT; + private int color3 = Color.TRANSPARENT; + private int color4 = Color.TRANSPARENT; + private int color5 = Color.TRANSPARENT; + + public ColorCircleBackgroundView(Context context) { + this(context, null); + } + + public ColorCircleBackgroundView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public ColorCircleBackgroundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ColorCircleBackgroundView); + color1 = typedArray.getColor(R.styleable.ColorCircleBackgroundView_ccbv_color1, color1); + color2 = typedArray.getColor(R.styleable.ColorCircleBackgroundView_ccbv_color2, color2); + color3 = typedArray.getColor(R.styleable.ColorCircleBackgroundView_ccbv_color3, color3); + color4 = typedArray.getColor(R.styleable.ColorCircleBackgroundView_ccbv_color4, color4); + color5 = typedArray.getColor(R.styleable.ColorCircleBackgroundView_ccbv_color5, color5); + typedArray.recycle(); + } + + @Override + public void draw(Canvas canvas) { + super.draw(canvas); + + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/widget/EmailInputView.java b/app/src/main/java/per/goweii/wanandroid/widget/EmailInputView.java new file mode 100644 index 00000000..6305dc90 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/EmailInputView.java @@ -0,0 +1,97 @@ +package per.goweii.wanandroid.widget; + +import android.content.Context; +import android.text.Editable; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.view.View; +import android.widget.ImageView; + +import per.goweii.basic.utils.ResUtils; +import per.goweii.wanandroid.R; + +/** + * @author CuiZhen + * @date 2019/5/15 + * GitHub: https://github.com/goweii + */ +public class EmailInputView extends InputView { + + private ImageView mIvEmailIcon; + private ImageView mIvDeleteIcon; + + public EmailInputView(Context context) { + super(context); + } + + public EmailInputView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public EmailInputView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void initViews(AttributeSet attrs) { + super.initViews(attrs); + getEditText().setHint(R.string.please_enter_your_email_address); + changeFocusMode(false); + } + + @Override + protected ImageView[] getLeftIcons() { + mIvEmailIcon = new ImageView(getContext()); + mIvEmailIcon.setScaleType(ImageView.ScaleType.FIT_CENTER); + mIvEmailIcon.setImageResource(R.drawable.ic_email); + return new ImageView[]{mIvEmailIcon}; + } + + @Override + protected ImageView[] getRightIcons() { + int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getContext().getResources().getDisplayMetrics()); + mIvDeleteIcon = new ImageView(getContext()); + mIvDeleteIcon.setVisibility(INVISIBLE); + mIvDeleteIcon.setPadding(padding, padding, padding, padding); + mIvDeleteIcon.setColorFilter(ResUtils.getThemeColor(getContext(), R.attr.colorIconThird)); + mIvDeleteIcon.setScaleType(ImageView.ScaleType.FIT_CENTER); + mIvDeleteIcon.setImageResource(R.drawable.ic_delete); + mIvDeleteIcon.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + getEditText().setText(""); + } + }); + return new ImageView[]{mIvDeleteIcon}; + } + + @Override + public void onFocusChange(View v, boolean hasFocus) { + super.onFocusChange(v, hasFocus); + changeFocusMode(hasFocus); + } + + private void changeFocusMode(boolean focus) { + if (focus) { + if (isEmpty()) { + mIvDeleteIcon.setVisibility(INVISIBLE); + } else { + mIvDeleteIcon.setVisibility(VISIBLE); + } + mIvEmailIcon.setColorFilter(mViewColorFocus); + } else { + mIvDeleteIcon.setVisibility(INVISIBLE); + mIvEmailIcon.setColorFilter(mViewColorNormal); + } + } + + @Override + public void afterTextChanged(Editable s) { + super.afterTextChanged(s); + if (isEmpty()) { + mIvDeleteIcon.setVisibility(INVISIBLE); + } else { + mIvDeleteIcon.setVisibility(VISIBLE); + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/widget/ImagePreviewContainer.kt b/app/src/main/java/per/goweii/wanandroid/widget/ImagePreviewContainer.kt new file mode 100644 index 00000000..f1562fd9 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/ImagePreviewContainer.kt @@ -0,0 +1,60 @@ +package per.goweii.wanandroid.widget + +import android.content.Context +import android.util.AttributeSet +import android.widget.FrameLayout +import androidx.core.view.ScrollingView + +/** + * @author CuiZhen + * @date 2020/3/7 + */ +class ImagePreviewContainer : FrameLayout, ScrollingView { + + private var imagePreviewView: ImagePreviewView? = null + + constructor(context: Context) : super(context) + constructor(context: Context, attr: AttributeSet?) : super(context, attr) + constructor(context: Context, attr: AttributeSet?, defStyle: Int) : super(context, attr, defStyle) + + override fun onFinishInflate() { + super.onFinishInflate() + for (i in 0 until childCount) { + val v = getChildAt(i) + if (v is ImagePreviewView) { + imagePreviewView = v + break + } + } + } + + private fun isScaled() = imagePreviewView?.isScaled() ?: false + + override fun computeHorizontalScrollRange(): Int = if (isScaled()) width * 3 else 0 + + override fun computeHorizontalScrollOffset(): Int = if (isScaled()) width * 1 else 0 + + override fun computeHorizontalScrollExtent(): Int = if (isScaled()) width * 1 else 0 + + override fun computeVerticalScrollRange(): Int = if (isScaled()) height * 3 else 0 + + override fun computeVerticalScrollOffset(): Int = if (isScaled()) height * 1 else 0 + + override fun computeVerticalScrollExtent(): Int = if (isScaled()) height * 1 else 0 + + override fun canScrollHorizontally(direction: Int): Boolean { + return isScaled() + } + + override fun canScrollVertically(direction: Int): Boolean { + return isScaled() + } + + interface OnImagePreviewListener { + fun onTap() + fun onTouching1() + fun onTouching2() + fun onTouchingUp() + fun onLongClick() + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/widget/ImagePreviewView.kt b/app/src/main/java/per/goweii/wanandroid/widget/ImagePreviewView.kt new file mode 100644 index 00000000..ad539052 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/ImagePreviewView.kt @@ -0,0 +1,75 @@ +package per.goweii.wanandroid.widget + +import android.content.Context +import android.util.AttributeSet +import android.view.MotionEvent +import androidx.core.view.ScrollingView +import com.github.chrisbanes.photoview.PhotoView + +/** + * @author CuiZhen + * @date 2020/3/7 + */ +class ImagePreviewView : PhotoView, ScrollingView { + constructor(context: Context) : super(context) + constructor(context: Context, attr: AttributeSet?) : super(context, attr) + constructor(context: Context, attr: AttributeSet?, defStyle: Int) : super(context, attr, defStyle) + + init { + setOnViewTapListener { view, x, y -> + onImagePreviewListener?.onTap() + } + setOnLongClickListener { + onImagePreviewListener?.onLongClick() + return@setOnLongClickListener true + } + } + + var onImagePreviewListener: OnImagePreviewListener? = null + + override fun dispatchTouchEvent(event: MotionEvent): Boolean { + when (event.action) { + MotionEvent.ACTION_DOWN -> { + if (event.pointerCount > 1) { + onImagePreviewListener?.onTouching2() + } else { + onImagePreviewListener?.onTouching1() + } + } + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + onImagePreviewListener?.onTouchingUp() + } + } + return super.dispatchTouchEvent(event) + } + + fun isScaled() = scale > 1F + + override fun computeHorizontalScrollRange(): Int = if (isScaled()) width * 3 else 0 + + override fun computeHorizontalScrollOffset(): Int = if (isScaled()) width * 1 else 0 + + override fun computeHorizontalScrollExtent(): Int = if (isScaled()) width * 1 else 0 + + override fun computeVerticalScrollRange(): Int = if (isScaled()) height * 3 else 0 + + override fun computeVerticalScrollOffset(): Int = if (isScaled()) height * 1 else 0 + + override fun computeVerticalScrollExtent(): Int = if (isScaled()) height * 1 else 0 + + override fun canScrollHorizontally(direction: Int): Boolean { + return isScaled() + } + + override fun canScrollVertically(direction: Int): Boolean { + return isScaled() + } + + interface OnImagePreviewListener { + fun onTap() + fun onTouching1() + fun onTouching2() + fun onTouchingUp() + fun onLongClick() + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/widget/InputView.java b/app/src/main/java/per/goweii/wanandroid/widget/InputView.java index 7bc183b6..05b63e2d 100644 --- a/app/src/main/java/per/goweii/wanandroid/widget/InputView.java +++ b/app/src/main/java/per/goweii/wanandroid/widget/InputView.java @@ -2,7 +2,6 @@ import android.content.Context; import android.graphics.Color; -import android.support.v4.content.ContextCompat; import android.text.InputType; import android.text.TextWatcher; import android.util.AttributeSet; @@ -14,13 +13,12 @@ import android.widget.FrameLayout; import android.widget.ImageView; +import per.goweii.basic.utils.ResUtils; import per.goweii.wanandroid.R; /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class InputView extends FrameLayout implements View.OnFocusChangeListener, TextWatcher, SubmitView.EditTextWrapper { @@ -64,8 +62,8 @@ public boolean isEmpty() { } protected void initViews(AttributeSet attrs) { - int icIconSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 22, getContext().getResources().getDisplayMetrics()); - int icIconMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getContext().getResources().getDisplayMetrics()); + int icIconSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 18, getContext().getResources().getDisplayMetrics()); + int icIconMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getContext().getResources().getDisplayMetrics()); ImageView[] ivIconLefts = getLeftIcons(); int ivIconLeftCount = ivIconLefts != null ? ivIconLefts.length : 0; for (int i = 0; i < ivIconLeftCount; i++) { @@ -92,8 +90,8 @@ protected void initViews(AttributeSet attrs) { etParams.rightMargin = icIconSize * ivIconRightCount + icIconMargin * (ivIconRightCount - 1) + etMargin; mEditText.setLayoutParams(etParams); mEditText.setBackgroundColor(Color.TRANSPARENT); - mEditText.setTextColor(ContextCompat.getColor(getContext(), R.color.text_surface)); - mEditText.setHintTextColor(ContextCompat.getColor(getContext(), R.color.text_third)); + mEditText.setTextColor(ResUtils.getThemeColor(getContext(), R.attr.colorTextSurface)); + mEditText.setHintTextColor(ResUtils.getThemeColor(getContext(), R.attr.colorTextThird)); mEditText.setTextSize(TypedValue.COMPLEX_UNIT_PX, getContext().getResources().getDimension(R.dimen.text_content)); mEditText.setSingleLine(); mEditText.setInputType(InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS); @@ -101,9 +99,9 @@ protected void initViews(AttributeSet attrs) { mEditText.addTextChangedListener(this); addView(mEditText); - mViewColorNormal = ContextCompat.getColor(getContext(), R.color.third); + mViewColorNormal = ResUtils.getThemeColor(getContext(), R.attr.colorIconThird); mViewHeightNormal = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getContext().getResources().getDisplayMetrics()); - mViewColorFocus = ContextCompat.getColor(getContext(), R.color.main); + mViewColorFocus = ResUtils.getThemeColor(getContext(), R.attr.colorIconMain); mViewHeightFocus = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getContext().getResources().getDisplayMetrics()); mBottomLine = new View(getContext()); diff --git a/app/src/main/java/per/goweii/wanandroid/widget/ListenableNestedScrollView.java b/app/src/main/java/per/goweii/wanandroid/widget/ListenableNestedScrollView.java new file mode 100644 index 00000000..a626a2ea --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/ListenableNestedScrollView.java @@ -0,0 +1,48 @@ +package per.goweii.wanandroid.widget; + +import android.content.Context; +import android.util.AttributeSet; + +import androidx.annotation.Nullable; +import androidx.core.widget.NestedScrollView; + +public class ListenableNestedScrollView extends NestedScrollView { + private OnScrollChanged mOnScrollChanged = null; + + public ListenableNestedScrollView(Context context) { + this(context, null); + } + + public ListenableNestedScrollView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public ListenableNestedScrollView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + if (mOnScrollChanged != null) { + float curScroll = 0F; + float maxScroll = 0F; + if (getChildCount() > 0) { + curScroll = getScrollY(); + curScroll = Math.max(0F, curScroll); + float meHeight = getHeight() - getPaddingTop() - getPaddingBottom(); + maxScroll = getChildAt(0).getHeight() - meHeight; + maxScroll = Math.max(0F, maxScroll); + } + mOnScrollChanged.onScrollChanged(curScroll, maxScroll); + } + } + + public void setOnScrollChanged(@Nullable OnScrollChanged onScrollChanged) { + mOnScrollChanged = onScrollChanged; + } + + public interface OnScrollChanged { + void onScrollChanged(float curScroll, float maxScroll); + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/widget/LogoAnimView.kt b/app/src/main/java/per/goweii/wanandroid/widget/LogoAnimView.kt new file mode 100644 index 00000000..10e54dc8 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/LogoAnimView.kt @@ -0,0 +1,161 @@ +package per.goweii.wanandroid.widget + +import android.animation.Animator +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.content.Context +import android.graphics.Color +import android.util.AttributeSet +import android.view.Gravity +import android.view.animation.AccelerateInterpolator +import android.view.animation.DecelerateInterpolator +import android.widget.FrameLayout +import android.widget.ImageView +import per.goweii.wanandroid.R +import kotlin.math.min +import kotlin.random.Random + +/** + * @author CuiZhen + * @date 2020/3/14 + */ +class LogoAnimView : FrameLayout, Runnable { + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + private val logo: ImageView = ImageView(context).apply { + setImageResource(R.drawable.logo) + setColorFilter(Color.WHITE) + scaleType = ImageView.ScaleType.FIT_CENTER + } + private val eyeLeft: ImageView = ImageView(context).apply { + setImageResource(R.drawable.bg_white_circle) + scaleType = ImageView.ScaleType.FIT_CENTER + } + private val eyeRight: ImageView = ImageView(context).apply { + setImageResource(R.drawable.bg_white_circle) + scaleType = ImageView.ScaleType.FIT_CENTER + } + + init { + addViewInLayout(logo, childCount, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT).apply { + gravity = Gravity.CENTER + }) + addViewInLayout(eyeLeft, childCount, LayoutParams(0, 0).apply { + gravity = Gravity.LEFT + }) + addViewInLayout(eyeRight, childCount, LayoutParams(0, 0).apply { + gravity = Gravity.RIGHT + }) + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + val w = MeasureSpec.getSize(widthMeasureSpec) + val h = MeasureSpec.getSize(heightMeasureSpec) + val size = min(w, h) + setMeasuredDimension(size, size) + val eyeSize = (size * 0.1F).toInt() + eyeLeft.layoutParams.apply { + this as LayoutParams + width = eyeSize + height = eyeSize + leftMargin = (size * 0.23F).toInt() + topMargin = (size * 0.45F).toInt() + } + eyeRight.layoutParams.apply { + this as LayoutParams + width = eyeSize + height = eyeSize + rightMargin = (size * 0.23F).toInt() + topMargin = (size * 0.45F).toInt() + } + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + eyeAnim?.cancel() + } + + private var eyeAnim: Animator? = null + + fun close(onEnd: (() -> Unit)? = null) { + removeCallbacks(this) + AnimatorSet().apply { + playTogether( + ObjectAnimator.ofFloat(eyeLeft, "translationY", eyeLeft.translationY, eyeLeft.height.toFloat()), + ObjectAnimator.ofFloat(eyeRight, "translationY", eyeRight.translationY, eyeRight.height.toFloat()) + ) + interpolator = DecelerateInterpolator() + duration = 200 + onEnd(onEnd) + }.also { + eyeAnim?.cancel() + eyeAnim = it + }.start() + } + + fun open(onEnd: (() -> Unit)? = null) { + removeCallbacks(this) + AnimatorSet().apply { + playTogether( + ObjectAnimator.ofFloat(eyeLeft, "translationY", eyeLeft.translationY, 0F), + ObjectAnimator.ofFloat(eyeRight, "translationY", eyeRight.translationY, 0F) + ) + interpolator = AccelerateInterpolator() + duration = 200 + onEnd(onEnd) + }.also { + eyeAnim?.cancel() + eyeAnim = it + }.start() + } + + fun blink(times: Int, onEnd: (() -> Unit)? = null) { + removeCallbacks(this) + AnimatorSet().apply { + playTogether( + ObjectAnimator.ofFloat(eyeLeft, "translationY", 0F, eyeLeft.height.toFloat(), 0F).apply { + repeatCount = times + }, + ObjectAnimator.ofFloat(eyeRight, "translationY", 0F, eyeRight.height.toFloat(), 0F).apply { + repeatCount = times + } + ) + interpolator = AccelerateInterpolator() + duration = 400 + onEnd(onEnd) + }.also { + eyeAnim?.cancel() + eyeAnim = it + }.start() + } + + fun randomBlink() { + postDelayed(this, Random.nextLong(1000L) + 2000L) + } + + override fun run() { + blink(Random.nextInt(2)) { + randomBlink() + } + } + + private fun Animator.onEnd(onEnd: (() -> Unit)? = null) { + addListener(object : Animator.AnimatorListener { + override fun onAnimationRepeat(animation: Animator) { + } + + override fun onAnimationEnd(animation: Animator) { + onEnd?.invoke() + } + + override fun onAnimationCancel(animation: Animator) { + } + + override fun onAnimationStart(animation: Animator) { + } + }) + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/widget/NestedSwipeLayout.java b/app/src/main/java/per/goweii/wanandroid/widget/NestedSwipeLayout.java index bb4a9d6f..0e2a4516 100644 --- a/app/src/main/java/per/goweii/wanandroid/widget/NestedSwipeLayout.java +++ b/app/src/main/java/per/goweii/wanandroid/widget/NestedSwipeLayout.java @@ -1,16 +1,15 @@ package per.goweii.wanandroid.widget; import android.content.Context; -import android.support.v4.view.ScrollingView; import android.util.AttributeSet; +import androidx.core.view.ScrollingView; + import com.daimajia.swipe.SwipeLayout; /** * @author CuiZhen * @date 2019/9/7 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class NestedSwipeLayout extends SwipeLayout implements ScrollingView { diff --git a/app/src/main/java/per/goweii/wanandroid/widget/ParallaxStackLayout.kt b/app/src/main/java/per/goweii/wanandroid/widget/ParallaxStackLayout.kt new file mode 100644 index 00000000..51b4ec09 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/ParallaxStackLayout.kt @@ -0,0 +1,264 @@ +package per.goweii.wanandroid.widget + +import android.app.Activity +import android.content.Context +import android.content.ContextWrapper +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager +import android.util.AttributeSet +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.annotation.Size +import androidx.core.content.getSystemService +import androidx.core.content.res.use +import androidx.core.view.isVisible +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleObserver +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.OnLifecycleEvent +import per.goweii.wanandroid.R + +@Suppress("DEPRECATION") +class ParallaxStackLayout @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : FrameLayout(context, attrs, defStyleAttr) { + private val sensorManager: SensorManager? = context.getSystemService() + private val gyroscopeSensor: Sensor? = sensorManager?.getDefaultSensor(Sensor.TYPE_ORIENTATION) + private var sensorEventListener: SensorEventListener? = null + + private var lifecycleObserver: LifecycleObserver? = null + private var lifecycle: Lifecycle? = null + set(value) { + lifecycleObserver?.let { field?.removeObserver(it) } + field = value + if (value == null) { + lifecycleObserver = null + return + } + if (lifecycleObserver == null) { + lifecycleObserver = LifecycleObserverImpl() + } + value.addObserver(lifecycleObserver!!) + } + + val degrees = FloatArray(3) + + override fun onAttachedToWindow() { + super.onAttachedToWindow() + findActivity()?.let { + if (it is LifecycleOwner) { + lifecycle = it.lifecycle + } + } + registerSensorIfNeeded() + } + + override fun onDetachedFromWindow() { + super.onDetachedFromWindow() + lifecycle = null + unregisterSensorIfNeeded() + } + + override fun onViewAdded(child: View?) { + super.onViewAdded(child) + registerSensorIfNeeded() + } + + override fun onViewRemoved(child: View?) { + super.onViewRemoved(child) + unregisterSensorIfNeeded() + } + + override fun setVisibility(visibility: Int) { + super.setVisibility(visibility) + if (isVisible) { + registerSensorIfNeeded() + } else { + unregisterSensorIfNeeded() + } + } + + override fun setEnabled(enabled: Boolean) { + super.setEnabled(enabled) + if (enabled) { + registerSensorIfNeeded() + } else { + unregisterSensorIfNeeded() + } + } + + override fun generateLayoutParams(attrs: AttributeSet?): FrameLayout.LayoutParams { + return LayoutParams(context, attrs) + } + + override fun generateLayoutParams(lp: ViewGroup.LayoutParams): ViewGroup.LayoutParams { + return when (lp) { + is LayoutParams -> LayoutParams(lp) + is FrameLayout.LayoutParams -> LayoutParams(lp) + is MarginLayoutParams -> LayoutParams(lp) + else -> LayoutParams(lp) + } + } + + override fun generateDefaultLayoutParams(): FrameLayout.LayoutParams { + return LayoutParams() + } + + override fun checkLayoutParams(p: ViewGroup.LayoutParams?): Boolean { + return p is LayoutParams + } + + /** + * 左手坐标系 + */ + private fun onDegreesChanged(@Size(3) degrees: FloatArray) { + val degreeX = degrees[0] % 360F + val degreeY = degrees[1] % 360F + val degreeZ = degrees[2] % 360F + + for (i in 0 until childCount) { + val view = getChildAt(i) + val lp = view.layoutParams as LayoutParams + + if (!lp.isEffective) continue + + view.translationX = if (degreeY in (-90F)..90F) { + degreeY / 90F * lp.deviationX + } else { + view.translationX + } + view.translationY = if (degreeX in (-90F)..90F) { + degreeX / 90F * lp.deviationY + } else { + view.translationY + } + view.rotation = when { + lp.rotationZ > 0F -> degreeZ.coerceIn(-lp.rotationZ, lp.rotationZ) + lp.rotationZ < 0F -> -degreeZ.coerceIn(lp.rotationZ, -lp.rotationZ) + else -> 0F + } + view.rotationX = when { + lp.rotationX > 0F -> degreeX.coerceIn(-lp.rotationX, lp.rotationX) + lp.rotationX < 0F -> -degreeX.coerceIn(lp.rotationX, -lp.rotationX) + else -> 0F + } + view.rotationY = when { + lp.rotationY > 0F -> degreeY.coerceIn(-lp.rotationY, lp.rotationY) + lp.rotationY < 0F -> -degreeY.coerceIn(lp.rotationY, -lp.rotationY) + else -> 0F + } + } + } + + private fun registerSensorIfNeeded() { + if (isEnabled && isAttachedToWindow && isVisible && childCount > 0) { + if (sensorEventListener == null) { + sensorEventListener = SensorEventListenerImpl() + sensorManager?.registerListener( + sensorEventListener, + gyroscopeSensor, + SensorManager.SENSOR_DELAY_UI + ) + } + } + } + + private fun unregisterSensorIfNeeded() { + if (!isEnabled || !isAttachedToWindow || !isVisible || childCount == 0) { + if (sensorEventListener != null) { + sensorManager?.unregisterListener(sensorEventListener) + sensorEventListener = null + } + } + } + + private fun findActivity(): Activity? { + fun Context.activity(): Activity? { + var context = this + while (true) { + if (context is Activity) { + return context + } + if (context is ContextWrapper) { + val baseContext = context.baseContext + if (baseContext !== context) { + context = baseContext + continue + } + } + return null + } + } + return context?.activity() + ?: rootView?.context?.activity() + } + + private inner class SensorEventListenerImpl : SensorEventListener { + override fun onSensorChanged(event: SensorEvent) { + degrees[0] = event.values[1] + degrees[1] = -event.values[2] + degrees[2] = event.values[0] + onDegreesChanged(degrees) + } + + override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { + } + } + + inner class LifecycleObserverImpl : LifecycleObserver { + @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) + fun onResume() { + isEnabled = true + } + + @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) + fun onPause() { + isEnabled = false + } + } + + class LayoutParams : FrameLayout.LayoutParams { + var deviationX = 0F + var deviationY = 0F + var rotationX = 0F + var rotationY = 0F + var rotationZ = 0F + + val isEffective: Boolean + get() = deviationX != 0F || deviationY != 0F || + rotationX != 0F || rotationY != 0F || rotationZ != 0F + + constructor() : this(MATCH_PARENT, MATCH_PARENT) + constructor(width: Int, height: Int) : super(width, height) + constructor(source: ViewGroup.LayoutParams) : super(source) + constructor(source: MarginLayoutParams) : super(source) + constructor(source: FrameLayout.LayoutParams) : super(source) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { + context.obtainStyledAttributes(attrs, R.styleable.ParallaxStackLayout_Layout).use { + deviationX = it.getDimension( + R.styleable.ParallaxStackLayout_Layout_layout_parallaxDeviationX, + deviationX + ) + deviationY = it.getDimension( + R.styleable.ParallaxStackLayout_Layout_layout_parallaxDeviationY, + deviationY + ) + rotationX = it.getFloat( + R.styleable.ParallaxStackLayout_Layout_layout_parallaxRotationX, + rotationX + ) + rotationY = it.getFloat( + R.styleable.ParallaxStackLayout_Layout_layout_parallaxRotationY, + rotationY + ) + rotationZ = it.getFloat( + R.styleable.ParallaxStackLayout_Layout_layout_parallaxRotationZ, + rotationZ + ) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/per/goweii/wanandroid/widget/PasswordInputView.java b/app/src/main/java/per/goweii/wanandroid/widget/PasswordInputView.java index 0c50078b..33b97da6 100644 --- a/app/src/main/java/per/goweii/wanandroid/widget/PasswordInputView.java +++ b/app/src/main/java/per/goweii/wanandroid/widget/PasswordInputView.java @@ -10,13 +10,12 @@ import android.widget.ImageView; import per.goweii.basic.utils.EditTextUtils; +import per.goweii.basic.utils.ResUtils; import per.goweii.wanandroid.R; /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class PasswordInputView extends InputView { @@ -26,6 +25,7 @@ public class PasswordInputView extends InputView { private ImageView mIcEyeIcon; private boolean isHidePwdMode = true; + private OnPwdFocusChangedListener mOnPwdFocusChangedListener = null; public PasswordInputView(Context context) { super(context); @@ -39,10 +39,15 @@ public PasswordInputView(Context context, AttributeSet attrs, int defStyleAttr) super(context, attrs, defStyleAttr); } + public void setOnPwdFocusChangedListener(OnPwdFocusChangedListener onPwdFocusChangedListener) { + mOnPwdFocusChangedListener = onPwdFocusChangedListener; + } + @Override protected void initViews(AttributeSet attrs) { super.initViews(attrs); - getEditText().setHint("请输入密码"); + getEditText().setHint(R.string.please_enter_your_password); + changeFocusMode(false); changePwdHideMode(true); } @@ -56,11 +61,12 @@ protected ImageView[] getLeftIcons() { @Override protected ImageView[] getRightIcons() { - int paddingDelete = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getContext().getResources().getDisplayMetrics()); + int paddingDelete = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getContext().getResources().getDisplayMetrics()); mIvDeleteIcon = new ImageView(getContext()); mIvDeleteIcon.setVisibility(INVISIBLE); mIvDeleteIcon.setPadding(paddingDelete, paddingDelete, paddingDelete, paddingDelete); mIvDeleteIcon.setScaleType(ImageView.ScaleType.FIT_CENTER); + mIvDeleteIcon.setColorFilter(ResUtils.getThemeColor(getContext(), R.attr.colorIconThird)); mIvDeleteIcon.setImageResource(R.drawable.ic_delete); mIvDeleteIcon.setOnClickListener(new OnClickListener() { @Override @@ -68,11 +74,12 @@ public void onClick(View v) { getEditText().setText(""); } }); - int paddingEye = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2, getContext().getResources().getDisplayMetrics()); + int paddingEye = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0, getContext().getResources().getDisplayMetrics()); mIcEyeIcon = new ImageView(getContext()); mIcEyeIcon.setVisibility(INVISIBLE); mIcEyeIcon.setPadding(paddingEye, paddingEye, paddingEye, paddingEye); mIcEyeIcon.setScaleType(ImageView.ScaleType.FIT_CENTER); + mIcEyeIcon.setColorFilter(ResUtils.getThemeColor(getContext(), R.attr.colorIconThird)); mIcEyeIcon.setImageResource(R.drawable.ic_eye_normal); mIcEyeIcon.setOnClickListener(new OnClickListener() { @Override @@ -86,7 +93,14 @@ public void onClick(View v) { @Override public void onFocusChange(View v, boolean hasFocus) { super.onFocusChange(v, hasFocus); - if (hasFocus) { + changeFocusMode(hasFocus); + if (mOnPwdFocusChangedListener != null) { + mOnPwdFocusChangedListener.onFocusChanged(hasFocus); + } + } + + private void changeFocusMode(boolean focus) { + if (focus) { if (isEmpty()) { mIvDeleteIcon.setVisibility(INVISIBLE); } else { @@ -124,4 +138,8 @@ public void afterTextChanged(Editable s) { mIvDeleteIcon.setVisibility(VISIBLE); } } + + public interface OnPwdFocusChangedListener { + void onFocusChanged(boolean focus); + } } diff --git a/app/src/main/java/per/goweii/wanandroid/widget/SubmitView.java b/app/src/main/java/per/goweii/wanandroid/widget/SubmitView.java index 555c9d4f..a0113e08 100644 --- a/app/src/main/java/per/goweii/wanandroid/widget/SubmitView.java +++ b/app/src/main/java/per/goweii/wanandroid/widget/SubmitView.java @@ -2,7 +2,6 @@ import android.content.Context; import android.content.res.TypedArray; -import android.support.v7.widget.AppCompatTextView; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; @@ -10,14 +9,14 @@ import android.view.View; import android.widget.EditText; +import androidx.appcompat.widget.AppCompatTextView; + import per.goweii.basic.utils.EditTextUtils; import per.goweii.wanandroid.R; /** * @author CuiZhen * @date 2019/5/16 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class SubmitView extends AppCompatTextView implements TextWatcher { diff --git a/app/src/main/java/per/goweii/wanandroid/widget/X5WebView.java b/app/src/main/java/per/goweii/wanandroid/widget/X5WebView.java deleted file mode 100644 index 6896ddc3..00000000 --- a/app/src/main/java/per/goweii/wanandroid/widget/X5WebView.java +++ /dev/null @@ -1,49 +0,0 @@ -package per.goweii.wanandroid.widget; - -import android.content.Context; -import android.support.v4.view.ScrollingView; -import android.util.AttributeSet; - -import com.tencent.smtt.sdk.WebView; - -import java.util.Map; - -/** - * @author CuiZhen - * @date 2019/11/30 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public class X5WebView extends WebView implements ScrollingView { - - public X5WebView(Context context) { - super(context); - } - - public X5WebView(Context context, AttributeSet attributeSet) { - super(context, attributeSet); - } - - public X5WebView(Context context, AttributeSet attributeSet, int i) { - super(context, attributeSet, i); - } - - public X5WebView(Context context, AttributeSet attributeSet, int i, boolean b) { - super(context, attributeSet, i, b); - } - - public X5WebView(Context context, AttributeSet attributeSet, int i, Map map, boolean b) { - super(context, attributeSet, i, map, b); - } - - @Override - public boolean canScrollHorizontally(int direction) { - return getView().canScrollHorizontally(direction); - } - - @Override - public boolean canScrollVertically(int direction) { - return getView().canScrollVertically(direction); - } -} diff --git a/app/src/main/java/per/goweii/wanandroid/widget/bottomdrawer/BottomDrawerLayout.kt b/app/src/main/java/per/goweii/wanandroid/widget/bottomdrawer/BottomDrawerLayout.kt new file mode 100644 index 00000000..adac8b55 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/bottomdrawer/BottomDrawerLayout.kt @@ -0,0 +1,717 @@ +package per.goweii.wanandroid.widget.bottomdrawer + +import android.annotation.SuppressLint +import android.content.Context +import android.util.AttributeSet +import android.view.KeyEvent +import android.view.MotionEvent +import android.view.View +import android.view.ViewConfiguration +import android.view.animation.DecelerateInterpolator +import android.widget.FrameLayout +import android.widget.Scroller +import androidx.core.view.* +import per.goweii.statusbarcompat.StatusBarCompat +import per.goweii.wanandroid.R +import java.lang.ref.WeakReference +import kotlin.math.abs + +class BottomDrawerLayout : FrameLayout, NestedScrollingParent2 { + + private val _dismissDuration = 300 + private val _dismissVelocity = 1000F + private val _dismissFraction = 0.1F + + private val mDragHelper: androidx.customview.widget.ViewDragHelper = androidx.customview.widget.ViewDragHelper.create(this, DragCallback()) + private val mNestedHelper: NestedScrollingParentHelper = NestedScrollingParentHelper(this) + private val mScroller: Scroller = Scroller(context, DecelerateInterpolator()) + + private var mInnerScrollViews: List? = null + private var mHandleTouchEvent = false + private var mDownX: Float = 0.toFloat() + private var mDownY: Float = 0.toFloat() + private var mLeft: Int = 0 + private var mRight: Int = 0 + private var mTop: Int = 0 + private var mBottom: Int = 0 + + private var usingNested = false + + private var mDragFraction = 0f + + private var onDragStart: (() -> Unit)? = null + private var onDragging: ((f: Float) -> Unit)? = null + private var onDragEnd: (() -> Unit)? = null + private var onOpened: (() -> Unit)? = null + private var onClosed: (() -> Unit)? = null + + private var enable: Boolean = false + private var draggable: Boolean = true + + private var moveDownY = 0F + private var moveUpOrDown: Boolean? = null + + private var open = false + private var openTopMarginStatusBarHeight = false + private var closeHeight: Int = 0 + private var openTopMargin: Int = 0 + + private val handleViews = arrayListOf>() + + constructor(context: Context) : this(context, null) + constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { + val typedArray = context.obtainStyledAttributes(attrs, R.styleable.BottomDrawerLayout) + enable = typedArray.getBoolean(R.styleable.BottomDrawerLayout_bdl_enable, enable) + open = typedArray.getBoolean(R.styleable.BottomDrawerLayout_bdl_open, open) + closeHeight = typedArray.getDimension(R.styleable.BottomDrawerLayout_bdl_closeHeight, closeHeight.toFloat()).toInt() + openTopMarginStatusBarHeight = typedArray.getBoolean(R.styleable.BottomDrawerLayout_bdl_openTopMarginStatusBarHeight, openTopMarginStatusBarHeight) + openTopMargin = if (openTopMarginStatusBarHeight) { + StatusBarCompat.getHeight(context) + } else { + typedArray.getDimension(R.styleable.BottomDrawerLayout_bdl_openTopMargin, openTopMargin.toFloat()).toInt() + } + mDragFraction = if (open) 0F else 1F + typedArray.recycle() + } + + fun addHandleView(vararg views: View) { + views.forEach { handleViews.add(WeakReference(it)) } + } + + fun removeHandleView(vararg views: View) { + val removeViews = views.toList() + val iterator = handleViews.iterator() + while (iterator.hasNext()) { + val next = iterator.next() + if (next.get() == null || removeViews.contains(next.get())) { + next.clear() + iterator.remove() + } + } + } + + fun clearHandleViews() { + handleViews.forEach { it.clear() } + handleViews.clear() + } + + fun cleanHandleViewIfRecycled() { + val iterator = handleViews.iterator() + while (iterator.hasNext()) { + val next = iterator.next() + if (next.get() == null) { + next.clear() + iterator.remove() + } + } + } + + fun onDragStart(listener: () -> Unit) { + onDragStart = listener + } + + fun onDragging(listener: (f: Float) -> Unit) { + onDragging = listener + } + + fun onDragEnd(listener: () -> Unit) { + onDragEnd = listener + } + + fun onOpened(listener: () -> Unit) { + onOpened = listener + } + + fun onClosed(listener: () -> Unit) { + onClosed = listener + } + + fun handleKeyEvent(keyCode: Int, event: KeyEvent): Boolean { + if (keyCode != KeyEvent.KEYCODE_BACK) { + return false + } + if (!isClosed()) { + close() + return true + } + return false + } + + fun setCloseHeight(height: Int) { + this.closeHeight = height + requestLayout() + } + + fun getCloseHeight(): Int { + return this.closeHeight + } + + fun setOpenTopMargin(margin: Int) { + this.openTopMargin = margin + requestLayout() + } + + fun getOpenTopMargin(): Int { + return this.openTopMargin + } + + fun setDraggable(draggable: Boolean) { + this.draggable = draggable + } + + fun isDraggable(): Boolean { + return this.draggable + } + + fun setEnable(enable: Boolean) { + this.enable = enable + } + + fun isOpen(): Boolean { + return open + } + + fun isOpened(): Boolean { + return getDragY() == getMinDragY() + } + + fun isClosed(): Boolean { + return getDragY() == getMaxDragY() + } + + fun toggle(duration: Int = 300) { + if (!enable) return + if (isOpened()) { + close(duration) + } else if (isClosed()) { + open(duration) + } + } + + fun open(duration: Int = 300) { + if (!enable) return + open = true + if (duration > 0) { + usingNested = true + mScroller.abortAnimation() + mScroller.startScroll(-getDragX().toInt(), -getDragY().toInt(), + -getDragX().toInt(), -(getMinDragY() - getDragY()).toInt(), duration) + invalidate() + } else { + initStateImmediately(true) + } + } + + fun close(duration: Int = 300) { + if (!enable) return + open = false + if (duration > 0) { + usingNested = true + mScroller.abortAnimation() + mScroller.startScroll(-getDragX().toInt(), -getDragY().toInt(), + -getDragX().toInt(), -(getMaxDragY() - getDragY()).toInt(), duration) + invalidate() + } else { + initStateImmediately(false) + } + } + + private fun initStateImmediately(openOrClose: Boolean) { + open = openOrClose + if (open) { + setDragY(getMinDragY()) + } else { + setDragY(getMaxDragY()) + } + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + dragView.isClickable = true + if (mInnerScrollViews.isNullOrEmpty()) { + mInnerScrollViews = DragCompat.findAllScrollViews(dragView) + } + val ws = MeasureSpec.getSize(widthMeasureSpec) + val hs = MeasureSpec.getSize(heightMeasureSpec) + super.onMeasure(widthMeasureSpec, heightMeasureSpec) + dragView.measure( + MeasureSpec.makeMeasureSpec(ws, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(hs - openTopMargin, MeasureSpec.EXACTLY) + ) + } + + val dragView: View + get() { + check(childCount > 0) + return getChildAt(childCount - 1) + } + + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { + super.onLayout(changed, left, top, right, bottom) + mLeft = dragView.left + mRight = dragView.right + mTop = dragView.top + mBottom = dragView.bottom + initStateImmediately(open) + when { + mDragFraction <= 0F -> initStateImmediately(true) + mDragFraction >= 1F -> initStateImmediately(false) + else -> setDragY(mDragFraction * abs(getMaxDragY() - getMinDragY()) + getMinDragY()) + } + } + + override fun dispatchTouchEvent(ev: MotionEvent): Boolean { + when (ev.action and MotionEvent.ACTION_MASK) { + MotionEvent.ACTION_DOWN -> { + moveDownY = ev.rawY + moveUpOrDown = null + } + MotionEvent.ACTION_MOVE -> { + moveUpOrDown = when { + ev.rawY - moveDownY > 0F -> false + ev.rawY - moveDownY < 0F -> true + else -> null + } + } + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + moveDownY = 0F + moveUpOrDown = null + } + } + return super.dispatchTouchEvent(ev) + } + + override fun onInterceptTouchEvent(ev: MotionEvent): Boolean { + if (!draggable) { + mHandleTouchEvent = false + return super.onInterceptTouchEvent(ev) + } + if (!enable) { + mHandleTouchEvent = false + return super.onInterceptTouchEvent(ev) + } + when (ev.action and MotionEvent.ACTION_MASK) { + MotionEvent.ACTION_DOWN -> { + if (ev.x < dragView.left || ev.x > dragView.right || ev.y < dragView.top || ev.y > dragView.bottom) { + return false + } + mDragHelper.abort() + mScroller.abortAnimation() + mDownX = ev.rawX + mDownY = ev.rawY + usingNested = false + } + } + if (usingNested) { + mHandleTouchEvent = false + return super.onInterceptTouchEvent(ev) + } + val shouldIntercept = mDragHelper.shouldInterceptTouchEvent(ev) + mHandleTouchEvent = shouldIntercept + when (ev.action and MotionEvent.ACTION_MASK) { + MotionEvent.ACTION_DOWN -> { + if (!mHandleTouchEvent) { + if (!DragCompat.canViewScrollUp(mInnerScrollViews, mDownX, mDownY, false)) { + mHandleTouchEvent = true + } + } + } + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + if (abs(ev.rawX - mDownX) < ViewConfiguration.getTouchSlop() && + abs(ev.rawY - mDownY) < ViewConfiguration.getTouchSlop()) { + judgeDragEnd() + } + } + } + return shouldIntercept + } + + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(ev: MotionEvent): Boolean { + if (!draggable) { + return super.onTouchEvent(ev) + } + if (!enable) { + return super.onTouchEvent(ev) + } + when (ev.action and MotionEvent.ACTION_MASK) { + MotionEvent.ACTION_DOWN -> { + if (ev.x < dragView.left || ev.x > dragView.right || ev.y < dragView.top || ev.y > dragView.bottom) { + return false + } + } + } + if (usingNested) { + return super.onTouchEvent(ev) + } + mDragHelper.processTouchEvent(ev) + when (ev.action and MotionEvent.ACTION_MASK) { + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + if (abs(ev.rawX - mDownX) < ViewConfiguration.getTouchSlop() && + abs(ev.rawY - mDownY) < ViewConfiguration.getTouchSlop()) { + judgeDragEnd() + } + } + } + return mHandleTouchEvent + } + + override fun computeScroll() { + if (!enable) return + if (mScroller.computeScrollOffset()) { + scrollTo(mScroller.currX, mScroller.currY) + invalidate() + } + if (mDragHelper.continueSettling(true)) { + invalidate() + } + } + + private fun getDragX(): Float { + return dragView.left.toFloat() - mLeft.toFloat() + } + + private fun getDragY(): Float { + return dragView.top.toFloat() - mTop.toFloat() + } + + private fun getMinDragY() = 0F + + private fun getMaxDragY() = (dragView.height - closeHeight).toFloat() + + private fun getMinDragX() = 0F + + private fun getMaxDragX() = (dragView.width - closeHeight).toFloat() + + private fun setDragX(dragX: Float) { + setDrag(dragX, getDragY()) + } + + private fun setDragY(dragY: Float) { + setDrag(getDragX(), dragY) + } + + private fun setDrag(dragX: Float, dragY: Float) { + val x = dragX.range(getMinDragX(), getMaxDragX()).toInt() + val y = dragY.range(getMinDragY(), getMaxDragY()).toInt() + dragView.left = mLeft + x + dragView.right = mRight + x + dragView.top = mTop + y + dragView.bottom = mBottom + y + onDragChanged() + invalidate() + } + + private fun Float.range(min: Float, max: Float): Float { + return when { + this < min -> min + this > max -> max + else -> this + } + } + + override fun scrollBy(x: Int, y: Int) { + setDrag(getDragX() - x, getDragY() - y) + } + + override fun scrollTo(x: Int, y: Int) { + setDrag(-x.toFloat(), -y.toFloat()) + } + + private fun refreshDragFraction() { + mDragFraction = abs(getDragY()) / abs(getMaxDragY() - getMinDragY()) + if (mDragFraction < 0) { + mDragFraction = 0f + } else if (mDragFraction > 1) { + mDragFraction = 1f + } + } + + private fun onDragChanged() { + refreshDragFraction() + onDragging() + if (open) { + if (mDragFraction == 0F) onDragEnd() + } else { + if (mDragFraction == 1F) onDragEnd() + } + } + + private var dargging = false + private var touching = false + + private fun onDragStart() { + dargging = true + onDragStart?.invoke() + } + + private fun onDragging() { + onDragging?.invoke(mDragFraction) + } + + private fun onDragEnd() { + dargging = false + onDragEnd?.invoke() + if (this.isOpened()) { + onOpened?.invoke() + } else if (isClosed()) { + onClosed?.invoke() + } + } + + private fun judgeDragEnd() { + refreshDragFraction() + val openOrClose = mDragFraction < 0.5F + val duration: Int = _dismissDuration + if (openOrClose) { + open(duration) + } else { + close(duration) + } + } + + override fun onStartNestedScroll(child: View, target: View, axes: Int, type: Int): Boolean { + usingNested = if (target is NestedScrollingChild) { + if (mInnerScrollViews?.contains(child) == true) { + target.canScrollVertically(-1) || target.canScrollVertically(1) + } else { + false + } + } else { + false + } + return usingNested + } + + override fun onNestedScrollAccepted(child: View, target: View, axes: Int, type: Int) { + nestedScrollStopped = false + mNestedHelper.onNestedScrollAccepted(child, target, axes, type) + if (type == ViewCompat.TYPE_TOUCH) { + mScroller.abortAnimation() + velocity = 0F + refreshDragFraction() + touching = true + onDragStart() + } + } + + private var velocity: Float = 0F + + override fun onNestedFling(target: View, velocityX: Float, velocityY: Float, consumed: Boolean): Boolean { + return if (nestedScrollStopped) { + false + } else { + super.onNestedFling(target, velocityX, velocityY, consumed) + } + } + + override fun onNestedPreFling(target: View, velocityX: Float, velocityY: Float): Boolean { + velocity = velocityY + return if (nestedScrollStopped) { + false + } else { + return super.onNestedPreFling(target, velocityX, velocityY) + } + } + + override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) { + target as ScrollingView + val scrollY: Int = -getDragY().toInt() + consumed[0] = 0 + consumed[1] = if (dy > 0) { + if (scrollY < 0) { + if (scrollY + dy > 0) { + -scrollY + } else { + dy + } + } else { + 0 + } + } else if (dy < 0) { + if (scrollY < 0) { + dy + } else { + if (target.canScrollVertically(-1)) { + 0 + } else { + if (type == ViewCompat.TYPE_NON_TOUCH) { + if (scrollY + dy > 0) { + -scrollY + } else { + 0 + } + } else { + dy + } + } + } + } else { + 0 + } + scrollBy(consumed[0], consumed[1]) + } + + override fun onNestedScroll(target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int/*, consumed: IntArray*/) { + val scrollY: Int = -getDragY().toInt() + val consumedx = 0 + val consumedy = if (scrollY + dyUnconsumed > 0) { + -scrollY + } else { + if (type == ViewCompat.TYPE_NON_TOUCH) { + 0 + } else { + dyUnconsumed + } + } + scrollBy(consumedx, consumedy) + } + + private var nestedScrollStopped = false + + override fun onStopNestedScroll(target: View, type: Int) { + mNestedHelper.onStopNestedScroll(target, type) + if (type == ViewCompat.TYPE_TOUCH) { + if (!target.canScrollVertically(-1)) { + touching = false + nestedScrollStopped = true + val openOrClose = if (open) { + when { + velocity > 0F -> true + velocity < 0F -> false + else -> if (open) { + mDragFraction < _dismissFraction + } else { + mDragFraction < 1F - _dismissFraction + } + } + } else { + when { + velocity > 0F -> true + velocity < 0F -> false + else -> if (open) { + mDragFraction < _dismissFraction + } else { + mDragFraction < 1F - _dismissFraction + } + } + } + val f = abs(velocity) / _dismissVelocity + val duration: Int = if (f <= 1) { + _dismissDuration + } else { + val d = (_dismissDuration / f).toInt() + if (d < 200) 200 else d + } + if (openOrClose) { + open(duration) + } else { + close(duration) + } + } + } + } + + override fun getNestedScrollAxes(): Int { + return mNestedHelper.nestedScrollAxes + } + + private inner class DragCallback : androidx.customview.widget.ViewDragHelper.Callback() { + override fun tryCaptureView(child: View, pointerId: Int): Boolean { + if (!enable) return false + if (child != dragView) return false + cleanHandleViewIfRecycled() + handleViews.forEach { + if (DragCompat.contains(it.get()!!, mDownX, mDownY)) { + return true + } + } + if (usingNested) return false + if (isOpened()) { + return !DragCompat.canViewScrollUp(mInnerScrollViews, mDownX, mDownY, false) + } + if (isClosed()) { + return !DragCompat.canViewScrollDown(mInnerScrollViews, mDownX, mDownY, false) + } + return true + } + + override fun onViewCaptured(capturedChild: View, activePointerId: Int) { + super.onViewCaptured(capturedChild, activePointerId) + refreshDragFraction() + touching = true + onDragStart() + } + + override fun getViewHorizontalDragRange(child: View): Int { + return 0 + } + + override fun getViewVerticalDragRange(child: View): Int { + return height - mTop - closeHeight + } + + override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int { + return mLeft + } + + override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int { + val pv = if (top > height - closeHeight) { + height - closeHeight + } else { + if (top < mTop) { + mTop + } else { + top + } + } + return pv + } + + override fun onViewPositionChanged(changedView: View, left: Int, top: Int, dx: Int, dy: Int) { + super.onViewPositionChanged(changedView, left, top, dx, dy) + refreshDragFraction() + onDragging() + if (open) { + if (mDragFraction == 0F) onDragEnd() + } else { + if (mDragFraction == 1F) onDragEnd() + } + } + + override fun onViewReleased(releasedChild: View, xvel: Float, yvel: Float) { + super.onViewReleased(releasedChild, xvel, yvel) + touching = false + val openOrClose = if (open) { + when { + yvel > 0F -> false + yvel < 0F -> true + else -> if (open) { + mDragFraction < _dismissFraction + } else { + mDragFraction < 1F - _dismissFraction + } + } + } else { + when { + yvel > 0F -> false + yvel < 0F -> true + else -> if (open) { + mDragFraction < _dismissFraction + } else { + mDragFraction < 1F - _dismissFraction + } + } + } + val l = mLeft + val t = if (openOrClose) { + mTop + } else { + height - closeHeight + } + open = openOrClose + mDragHelper.settleCapturedViewAt(l, t) + invalidate() + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/widget/bottomdrawer/DragCompat.kt b/app/src/main/java/per/goweii/wanandroid/widget/bottomdrawer/DragCompat.kt new file mode 100644 index 00000000..cba05373 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/bottomdrawer/DragCompat.kt @@ -0,0 +1,270 @@ +package per.goweii.wanandroid.widget.bottomdrawer + +import android.graphics.Rect +import android.view.View +import android.view.ViewGroup +import android.webkit.WebView +import android.widget.AbsListView +import android.widget.HorizontalScrollView +import android.widget.ScrollView +import androidx.core.view.ScrollingView +import java.util.* + +object DragCompat { + private val localRect = Rect() + + fun canViewScrollUp(view: View?, x: Float, y: Float, defaultValueForNull: Boolean): Boolean { + return if (view == null || !view.isShown || !contains(view, x, y)) { + defaultValueForNull + } else view.canScrollVertically(-1) + } + + fun canViewScrollUp(views: List?, x: Float, y: Float, defaultValueForNull: Boolean): Boolean { + if (views == null) { + return defaultValueForNull + } + val contains = contains(views, x, y) ?: return defaultValueForNull + var canViewScroll = false + for (i in contains.indices.reversed()) { + if (!contains[i].isShown) { + continue + } + canViewScroll = ScrollCompat.canScrollVertically(contains[i], -1) + if (canViewScroll) { + break + } + } + return canViewScroll + } + + fun canScrollUpView(views: List?, x: Float, y: Float): View? { + if (views == null) { + return null + } + val contains = contains(views, x, y) ?: return null + for (i in contains.indices.reversed()) { + if (!contains[i].isShown) { + continue + } + if (ScrollCompat.canScrollVertically(contains[i], -1)) { + return contains[i] + } + } + return null + } + + fun canViewScrollDown(view: View?, x: Float, y: Float, defaultValueForNull: Boolean): Boolean { + return if (view == null || !view.isShown || !contains(view, x, y)) { + defaultValueForNull + } else view.canScrollVertically(1) + } + + fun canViewScrollDown(views: List?, x: Float, y: Float, defaultValueForNull: Boolean): Boolean { + if (views == null) { + return defaultValueForNull + } + val contains = contains(views, x, y) ?: return defaultValueForNull + var canViewScroll = false + for (i in contains.indices.reversed()) { + val view = contains[i] + if (!view.isShown) { + continue + } + canViewScroll = ScrollCompat.canScrollVertically(view, 1) + if (canViewScroll) { + break + } + } + return canViewScroll + } + + fun canScrollDownView(views: List?, x: Float, y: Float): View? { + if (views == null) { + return null + } + val contains = contains(views, x, y) ?: return null + for (i in contains.indices.reversed()) { + val view = contains[i] + if (!view.isShown) { + continue + } + if (ScrollCompat.canScrollVertically(view, 1)) { + return view + } + } + return null + } + + fun canViewScrollRight(view: View?, x: Float, y: Float, defaultValueForNull: Boolean): Boolean { + return if (view == null || !view.isShown || !contains(view, x, y)) { + defaultValueForNull + } else view.canScrollHorizontally(-1) + } + + fun canViewScrollRight(views: List?, x: Float, y: Float, defaultValueForNull: Boolean): Boolean { + if (views == null) { + return defaultValueForNull + } + val contains = contains(views, x, y) ?: return defaultValueForNull + var canViewScroll = false + for (i in contains.indices.reversed()) { + val view = contains[i] + if (!view.isShown) { + continue + } + canViewScroll = ScrollCompat.canScrollHorizontally(view, 1) + if (canViewScroll) { + break + } + } + return canViewScroll + } + + fun canScrollRightView(views: List?, x: Float, y: Float): View? { + if (views == null) { + return null + } + val contains = contains(views, x, y) ?: return null + for (i in contains.indices.reversed()) { + val view = contains[i] + if (!view.isShown) { + continue + } + if (ScrollCompat.canScrollHorizontally(view, 1)) { + return view + } + } + return null + } + + fun canViewScrollLeft(view: View?, x: Float, y: Float, defaultValueForNull: Boolean): Boolean { + return if (view == null || !view.isShown || !contains(view, x, y)) { + defaultValueForNull + } else view.canScrollHorizontally(1) + } + + fun canViewScrollLeft(views: List?, x: Float, y: Float, defaultValueForNull: Boolean): Boolean { + if (views == null) { + return defaultValueForNull + } + val contains = contains(views, x, y) ?: return defaultValueForNull + var canViewScroll = false + for (i in contains.indices.reversed()) { + val view = contains[i] + if (!view.isShown) { + continue + } + canViewScroll = ScrollCompat.canScrollHorizontally(view, -1) + if (canViewScroll) { + break + } + } + return canViewScroll + } + + fun canScrollLeftView(views: List?, x: Float, y: Float): View? { + if (views == null) { + return null + } + val contains = contains(views, x, y) ?: return null + for (i in contains.indices.reversed()) { + val view = contains[i] + if (!view.isShown) { + continue + } + if (ScrollCompat.canScrollHorizontally(view, -1)) { + return view + } + } + return null + } + + fun findAllScrollViews(view: View, scrollViews: MutableList) { + if (isScrollableView(view)) { + scrollViews.add(view) + } + if (view is ViewGroup) { + for (i in 0 until view.childCount) { + findAllScrollViews(view.getChildAt(i), scrollViews) + } + } + } + + fun findAllScrollViews(view: View): List { + val views = ArrayList() + if (isScrollableView(view)) { + views.add(view) + } + if (view is ViewGroup) { + for (i in 0 until view.childCount) { + views.addAll(findAllScrollViews(view.getChildAt(i))) + } + } + return views + } + + fun hasScrollableParent(view: View): Boolean { + return findScrollableParent(view) != null + } + + fun findScrollableParent(view: View): View? { + var viewGroup = view.parent as? ViewGroup? + while (viewGroup != null) { + if (isScrollableView(viewGroup)) { + return viewGroup + } + viewGroup = viewGroup.parent as? ViewGroup? + } + return null + } + + fun findFirstClickableView(view: View, x: Float, y: Float): View? { + if (!contains(view, x, y)) return null + var clickableChild: View? = null + if (view is ViewGroup) { + for (i in view.childCount - 1 downTo 0) { + val child = view.getChildAt(i) + clickableChild = findFirstClickableView(child, x, y) + if (clickableChild != null) { + return clickableChild + } + } + } + if (view.isClickable) { + return view + } + return null + } + + private fun isScrollableView(view: View): Boolean { + return (view is ScrollView + || view is HorizontalScrollView + || view is AbsListView + || view is androidx.viewpager.widget.ViewPager + || view is WebView + || view is ScrollingView) + } + + fun contains(view: View, x: Float, y: Float): Boolean { + view.getGlobalVisibleRect(localRect) + return localRect.contains(x.toInt(), y.toInt()) + } + + fun contains(views: List?, x: Float, y: Float): List? { + if (views == null) { + return null + } + val contains = ArrayList(views.size) + val r = localRect + val l = IntArray(2) + for (i in views.indices.reversed()) { + val v = views[i] + v.getLocationOnScreen(l) + r.set(l[0], l[1], l[0] + v.width, l[1] + v.height) + if (r.contains(x.toInt(), y.toInt())) { + contains.add(v) + } + } + return contains + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/widget/bottomdrawer/ScrollCompat.kt b/app/src/main/java/per/goweii/wanandroid/widget/bottomdrawer/ScrollCompat.kt new file mode 100644 index 00000000..3bb3c084 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/bottomdrawer/ScrollCompat.kt @@ -0,0 +1,45 @@ +package per.goweii.wanandroid.widget.bottomdrawer + +import android.view.View +import androidx.core.view.ScrollingView + +object ScrollCompat { + + fun canScrollHorizontally(v: View, direction: Int): Boolean { + return if (v is ScrollingView) { + canScrollingViewScrollHorizontally(v as ScrollingView, direction) + } else { + v.canScrollHorizontally(direction) + } + } + + fun canScrollVertically(v: View, direction: Int): Boolean { + return if (v is ScrollingView) { + canScrollingViewScrollVertically(v as ScrollingView, direction) + } else { + v.canScrollVertically(direction) + } + } + + private fun canScrollingViewScrollHorizontally(view: ScrollingView, direction: Int): Boolean { + val offset = view.computeHorizontalScrollOffset() + val range = view.computeHorizontalScrollRange() - view.computeHorizontalScrollExtent() + if (range == 0) return false + return if (direction < 0) { + offset > 0 + } else { + offset < range - 1 + } + } + + private fun canScrollingViewScrollVertically(view: ScrollingView, direction: Int): Boolean { + val offset = view.computeVerticalScrollOffset() + val range = view.computeVerticalScrollRange() - view.computeVerticalScrollExtent() + if (range == 0) return false + return if (direction < 0) { + offset > 0 + } else { + offset < range - 1 + } + } +} diff --git a/app/src/main/java/per/goweii/wanandroid/widget/refresh/ShiciRefreshHeader.java b/app/src/main/java/per/goweii/wanandroid/widget/refresh/ShiciRefreshHeader.java new file mode 100644 index 00000000..3b9f1064 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/refresh/ShiciRefreshHeader.java @@ -0,0 +1,180 @@ +package per.goweii.wanandroid.widget.refresh; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.scwang.smart.drawable.ProgressDrawable; +import com.scwang.smart.refresh.classics.ArrowDrawable; +import com.scwang.smart.refresh.layout.api.RefreshHeader; +import com.scwang.smart.refresh.layout.api.RefreshLayout; +import com.scwang.smart.refresh.layout.constant.RefreshState; +import com.scwang.smart.refresh.layout.simple.SimpleComponent; + +import per.goweii.basic.utils.ResUtils; +import per.goweii.basic.utils.display.DisplayInfoUtils; +import per.goweii.wanandroid.R; + +public class ShiciRefreshHeader extends SimpleComponent implements RefreshHeader { + private final ImageView imageView; + private final TextView textView; + private int color; + private final ArrowDrawable arrowDrawable; + private final ProgressDrawable progressDrawable; + private final Drawable successDrawable; + private final Drawable failureDrawable; + + private RefreshState mCurState = RefreshState.None; + + static { + ShiciRefreshHolder.instance().refresh(); + } + + private String textPulling() { + String shici = ShiciRefreshHolder.instance().get(); + if (!TextUtils.isEmpty(shici)) return shici; + return textView.getContext().getString(R.string.pull_down_to_refresh); + } + + private String textRefreshing() { + String shici = ShiciRefreshHolder.instance().get(); + if (!TextUtils.isEmpty(shici)) return shici; + return textView.getContext().getString(R.string.refreshing); + } + + private String textRelease() { + String shici = ShiciRefreshHolder.instance().get(); + if (!TextUtils.isEmpty(shici)) return shici; + return textView.getContext().getString(R.string.release_to_refresh); + } + + private String textFinish() { + String shici = ShiciRefreshHolder.instance().get(); + if (!TextUtils.isEmpty(shici)) return shici; + return textView.getContext().getString(R.string.refresh_completed); + } + + private String textFailed() { + String shici = ShiciRefreshHolder.instance().get(); + if (!TextUtils.isEmpty(shici)) return shici; + return textView.getContext().getString(R.string.refresh_failed); + } + + public ShiciRefreshHeader(Context context) { + this(context, null); + } + + public ShiciRefreshHeader(Context context, AttributeSet attrs) { + super(context, attrs, 0); + int padding = (int) DisplayInfoUtils.getInstance().dp2px(20); + setPadding(0, padding, 0, padding); + View.inflate(context, R.layout.layout_shici_refresh_header, this); + color = ResUtils.getThemeColor(context, R.attr.colorTextSecond); + imageView = findViewById(R.id.iv_shici_refresh_header_state); + textView = findViewById(R.id.tv_shici_refresh_header_content); + imageView.animate().setInterpolator(null); + textView.setTextColor(color); + arrowDrawable = new ArrowDrawable(); + arrowDrawable.setColor(color); + progressDrawable = new ProgressDrawable(); + progressDrawable.setColor(color); + successDrawable = ResUtils.getDrawable(R.drawable.ic_success); + successDrawable.setTint(color); + failureDrawable = ResUtils.getDrawable(R.drawable.ic_failure); + failureDrawable.setTint(color); + setColor(color); + setAlpha(0.4F); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + imageView.animate().cancel(); + progressDrawable.stop(); + } + + public void setColor(@ColorInt int color) { + this.color = color; + textView.setTextColor(color); + imageView.setColorFilter(color); + arrowDrawable.setColor(color); + progressDrawable.setColor(color); + successDrawable.setTint(color); + failureDrawable.setTint(color); + } + + public void setTextAndHideIcon(@Nullable String text) { + progressDrawable.stop(); + imageView.setImageResource(android.R.color.transparent); + textView.setText(text); + } + + public void restoreToCurrState() { + updateByState(mCurState); + } + + @Override + public int onFinish(@NonNull RefreshLayout layout, boolean success) { + if (success) { + imageView.setImageDrawable(successDrawable); + textView.setText(textFinish()); + } else { + imageView.setImageDrawable(failureDrawable); + textView.setText(textFailed()); + } + ShiciRefreshHolder.instance().refresh(); + return 500; + } + + @Override + public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) { + mCurState = newState; + super.onStateChanged(refreshLayout, oldState, newState); + updateByState(newState); + } + + private void updateByState(@NonNull RefreshState newState) { + switch (newState) { + case None: + imageView.animate().cancel(); + imageView.setRotation(0F); + progressDrawable.stop(); + imageView.setImageResource(android.R.color.transparent); + textView.setText(""); + break; + case PullDownToRefresh: + textView.setText(textPulling()); + imageView.animate().rotation(0); + imageView.setImageDrawable(arrowDrawable); + break; + case ReleaseToRefresh: + textView.setText(textRelease()); + imageView.animate().rotation(180); + imageView.setImageDrawable(arrowDrawable); + break; + case RefreshReleased: + textView.setText(textRefreshing()); + imageView.animate().cancel(); + imageView.setRotation(0F); + imageView.setImageDrawable(progressDrawable); + progressDrawable.start(); + break; + case Refreshing: + break; + case RefreshFinish: + imageView.animate().cancel(); + imageView.setRotation(0F); + progressDrawable.stop(); + break; + } + } + +} diff --git a/app/src/main/java/per/goweii/wanandroid/widget/refresh/ShiciRefreshHolder.java b/app/src/main/java/per/goweii/wanandroid/widget/refresh/ShiciRefreshHolder.java new file mode 100644 index 00000000..bc5260f7 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/refresh/ShiciRefreshHolder.java @@ -0,0 +1,67 @@ +package per.goweii.wanandroid.widget.refresh; + +import java.util.ArrayList; +import java.util.List; + +import per.goweii.rxhttp.core.RxLife; +import per.goweii.wanandroid.http.RequestCallback; +import per.goweii.wanandroid.module.main.model.JinrishiciBean; +import per.goweii.wanandroid.module.main.model.MainRequest; + +/** + * @author CuiZhen + * @date 2020/5/10 + */ +public class ShiciRefreshHolder { + + private static ShiciRefreshHolder instance = null; + private static final int MAX_SIZE = 3; + + public static ShiciRefreshHolder instance() { + if (instance == null) { + instance = new ShiciRefreshHolder(); + } + return instance; + } + + private final RxLife mRxLife; + + private final List mCache = new ArrayList<>(MAX_SIZE); + + private ShiciRefreshHolder() { + mRxLife = RxLife.create(); + request(); + } + + private void request() { + if (mCache.size() >= MAX_SIZE) { + return; + } + MainRequest.getJinrishici(mRxLife, new RequestCallback() { + @Override + public void onSuccess(int code, JinrishiciBean data) { + mCache.add(data.getContent()); + request(); + } + + @Override + public void onFailed(int code, String msg) { + } + }); + } + + public void refresh() { + if (mCache.size() >= 2) { + mCache.remove(0); + } + request(); + } + + public String get() { + if (mCache.isEmpty()) { + return null; + } + return mCache.get(0); + } + +} diff --git a/app/src/main/java/per/goweii/wanandroid/widget/refresh/SimpleOnMultiListener.java b/app/src/main/java/per/goweii/wanandroid/widget/refresh/SimpleOnMultiListener.java new file mode 100644 index 00000000..0a87cb90 --- /dev/null +++ b/app/src/main/java/per/goweii/wanandroid/widget/refresh/SimpleOnMultiListener.java @@ -0,0 +1,66 @@ +package per.goweii.wanandroid.widget.refresh; + +import androidx.annotation.NonNull; + +import com.scwang.smart.refresh.layout.api.RefreshFooter; +import com.scwang.smart.refresh.layout.api.RefreshHeader; +import com.scwang.smart.refresh.layout.api.RefreshLayout; +import com.scwang.smart.refresh.layout.constant.RefreshState; +import com.scwang.smart.refresh.layout.listener.OnMultiListener; + +public class SimpleOnMultiListener implements OnMultiListener { + @Override + public void onHeaderMoving(RefreshHeader header, boolean isDragging, float percent, int offset, int headerHeight, int maxDragHeight) { + + } + + @Override + public void onHeaderReleased(RefreshHeader header, int headerHeight, int maxDragHeight) { + + } + + @Override + public void onHeaderStartAnimator(RefreshHeader header, int headerHeight, int maxDragHeight) { + + } + + @Override + public void onHeaderFinish(RefreshHeader header, boolean success) { + + } + + @Override + public void onFooterMoving(RefreshFooter footer, boolean isDragging, float percent, int offset, int footerHeight, int maxDragHeight) { + + } + + @Override + public void onFooterReleased(RefreshFooter footer, int footerHeight, int maxDragHeight) { + + } + + @Override + public void onFooterStartAnimator(RefreshFooter footer, int footerHeight, int maxDragHeight) { + + } + + @Override + public void onFooterFinish(RefreshFooter footer, boolean success) { + + } + + @Override + public void onLoadMore(@NonNull RefreshLayout refreshLayout) { + + } + + @Override + public void onRefresh(@NonNull RefreshLayout refreshLayout) { + + } + + @Override + public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) { + + } +} diff --git a/app/src/main/res/color-night/selector_text_color_article_author.xml b/app/src/main/res/color-night/selector_text_color_article_author.xml new file mode 100644 index 00000000..5b869131 --- /dev/null +++ b/app/src/main/res/color-night/selector_text_color_article_author.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/color-night/selector_text_color_article_chapter.xml b/app/src/main/res/color-night/selector_text_color_article_chapter.xml new file mode 100644 index 00000000..622a6cf8 --- /dev/null +++ b/app/src/main/res/color-night/selector_text_color_article_chapter.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/color-night/selector_text_color_article_tag.xml b/app/src/main/res/color-night/selector_text_color_article_tag.xml new file mode 100644 index 00000000..8e5376d1 --- /dev/null +++ b/app/src/main/res/color-night/selector_text_color_article_tag.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/picture_selector_preview_text_color.xml b/app/src/main/res/color/picture_selector_preview_text_color.xml deleted file mode 100644 index 50004401..00000000 --- a/app/src/main/res/color/picture_selector_preview_text_color.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/color/selector_text_color_article_author.xml b/app/src/main/res/color/selector_text_color_article_author.xml new file mode 100644 index 00000000..5b869131 --- /dev/null +++ b/app/src/main/res/color/selector_text_color_article_author.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/selector_text_color_article_chapter.xml b/app/src/main/res/color/selector_text_color_article_chapter.xml new file mode 100644 index 00000000..622a6cf8 --- /dev/null +++ b/app/src/main/res/color/selector_text_color_article_chapter.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/color/selector_text_color_article_tag.xml b/app/src/main/res/color/selector_text_color_article_tag.xml new file mode 100644 index 00000000..8e5376d1 --- /dev/null +++ b/app/src/main/res/color/selector_text_color_article_tag.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night-xxhdpi/ic_pause.png b/app/src/main/res/drawable-night-xxhdpi/ic_pause.png new file mode 100644 index 00000000..28040da1 Binary files /dev/null and b/app/src/main/res/drawable-night-xxhdpi/ic_pause.png differ diff --git a/app/src/main/res/drawable-night-xxhdpi/ic_play.png b/app/src/main/res/drawable-night-xxhdpi/ic_play.png new file mode 100644 index 00000000..0e0f7482 Binary files /dev/null and b/app/src/main/res/drawable-night-xxhdpi/ic_play.png differ diff --git a/app/src/main/res/drawable-night-xxhdpi/ic_skip_next.png b/app/src/main/res/drawable-night-xxhdpi/ic_skip_next.png new file mode 100644 index 00000000..b7b2db5c Binary files /dev/null and b/app/src/main/res/drawable-night-xxhdpi/ic_skip_next.png differ diff --git a/app/src/main/res/drawable-night-xxhdpi/ic_skip_previous.png b/app/src/main/res/drawable-night-xxhdpi/ic_skip_previous.png new file mode 100644 index 00000000..8197618f Binary files /dev/null and b/app/src/main/res/drawable-night-xxhdpi/ic_skip_previous.png differ diff --git a/app/src/main/res/drawable-night/action_search_bg.xml b/app/src/main/res/drawable-night/action_search_bg.xml new file mode 100644 index 00000000..2422d059 --- /dev/null +++ b/app/src/main/res/drawable-night/action_search_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/article_tag_bg.xml b/app/src/main/res/drawable-night/article_tag_bg.xml new file mode 100644 index 00000000..217bcebf --- /dev/null +++ b/app/src/main/res/drawable-night/article_tag_bg.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/article_tag_bg_pressed.xml b/app/src/main/res/drawable-night/article_tag_bg_pressed.xml new file mode 100644 index 00000000..e5c2a57e --- /dev/null +++ b/app/src/main/res/drawable-night/article_tag_bg_pressed.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/bg_crash_exit.xml b/app/src/main/res/drawable-night/bg_crash_exit.xml new file mode 100644 index 00000000..fd2eb8df --- /dev/null +++ b/app/src/main/res/drawable-night/bg_crash_exit.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/basic_ui/src/main/res/drawable/basic_ui_toast_bg.xml b/app/src/main/res/drawable-night/bg_crash_restart.xml similarity index 62% rename from basic_ui/src/main/res/drawable/basic_ui_toast_bg.xml rename to app/src/main/res/drawable-night/bg_crash_restart.xml index 7759c0dd..2304558e 100644 --- a/basic_ui/src/main/res/drawable/basic_ui_toast_bg.xml +++ b/app/src/main/res/drawable-night/bg_crash_restart.xml @@ -1,9 +1,9 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/bg_notification.xml b/app/src/main/res/drawable-night/bg_notification.xml new file mode 100644 index 00000000..fdb3ce89 --- /dev/null +++ b/app/src/main/res/drawable-night/bg_notification.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/bg_solid_color_heart_radius_max.xml b/app/src/main/res/drawable-night/bg_solid_color_heart_radius_max.xml new file mode 100644 index 00000000..574ef4ef --- /dev/null +++ b/app/src/main/res/drawable-night/bg_solid_color_heart_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_accent_radius_max.xml b/app/src/main/res/drawable-night/bg_white_circle.xml similarity index 77% rename from basic_res/src/main/res/drawable/bg_solid_round_color_accent_radius_max.xml rename to app/src/main/res/drawable-night/bg_white_circle.xml index 9a05fc9f..cf043b0a 100644 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_accent_radius_max.xml +++ b/app/src/main/res/drawable-night/bg_white_circle.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/coin_rank_progress_bar.xml b/app/src/main/res/drawable-night/coin_rank_progress_bar.xml new file mode 100644 index 00000000..81851d50 --- /dev/null +++ b/app/src/main/res/drawable-night/coin_rank_progress_bar.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/scrollbar.xml b/app/src/main/res/drawable-night/scrollbar.xml new file mode 100644 index 00000000..f62e9145 --- /dev/null +++ b/app/src/main/res/drawable-night/scrollbar.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/selector_article_tag_bg.xml b/app/src/main/res/drawable-night/selector_article_tag_bg.xml new file mode 100644 index 00000000..44052105 --- /dev/null +++ b/app/src/main/res/drawable-night/selector_article_tag_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/selector_flush.xml b/app/src/main/res/drawable-night/selector_flush.xml new file mode 100644 index 00000000..94eedb74 --- /dev/null +++ b/app/src/main/res/drawable-night/selector_flush.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/shadow_circle.xml b/app/src/main/res/drawable-night/shadow_circle.xml new file mode 100644 index 00000000..9ba697b1 --- /dev/null +++ b/app/src/main/res/drawable-night/shadow_circle.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/shape_image_perview_place_holder.xml b/app/src/main/res/drawable-night/shape_image_perview_place_holder.xml new file mode 100644 index 00000000..c57b154b --- /dev/null +++ b/app/src/main/res/drawable-night/shape_image_perview_place_holder.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/shape_image_place_holder.xml b/app/src/main/res/drawable-night/shape_image_place_holder.xml new file mode 100644 index 00000000..fa4cefd1 --- /dev/null +++ b/app/src/main/res/drawable-night/shape_image_place_holder.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/splash_bg.xml b/app/src/main/res/drawable-night/splash_bg.xml index 27c00947..f91c992f 100644 --- a/app/src/main/res/drawable-night/splash_bg.xml +++ b/app/src/main/res/drawable-night/splash_bg.xml @@ -5,17 +5,18 @@ - + tools:height="360dp" + tools:width="360dp" /> + + android:tint="#FFFFFF" /> \ No newline at end of file diff --git a/app/src/main/res/drawable-night/splash_bg_gold.xml b/app/src/main/res/drawable-night/splash_bg_gold.xml new file mode 100644 index 00000000..f91c992f --- /dev/null +++ b/app/src/main/res/drawable-night/splash_bg_gold.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/splash_bg_green.xml b/app/src/main/res/drawable-night/splash_bg_green.xml new file mode 100644 index 00000000..f91c992f --- /dev/null +++ b/app/src/main/res/drawable-night/splash_bg_green.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/splash_bg_pink.xml b/app/src/main/res/drawable-night/splash_bg_pink.xml new file mode 100644 index 00000000..f91c992f --- /dev/null +++ b/app/src/main/res/drawable-night/splash_bg_pink.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-night/splash_bg_red.xml b/app/src/main/res/drawable-night/splash_bg_red.xml new file mode 100644 index 00000000..f91c992f --- /dev/null +++ b/app/src/main/res/drawable-night/splash_bg_red.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-xxhdpi/ic_about.png b/app/src/main/res/drawable-xxhdpi/ic_about.png index 51915cf6..52c9b42c 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_about.png and b/app/src/main/res/drawable-xxhdpi/ic_about.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_account_normal.png b/app/src/main/res/drawable-xxhdpi/ic_account_normal.png index cb259107..be79ec8a 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_account_normal.png and b/app/src/main/res/drawable-xxhdpi/ic_account_normal.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_add.png b/app/src/main/res/drawable-xxhdpi/ic_add.png index 0f536074..23f39a85 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_add.png and b/app/src/main/res/drawable-xxhdpi/ic_add.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_album.png b/app/src/main/res/drawable-xxhdpi/ic_album.png index 3f7af7a1..588d9173 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_album.png and b/app/src/main/res/drawable-xxhdpi/ic_album.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_article.png b/app/src/main/res/drawable-xxhdpi/ic_article.png deleted file mode 100644 index 5be8ce88..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_article.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_avatar_edit.png b/app/src/main/res/drawable-xxhdpi/ic_avatar_edit.png new file mode 100644 index 00000000..ff1540b1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_avatar_edit.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_home.png b/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_home.png index e0d4a756..0a6dfce7 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_home.png and b/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_home.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_mine.png b/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_mine.png index e946b622..41e73f8a 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_mine.png and b/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_mine.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_navi.png b/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_navi.png index 1db01453..b1b08a22 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_navi.png and b/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_navi.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_project.png b/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_project.png deleted file mode 100644 index 35d3dbaf..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_project.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_ques.png b/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_ques.png new file mode 100644 index 00000000..67bd05cb Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_ques.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_square.png b/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_square.png new file mode 100644 index 00000000..7f0d9d75 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_square.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_wechat.png b/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_wechat.png deleted file mode 100644 index 8274950b..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_bottom_bar_wechat.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_browser.png b/app/src/main/res/drawable-xxhdpi/ic_browser.png index 58191b34..5404b123 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_browser.png and b/app/src/main/res/drawable-xxhdpi/ic_browser.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_capture.png b/app/src/main/res/drawable-xxhdpi/ic_capture.png index 583e11e7..8061ede6 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_capture.png and b/app/src/main/res/drawable-xxhdpi/ic_capture.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_check.png b/app/src/main/res/drawable-xxhdpi/ic_check.png new file mode 100644 index 00000000..1785c964 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_check.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_check_box_checked.png b/app/src/main/res/drawable-xxhdpi/ic_check_box_checked.png deleted file mode 100644 index 65029ab6..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_check_box_checked.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_check_box_unchecked.png b/app/src/main/res/drawable-xxhdpi/ic_check_box_unchecked.png deleted file mode 100644 index 1f254df3..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_check_box_unchecked.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_cloud.png b/app/src/main/res/drawable-xxhdpi/ic_cloud.png deleted file mode 100644 index 23ab3980..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_cloud.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_coin.png b/app/src/main/res/drawable-xxhdpi/ic_coin.png index a865ba85..2baf03fe 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_coin.png and b/app/src/main/res/drawable-xxhdpi/ic_coin.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_collect.png b/app/src/main/res/drawable-xxhdpi/ic_collect.png index 9a27efd1..1c7f069f 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_collect.png and b/app/src/main/res/drawable-xxhdpi/ic_collect.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_copy_link.png b/app/src/main/res/drawable-xxhdpi/ic_copy_link.png index c15a059f..cfecfc20 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_copy_link.png and b/app/src/main/res/drawable-xxhdpi/ic_copy_link.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_delete.png b/app/src/main/res/drawable-xxhdpi/ic_delete.png index 3b2194a3..76f1b0b1 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_delete.png and b/app/src/main/res/drawable-xxhdpi/ic_delete.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_down.png b/app/src/main/res/drawable-xxhdpi/ic_down.png new file mode 100644 index 00000000..c1c1212c Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_down.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_download.png b/app/src/main/res/drawable-xxhdpi/ic_download.png new file mode 100644 index 00000000..71bee724 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_download.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_email.png b/app/src/main/res/drawable-xxhdpi/ic_email.png new file mode 100644 index 00000000..ac85a29b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_email.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_empty.png b/app/src/main/res/drawable-xxhdpi/ic_empty.png index bc9ee0bb..6ebfd349 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_empty.png and b/app/src/main/res/drawable-xxhdpi/ic_empty.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_enter.png b/app/src/main/res/drawable-xxhdpi/ic_enter.png new file mode 100644 index 00000000..8a87ddcc Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_enter.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_error.png b/app/src/main/res/drawable-xxhdpi/ic_error.png index c751d98b..4603b778 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_error.png and b/app/src/main/res/drawable-xxhdpi/ic_error.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_exit.png b/app/src/main/res/drawable-xxhdpi/ic_exit.png index 23b92fa0..7ea4f32e 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_exit.png and b/app/src/main/res/drawable-xxhdpi/ic_exit.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_eye_normal.png b/app/src/main/res/drawable-xxhdpi/ic_eye_normal.png index 81bde5bf..e65de403 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_eye_normal.png and b/app/src/main/res/drawable-xxhdpi/ic_eye_normal.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_failure.png b/app/src/main/res/drawable-xxhdpi/ic_failure.png new file mode 100644 index 00000000..7de4eb45 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_failure.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_flush_close.png b/app/src/main/res/drawable-xxhdpi/ic_flush_close.png new file mode 100644 index 00000000..5dab9ae1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_flush_close.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_flush_open.png b/app/src/main/res/drawable-xxhdpi/ic_flush_open.png new file mode 100644 index 00000000..0fc17613 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_flush_open.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_github.png b/app/src/main/res/drawable-xxhdpi/ic_github.png index c13caf58..e2467b76 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_github.png and b/app/src/main/res/drawable-xxhdpi/ic_github.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_go_left.png b/app/src/main/res/drawable-xxhdpi/ic_go_left.png new file mode 100644 index 00000000..5bde7cdd Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_go_left.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_home.png b/app/src/main/res/drawable-xxhdpi/ic_home.png index 4774974b..dbc68457 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_home.png and b/app/src/main/res/drawable-xxhdpi/ic_home.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_http_interrupt.png b/app/src/main/res/drawable-xxhdpi/ic_http_interrupt.png index 6c942b21..6c1220b7 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_http_interrupt.png and b/app/src/main/res/drawable-xxhdpi/ic_http_interrupt.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_into.png b/app/src/main/res/drawable-xxhdpi/ic_into.png index 9f113811..14777663 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_into.png and b/app/src/main/res/drawable-xxhdpi/ic_into.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_jianshu.png b/app/src/main/res/drawable-xxhdpi/ic_jianshu.png index 9e39b1e3..6b158192 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_jianshu.png and b/app/src/main/res/drawable-xxhdpi/ic_jianshu.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_listen.png b/app/src/main/res/drawable-xxhdpi/ic_listen.png new file mode 100644 index 00000000..e255dab0 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_listen.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_menu.png b/app/src/main/res/drawable-xxhdpi/ic_menu.png index 7ad28cd2..6c940273 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_menu.png and b/app/src/main/res/drawable-xxhdpi/ic_menu.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_more.png b/app/src/main/res/drawable-xxhdpi/ic_more.png index 2f3cca25..02d40071 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_more.png and b/app/src/main/res/drawable-xxhdpi/ic_more.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_next.png b/app/src/main/res/drawable-xxhdpi/ic_next.png index 952f5674..80deba57 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_next.png and b/app/src/main/res/drawable-xxhdpi/ic_next.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_none.png b/app/src/main/res/drawable-xxhdpi/ic_none.png new file mode 100644 index 00000000..5044a4a2 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_none.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_notification.png b/app/src/main/res/drawable-xxhdpi/ic_notification.png new file mode 100644 index 00000000..d09620db Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_notification.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_password_normal.png b/app/src/main/res/drawable-xxhdpi/ic_password_normal.png index 1b654f36..1c03d531 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_password_normal.png and b/app/src/main/res/drawable-xxhdpi/ic_password_normal.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_pause.png b/app/src/main/res/drawable-xxhdpi/ic_pause.png new file mode 100644 index 00000000..564c4547 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_pause.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_play.png b/app/src/main/res/drawable-xxhdpi/ic_play.png new file mode 100644 index 00000000..bc949ffe Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_play.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_qq.png b/app/src/main/res/drawable-xxhdpi/ic_qq.png index 1b2d4ef8..ad8e3db0 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_qq.png and b/app/src/main/res/drawable-xxhdpi/ic_qq.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_qq_group.png b/app/src/main/res/drawable-xxhdpi/ic_qq_group.png index c61c76f1..67dfde6e 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_qq_group.png and b/app/src/main/res/drawable-xxhdpi/ic_qq_group.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_qrcode.png b/app/src/main/res/drawable-xxhdpi/ic_qrcode.png index 3a13452b..7678cafc 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_qrcode.png and b/app/src/main/res/drawable-xxhdpi/ic_qrcode.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_rank.png b/app/src/main/res/drawable-xxhdpi/ic_rank.png index 787c4b58..db478410 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_rank.png and b/app/src/main/res/drawable-xxhdpi/ic_rank.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_read_later.png b/app/src/main/res/drawable-xxhdpi/ic_read_later.png index 4239d7da..00a2631f 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_read_later.png and b/app/src/main/res/drawable-xxhdpi/ic_read_later.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_read_later_added.png b/app/src/main/res/drawable-xxhdpi/ic_read_later_added.png new file mode 100644 index 00000000..a1f94466 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_read_later_added.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_read_record.png b/app/src/main/res/drawable-xxhdpi/ic_read_record.png new file mode 100644 index 00000000..e77cacfb Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_read_record.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_refresh.png b/app/src/main/res/drawable-xxhdpi/ic_refresh.png index 98f4125c..6bd477ae 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_refresh.png and b/app/src/main/res/drawable-xxhdpi/ic_refresh.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_remove.png b/app/src/main/res/drawable-xxhdpi/ic_remove.png index 06d64c71..aec3a37e 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_remove.png and b/app/src/main/res/drawable-xxhdpi/ic_remove.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_rule.png b/app/src/main/res/drawable-xxhdpi/ic_rule.png index c5ec78c0..55ef9514 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_rule.png and b/app/src/main/res/drawable-xxhdpi/ic_rule.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_scan.png b/app/src/main/res/drawable-xxhdpi/ic_scan.png new file mode 100644 index 00000000..60f02a71 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_scan.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_search.png b/app/src/main/res/drawable-xxhdpi/ic_search.png index b0ec0d6c..66f945c0 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_search.png and b/app/src/main/res/drawable-xxhdpi/ic_search.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_setting.png b/app/src/main/res/drawable-xxhdpi/ic_setting.png index f3a53add..bb86c69c 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_setting.png and b/app/src/main/res/drawable-xxhdpi/ic_setting.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_share.png b/app/src/main/res/drawable-xxhdpi/ic_share.png index 244bc81d..d414f8c7 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_share.png and b/app/src/main/res/drawable-xxhdpi/ic_share.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_share_article.png b/app/src/main/res/drawable-xxhdpi/ic_share_article.png index a150c712..d6d14b3a 100644 Binary files a/app/src/main/res/drawable-xxhdpi/ic_share_article.png and b/app/src/main/res/drawable-xxhdpi/ic_share_article.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_skip_next.png b/app/src/main/res/drawable-xxhdpi/ic_skip_next.png new file mode 100644 index 00000000..e679307b Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_skip_next.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_skip_previous.png b/app/src/main/res/drawable-xxhdpi/ic_skip_previous.png new file mode 100644 index 00000000..5f783fbf Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_skip_previous.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_success.png b/app/src/main/res/drawable-xxhdpi/ic_success.png new file mode 100644 index 00000000..87d6e367 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_success.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_swipe_back.png b/app/src/main/res/drawable-xxhdpi/ic_swipe_back.png deleted file mode 100644 index 5ad9c4a0..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_swipe_back.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_wind.png b/app/src/main/res/drawable-xxhdpi/ic_wind.png deleted file mode 100644 index 33bd1d1c..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_wind.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/logo_512.png b/app/src/main/res/drawable-xxhdpi/logo_512.png new file mode 100644 index 00000000..672c1634 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/logo_512.png differ diff --git a/app/src/main/res/drawable-xxhdpi/pic_404.jpg b/app/src/main/res/drawable-xxhdpi/pic_404.jpg deleted file mode 100644 index 2bdfc0de..00000000 Binary files a/app/src/main/res/drawable-xxhdpi/pic_404.jpg and /dev/null differ diff --git a/app/src/main/res/drawable/action_search_bg.xml b/app/src/main/res/drawable/action_search_bg.xml index 4f399a32..2422d059 100644 --- a/app/src/main/res/drawable/action_search_bg.xml +++ b/app/src/main/res/drawable/action_search_bg.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/drawable/article_tag_bg.xml b/app/src/main/res/drawable/article_tag_bg.xml index 08c1f0fe..217bcebf 100644 --- a/app/src/main/res/drawable/article_tag_bg.xml +++ b/app/src/main/res/drawable/article_tag_bg.xml @@ -1,5 +1,7 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/article_tag_bg_pressed.xml b/app/src/main/res/drawable/article_tag_bg_pressed.xml new file mode 100644 index 00000000..e5c2a57e --- /dev/null +++ b/app/src/main/res/drawable/article_tag_bg_pressed.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_crash_exit.xml b/app/src/main/res/drawable/bg_crash_exit.xml new file mode 100644 index 00000000..fd2eb8df --- /dev/null +++ b/app/src/main/res/drawable/bg_crash_exit.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_crash_restart.xml b/app/src/main/res/drawable/bg_crash_restart.xml new file mode 100644 index 00000000..2304558e --- /dev/null +++ b/app/src/main/res/drawable/bg_crash_restart.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_notification.xml b/app/src/main/res/drawable/bg_notification.xml new file mode 100644 index 00000000..8621c922 --- /dev/null +++ b/app/src/main/res/drawable/bg_notification.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_solid_color_heart_radius_max.xml b/app/src/main/res/drawable/bg_solid_color_heart_radius_max.xml new file mode 100644 index 00000000..574ef4ef --- /dev/null +++ b/app/src/main/res/drawable/bg_solid_color_heart_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/bg_white_circle.xml b/app/src/main/res/drawable/bg_white_circle.xml new file mode 100644 index 00000000..cf043b0a --- /dev/null +++ b/app/src/main/res/drawable/bg_white_circle.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/coin_rank_progress_bar.xml b/app/src/main/res/drawable/coin_rank_progress_bar.xml index 000a6b48..81851d50 100644 --- a/app/src/main/res/drawable/coin_rank_progress_bar.xml +++ b/app/src/main/res/drawable/coin_rank_progress_bar.xml @@ -4,14 +4,14 @@ - + - + diff --git a/app/src/main/res/drawable/picture_selector_checkbox.xml b/app/src/main/res/drawable/picture_selector_checkbox.xml deleted file mode 100644 index 16f504af..00000000 --- a/app/src/main/res/drawable/picture_selector_checkbox.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/picture_selector_num_oval.xml b/app/src/main/res/drawable/picture_selector_num_oval.xml deleted file mode 100644 index d4343151..00000000 --- a/app/src/main/res/drawable/picture_selector_num_oval.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/scrollbar.xml b/app/src/main/res/drawable/scrollbar.xml index 520e3169..f62e9145 100644 --- a/app/src/main/res/drawable/scrollbar.xml +++ b/app/src/main/res/drawable/scrollbar.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_article_tag_bg.xml b/app/src/main/res/drawable/selector_article_tag_bg.xml new file mode 100644 index 00000000..44052105 --- /dev/null +++ b/app/src/main/res/drawable/selector_article_tag_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/selector_flush.xml b/app/src/main/res/drawable/selector_flush.xml new file mode 100644 index 00000000..94eedb74 --- /dev/null +++ b/app/src/main/res/drawable/selector_flush.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shadow_circle.xml b/app/src/main/res/drawable/shadow_circle.xml new file mode 100644 index 00000000..9ba697b1 --- /dev/null +++ b/app/src/main/res/drawable/shadow_circle.xml @@ -0,0 +1,11 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_dialog_card_share_btn_mask.xml b/app/src/main/res/drawable/shape_dialog_card_share_btn_mask.xml new file mode 100644 index 00000000..7fdd1d16 --- /dev/null +++ b/app/src/main/res/drawable/shape_dialog_card_share_btn_mask.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_image_perview_place_holder.xml b/app/src/main/res/drawable/shape_image_perview_place_holder.xml new file mode 100644 index 00000000..c57b154b --- /dev/null +++ b/app/src/main/res/drawable/shape_image_perview_place_holder.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_image_place_holder.xml b/app/src/main/res/drawable/shape_image_place_holder.xml new file mode 100644 index 00000000..465337cb --- /dev/null +++ b/app/src/main/res/drawable/shape_image_place_holder.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/splash_bg.xml b/app/src/main/res/drawable/splash_bg.xml index ed609f8c..8e091457 100644 --- a/app/src/main/res/drawable/splash_bg.xml +++ b/app/src/main/res/drawable/splash_bg.xml @@ -4,14 +4,18 @@ - - + + - + \ No newline at end of file diff --git a/app/src/main/res/drawable/splash_bg_gold.xml b/app/src/main/res/drawable/splash_bg_gold.xml new file mode 100644 index 00000000..4a0d06d2 --- /dev/null +++ b/app/src/main/res/drawable/splash_bg_gold.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/splash_bg_green.xml b/app/src/main/res/drawable/splash_bg_green.xml new file mode 100644 index 00000000..204e9a94 --- /dev/null +++ b/app/src/main/res/drawable/splash_bg_green.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/splash_bg_pink.xml b/app/src/main/res/drawable/splash_bg_pink.xml new file mode 100644 index 00000000..6c18f201 --- /dev/null +++ b/app/src/main/res/drawable/splash_bg_pink.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/splash_bg_red.xml b/app/src/main/res/drawable/splash_bg_red.xml new file mode 100644 index 00000000..940850a5 --- /dev/null +++ b/app/src/main/res/drawable/splash_bg_red.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/action_bar_fixed_magic_indicator.xml b/app/src/main/res/layout/action_bar_fixed_magic_indicator.xml index 1f7fb9d1..ad011c15 100644 --- a/app/src/main/res/layout/action_bar_fixed_magic_indicator.xml +++ b/app/src/main/res/layout/action_bar_fixed_magic_indicator.xml @@ -2,14 +2,15 @@ + xmlns:tools="http://schemas.android.com/tools" + android:background="?colorMainOrSurface"> @@ -17,8 +18,9 @@ android:id="@+id/action_bar_fixed_magic_indicator_iv_back" style="@style/actionbarex_common_action_bar_icon" android:visibility="gone" + tools:visibility="visible" android:src="@drawable/ic_back" - android:tint="@color/basic_ui_action_bar_icon" + android:tint="?colorOnMainOrSurface" android:padding="13dp"/> \ No newline at end of file diff --git a/app/src/main/res/layout/action_bar_mine.xml b/app/src/main/res/layout/action_bar_mine.xml new file mode 100644 index 00000000..d64f17a6 --- /dev/null +++ b/app/src/main/res/layout/action_bar_mine.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/action_bar_multi_magic_indicator.xml b/app/src/main/res/layout/action_bar_multi_magic_indicator.xml index ff5643ad..37cf1eca 100644 --- a/app/src/main/res/layout/action_bar_multi_magic_indicator.xml +++ b/app/src/main/res/layout/action_bar_multi_magic_indicator.xml @@ -3,6 +3,6 @@ android:id="@+id/mi" android:layout_width="match_parent" android:layout_height="@dimen/action_bar_height" - android:background="@color/basic_ui_action_bar_bg" + android:background="?colorMainOrSurface" android:overScrollMode="never" android:scrollbars="none" /> \ No newline at end of file diff --git a/app/src/main/res/layout/action_bar_search.xml b/app/src/main/res/layout/action_bar_search.xml new file mode 100644 index 00000000..4bbce1a7 --- /dev/null +++ b/app/src/main/res/layout/action_bar_search.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/action_bar_web.xml b/app/src/main/res/layout/action_bar_web.xml index 3b71057b..2a552083 100644 --- a/app/src/main/res/layout/action_bar_web.xml +++ b/app/src/main/res/layout/action_bar_web.xml @@ -4,7 +4,7 @@ android:layout_height="@dimen/action_bar_height" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" - android:background="@color/basic_ui_action_bar_bg"> + android:background="?colorMainOrSurface"> + android:textColorHint="?colorOnMainOrSurfaceAlpha" + android:textColor="?colorOnMainOrSurface" + android:background="?colorTransparent" /> + android:tint="?colorOnMainOrSurface" + android:src="@drawable/ic_into" + tools:ignore="UseAppTint" /> diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index 0aa576eb..1f9fb568 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -4,39 +4,51 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background"> + android:background="?colorBackground"> + app:abc_rightIconRes="@drawable/ic_share" + app:abc_titleText="@string/about_us" /> - + android:layout_marginTop="50dp"> + + + + + + + tools:text="1.0.0(1)" /> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + + + + + + + + + + + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> @@ -78,27 +133,27 @@ android:background="@drawable/bg_press_color_surface" android:gravity="center" android:orientation="horizontal" - android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> @@ -111,27 +166,60 @@ android:background="@drawable/bg_press_color_surface" android:gravity="center" android:orientation="horizontal" - android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + + + + + + + + + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> @@ -141,8 +229,8 @@ android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="16dp" - android:text="本APP由Goweii独立开发" - android:textColor="@color/text_third" + android:text="@string/this_app_is_independently_developed_by_goweii" + android:textColor="?colorTextThird" android:textSize="@dimen/text_notes" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about_me.xml b/app/src/main/res/layout/activity_about_me.xml index 5b4de509..e39f1214 100644 --- a/app/src/main/res/layout/activity_about_me.xml +++ b/app/src/main/res/layout/activity_about_me.xml @@ -4,17 +4,18 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background" + android:background="?colorBackground" android:clipChildren="false"> + android:tint="?colorAboutMeBlurOverlay" + tools:background="?colorMain" + tools:ignore="UseAppTint" /> - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + android:background="?colorTransparent" + app:abc_rightText="@string/wan_watchword" + app:abc_titleText="@string/about_the_author" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_ai_setting.xml b/app/src/main/res/layout/activity_ai_setting.xml new file mode 100644 index 00000000..d2888732 --- /dev/null +++ b/app/src/main/res/layout/activity_ai_setting.xml @@ -0,0 +1,200 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_article.xml b/app/src/main/res/layout/activity_article.xml new file mode 100644 index 00000000..7137879e --- /dev/null +++ b/app/src/main/res/layout/activity_article.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_article_float_btn.xml b/app/src/main/res/layout/activity_article_float_btn.xml new file mode 100644 index 00000000..3560e55d --- /dev/null +++ b/app/src/main/res/layout/activity_article_float_btn.xml @@ -0,0 +1,381 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_article_list.xml b/app/src/main/res/layout/activity_article_list.xml new file mode 100644 index 00000000..d697e84c --- /dev/null +++ b/app/src/main/res/layout/activity_article_list.xml @@ -0,0 +1,19 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_auth.xml b/app/src/main/res/layout/activity_auth.xml new file mode 100644 index 00000000..7215dadd --- /dev/null +++ b/app/src/main/res/layout/activity_auth.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_book_details.xml b/app/src/main/res/layout/activity_book_details.xml new file mode 100644 index 00000000..24b343f5 --- /dev/null +++ b/app/src/main/res/layout/activity_book_details.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_coin.xml b/app/src/main/res/layout/activity_coin.xml index da8d673e..fcf5178b 100644 --- a/app/src/main/res/layout/activity_coin.xml +++ b/app/src/main/res/layout/activity_coin.xml @@ -1,50 +1,65 @@ - + android:orientation="vertical"> - - - - - + android:background="?colorForeground"> + + + + + + + + + style="@style/StateView_Center" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> - - - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_coin_rank.xml b/app/src/main/res/layout/activity_coin_rank.xml index 2d3005af..3f56de85 100644 --- a/app/src/main/res/layout/activity_coin_rank.xml +++ b/app/src/main/res/layout/activity_coin_rank.xml @@ -4,25 +4,28 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> + app:abc_titleText="@string/leaderboard" /> + style="@style/StateView_Center" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"> - - - + diff --git a/app/src/main/res/layout/activity_collection.xml b/app/src/main/res/layout/activity_collection.xml index 98b347ef..83c797e9 100644 --- a/app/src/main/res/layout/activity_collection.xml +++ b/app/src/main/res/layout/activity_collection.xml @@ -3,7 +3,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> - - - - + android:orientation="vertical"> - + android:layout_marginStart="15dp" + android:gravity="center" + android:text="@string/something_went_wrong" + android:textColor="?colorTextSurface" + android:textSize="36sp" + android:typeface="monospace" /> - + + + + android:textColor="?colorTextSurfaceAlpha" + android:textSize="12sp" + tools:text="log" + android:typeface="monospace" /> - + - - - + android:layout_height="44dp" + android:layout_marginStart="15dp" + android:layout_marginTop="15dp" + android:layout_marginEnd="15dp" + android:layout_marginBottom="15dp" + android:orientation="horizontal"> - + android:layout_weight="1" + android:background="@drawable/bg_crash_exit" + android:gravity="center" + android:text="@string/close" + android:textColor="?colorTextInvert" /> - - - - - - - + android:layout_weight="1" + android:background="@drawable/bg_crash_restart" + android:gravity="center" + android:text="@string/reboot" + android:textColor="?colorTextInvert" + android:textSize="15sp" /> - - - + - + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_host_interrupt.xml b/app/src/main/res/layout/activity_host_interrupt.xml index 98b347ef..83c797e9 100644 --- a/app/src/main/res/layout/activity_host_interrupt.xml +++ b/app/src/main/res/layout/activity_host_interrupt.xml @@ -3,7 +3,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> - - - - - + app:abc_titleText="@string/body_of_knowledge" /> + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_language.xml b/app/src/main/res/layout/activity_language.xml new file mode 100644 index 00000000..6ab63820 --- /dev/null +++ b/app/src/main/res/layout/activity_language.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml deleted file mode 100644 index e76cecee..00000000 --- a/app/src/main/res/layout/activity_login.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 8269a2b4..35f8cd95 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,9 +1,10 @@ - \ No newline at end of file diff --git a/app/src/main/res/layout/activity_message.xml b/app/src/main/res/layout/activity_message.xml new file mode 100644 index 00000000..83c797e9 --- /dev/null +++ b/app/src/main/res/layout/activity_message.xml @@ -0,0 +1,21 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_mine_share.xml b/app/src/main/res/layout/activity_mine_share.xml index f3c22089..aa82768c 100644 --- a/app/src/main/res/layout/activity_mine_share.xml +++ b/app/src/main/res/layout/activity_mine_share.xml @@ -4,25 +4,25 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> + app:abc_titleText="@string/my_sharing" /> + style="@style/StateView_Center"> - - - + diff --git a/app/src/main/res/layout/activity_open.xml b/app/src/main/res/layout/activity_open.xml index 41908229..60ed927f 100644 --- a/app/src/main/res/layout/activity_open.xml +++ b/app/src/main/res/layout/activity_open.xml @@ -4,25 +4,25 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> + app:abc_titleText="@string/open_source_projects" /> + style="@style/StateView_Center"> - - - + diff --git a/app/src/main/res/layout/activity_quick_login.xml b/app/src/main/res/layout/activity_quick_login.xml new file mode 100644 index 00000000..25f0f3be --- /dev/null +++ b/app/src/main/res/layout/activity_quick_login.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_read_later.xml b/app/src/main/res/layout/activity_read_later.xml index ea0259f2..82e3aa91 100644 --- a/app/src/main/res/layout/activity_read_later.xml +++ b/app/src/main/res/layout/activity_read_later.xml @@ -4,33 +4,34 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> + app:abc_titleText="@string/my_bookmarks" + app:abc_rightText="@string/clear" /> + style="@style/StateView_Center"> - - + tools:listitem="@layout/rv_item_read_later" /> - + diff --git a/app/src/main/res/layout/activity_read_record.xml b/app/src/main/res/layout/activity_read_record.xml new file mode 100644 index 00000000..4e158825 --- /dev/null +++ b/app/src/main/res/layout/activity_read_record.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_scan.xml b/app/src/main/res/layout/activity_scan.xml new file mode 100644 index 00000000..4715d0b5 --- /dev/null +++ b/app/src/main/res/layout/activity_scan.xml @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_search.xml b/app/src/main/res/layout/activity_search.xml index e02e497a..0871680c 100644 --- a/app/src/main/res/layout/activity_search.xml +++ b/app/src/main/res/layout/activity_search.xml @@ -7,27 +7,15 @@ android:orientation="vertical" tools:context=".module.main.activity.MainActivity"> - + + android:layout_height="match_parent" + android:background="?colorBackground" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_setting.xml b/app/src/main/res/layout/activity_setting.xml index 5d49ce8b..a4dd3904 100644 --- a/app/src/main/res/layout/activity_setting.xml +++ b/app/src/main/res/layout/activity_setting.xml @@ -9,12 +9,12 @@ + app:abc_titleText="@string/settings" /> - - + android:gravity="center" + android:orientation="horizontal" + android:paddingStart="16dp" + android:paddingEnd="16dp"> - - - - - - - - + android:text="" + android:textColor="?colorTextThird" + android:textSize="@dimen/text_notes" + tools:text="@string/follow_the_system" /> - + - + - + android:gravity="center" + android:orientation="horizontal" + android:paddingStart="16dp" + android:paddingEnd="16dp"> + android:layout_marginEnd="5dp" + android:singleLine="true" + android:text="" + android:textColor="?colorTextThird" + android:textSize="@dimen/text_notes" + tools:text="@string/follow_the_system" /> - + - + - @@ -180,38 +154,38 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/bg_press_color_surface" - android:paddingLeft="16dp" + android:paddingStart="16dp" android:paddingTop="14dp" - android:paddingRight="16dp" + android:paddingEnd="16dp" android:paddingBottom="14dp"> - @@ -220,38 +194,38 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/bg_press_color_surface" - android:paddingLeft="16dp" + android:paddingStart="16dp" android:paddingTop="14dp" - android:paddingRight="16dp" + android:paddingEnd="16dp" android:paddingBottom="14dp"> - @@ -260,38 +234,38 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/bg_press_color_surface" - android:paddingLeft="16dp" + android:paddingStart="16dp" android:paddingTop="14dp" - android:paddingRight="16dp" + android:paddingEnd="16dp" android:paddingBottom="14dp"> - @@ -302,9 +276,9 @@ android:layout_height="wrap_content" android:background="@drawable/bg_press_color_surface" android:orientation="horizontal" - android:paddingLeft="16dp" + android:paddingStart="16dp" android:paddingTop="14dp" - android:paddingRight="16dp" + android:paddingEnd="16dp" android:paddingBottom="14dp"> + tools:text="@string/do_not_intercept" /> + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + tools:text="@string/disabled" /> + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> @@ -402,119 +377,108 @@ android:background="@drawable/bg_press_color_surface" android:gravity="center" android:orientation="horizontal" - android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + tools:text="1.0.0" /> + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> + android:paddingStart="16dp" + android:paddingEnd="16dp"> - - + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> @@ -525,17 +489,17 @@ android:background="@drawable/bg_press_color_surface" android:gravity="center" android:orientation="horizontal" - android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> @@ -544,6 +508,6 @@ - + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_share_article.xml b/app/src/main/res/layout/activity_share_article.xml index 34d7645b..de0094d4 100644 --- a/app/src/main/res/layout/activity_share_article.xml +++ b/app/src/main/res/layout/activity_share_article.xml @@ -3,92 +3,104 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/foreground" + android:background="?colorForeground" android:orientation="vertical"> - - - + android:orientation="vertical"> - + - - - + android:orientation="vertical" + android:padding="16dp"> - + - + - + + + + + + + + + + + - + - diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml new file mode 100644 index 00000000..5ce1551f --- /dev/null +++ b/app/src/main/res/layout/activity_splash.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_user_info.xml b/app/src/main/res/layout/activity_user_info.xml new file mode 100644 index 00000000..72c87557 --- /dev/null +++ b/app/src/main/res/layout/activity_user_info.xml @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_user_page.xml b/app/src/main/res/layout/activity_user_page.xml index 9a79d5b7..9b63e4a9 100644 --- a/app/src/main/res/layout/activity_user_page.xml +++ b/app/src/main/res/layout/activity_user_page.xml @@ -4,21 +4,22 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background"> + android:background="?colorBackground"> + android:tint="?colorMineBlurOverlay" + tools:ignore="UseAppTint" /> + style="@style/StateView_Center"> - - + android:background="?colorTransparent"> - - @@ -74,43 +77,46 @@ android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:text="-----" - android:textColor="@color/basic_ui_action_bar_text" + android:textColor="?colorOnMainOrSurface" android:textSize="@dimen/text_headline" /> + android:alpha="0.8"> - + android:layout_below="@+id/tv_user_name" + android:layout_centerHorizontal="true" + android:orientation="horizontal"> - + - + - + + + @@ -148,8 +154,8 @@ @@ -166,24 +172,24 @@ - + - + - - + - + + android:background="?colorTransparent" + app:abc_rightText="@string/wan_watchword" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_web.xml b/app/src/main/res/layout/activity_web.xml index 3275f021..ada5c889 100644 --- a/app/src/main/res/layout/activity_web.xml +++ b/app/src/main/res/layout/activity_web.xml @@ -4,7 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/foreground" + android:background="?colorBackground" android:orientation="vertical" tools:context=".module.main.activity.MainActivity"> @@ -13,53 +13,66 @@ style="@style/ActionBar" app:ab_titleBarLayout="@layout/action_bar_web" /> - + android:layout_weight="1" /> - + + + android:layout_height="@dimen/bottom_bar_height" + android:gravity="center" + android:orientation="horizontal"> - + - + - + - + - + - + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_add_host.xml b/app/src/main/res/layout/dialog_add_host.xml index 3939158a..09f5ed33 100644 --- a/app/src/main/res/layout/dialog_add_host.xml +++ b/app/src/main/res/layout/dialog_add_host.xml @@ -2,8 +2,8 @@ + android:text="@string/domain_name" + android:textColor="?colorTextSecond" + android:textSize="@dimen/text_title" /> + android:hint="@string/host_input_hint" /> + android:layout_height="@dimen/line" + android:background="?colorLine" /> + android:background="?colorBackground" /> diff --git a/app/src/main/res/layout/dialog_advert.xml b/app/src/main/res/layout/dialog_advert.xml new file mode 100644 index 00000000..e7e3ce9d --- /dev/null +++ b/app/src/main/res/layout/dialog_advert.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_article_share.xml b/app/src/main/res/layout/dialog_article_share.xml new file mode 100644 index 00000000..e77ecb91 --- /dev/null +++ b/app/src/main/res/layout/dialog_article_share.xml @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_card_share.xml b/app/src/main/res/layout/dialog_card_share.xml new file mode 100644 index 00000000..db2a6d52 --- /dev/null +++ b/app/src/main/res/layout/dialog_card_share.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_choice_daily_news_platform.xml b/app/src/main/res/layout/dialog_choice_daily_news_platform.xml new file mode 100644 index 00000000..326078cc --- /dev/null +++ b/app/src/main/res/layout/dialog_choice_daily_news_platform.xml @@ -0,0 +1,17 @@ + + + + + + diff --git a/app/src/main/res/layout/dialog_copied_link.xml b/app/src/main/res/layout/dialog_copied_link.xml index 951d379c..318a65d2 100644 --- a/app/src/main/res/layout/dialog_copied_link.xml +++ b/app/src/main/res/layout/dialog_copied_link.xml @@ -25,17 +25,18 @@ android:padding="5dp" android:scaleType="fitCenter" android:src="@drawable/ic_close" - android:tint="@color/text_invert" /> + android:tint="?colorIconOnMain" + tools:ignore="UseAppTint" /> diff --git a/app/src/main/res/layout/dialog_daily.xml b/app/src/main/res/layout/dialog_daily.xml new file mode 100644 index 00000000..8cee9bf1 --- /dev/null +++ b/app/src/main/res/layout/dialog_daily.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_download.xml b/app/src/main/res/layout/dialog_download.xml index 9c74a5c9..fb31ce34 100644 --- a/app/src/main/res/layout/dialog_download.xml +++ b/app/src/main/res/layout/dialog_download.xml @@ -10,7 +10,7 @@ @@ -82,10 +82,10 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" - android:layout_marginLeft="5dp" + android:layout_marginStart="5dp" android:visibility="invisible" - android:layout_marginRight="5dp" - android:textColor="@color/text_second" + android:layout_marginEnd="5dp" + android:textColor="?colorTextSecond" android:textSize="@dimen/text_notes" android:text="|" /> @@ -94,7 +94,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" - android:textColor="@color/text_second" + android:textColor="?colorTextSecond" android:textSize="@dimen/text_notes" tools:text="40M" /> diff --git a/app/src/main/res/layout/dialog_edit_collect_link.xml b/app/src/main/res/layout/dialog_edit_collect_link.xml index 58cef1f5..a70452c1 100644 --- a/app/src/main/res/layout/dialog_edit_collect_link.xml +++ b/app/src/main/res/layout/dialog_edit_collect_link.xml @@ -3,8 +3,8 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="top" - android:layout_margin="@dimen/margin_def" + android:layout_gravity="bottom" + android:padding="@dimen/margin_def" android:orientation="vertical"> @@ -47,30 +47,30 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10dp" - android:text="网站链接" - android:textColor="@color/text_second" + android:text="@string/links_to_the_site" + android:textColor="?colorTextSecond" android:textSize="@dimen/text_auxiliary" /> + tools:text="https://wanandroid.com" /> + android:background="?colorLine" /> + android:background="?colorBackground" /> diff --git a/app/src/main/res/layout/dialog_guide_btn.xml b/app/src/main/res/layout/dialog_guide_btn.xml new file mode 100644 index 00000000..48c4651e --- /dev/null +++ b/app/src/main/res/layout/dialog_guide_btn.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_guide_tip.xml b/app/src/main/res/layout/dialog_guide_tip.xml new file mode 100644 index 00000000..c53e125e --- /dev/null +++ b/app/src/main/res/layout/dialog_guide_tip.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_image_list_preview.xml b/app/src/main/res/layout/dialog_image_list_preview.xml new file mode 100644 index 00000000..5c6adfbd --- /dev/null +++ b/app/src/main/res/layout/dialog_image_list_preview.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_image_menu.xml b/app/src/main/res/layout/dialog_image_menu.xml new file mode 100644 index 00000000..71a7d25a --- /dev/null +++ b/app/src/main/res/layout/dialog_image_menu.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_image_preview.xml b/app/src/main/res/layout/dialog_image_preview.xml new file mode 100644 index 00000000..f16f7517 --- /dev/null +++ b/app/src/main/res/layout/dialog_image_preview.xml @@ -0,0 +1,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_info_edit.xml b/app/src/main/res/layout/dialog_info_edit.xml new file mode 100644 index 00000000..0d5bc444 --- /dev/null +++ b/app/src/main/res/layout/dialog_info_edit.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_password.xml b/app/src/main/res/layout/dialog_password.xml new file mode 100644 index 00000000..27cb7de8 --- /dev/null +++ b/app/src/main/res/layout/dialog_password.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_privacy_policy.xml b/app/src/main/res/layout/dialog_privacy_policy.xml new file mode 100644 index 00000000..a081320d --- /dev/null +++ b/app/src/main/res/layout/dialog_privacy_policy.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_qrcode_share.xml b/app/src/main/res/layout/dialog_qrcode_share.xml deleted file mode 100644 index 8b8bd194..00000000 --- a/app/src/main/res/layout/dialog_qrcode_share.xml +++ /dev/null @@ -1,166 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_read_later_notification.xml b/app/src/main/res/layout/dialog_read_later_notification.xml new file mode 100644 index 00000000..f0a04a86 --- /dev/null +++ b/app/src/main/res/layout/dialog_read_later_notification.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_web.xml b/app/src/main/res/layout/dialog_web.xml index 3e4a23e0..c03b970d 100644 --- a/app/src/main/res/layout/dialog_web.xml +++ b/app/src/main/res/layout/dialog_web.xml @@ -4,16 +4,16 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - + tools:background="?colorDialogBg" /> + android:src="@drawable/ic_read_later_added" + android:tint="?colorOnMainOrSurface" + tools:ignore="UseAppTint" /> + android:src="@drawable/ic_close" + android:tint="?colorIconSurface" + tools:ignore="UseAppTint" /> diff --git a/app/src/main/res/layout/dialog_web_guide.xml b/app/src/main/res/layout/dialog_web_guide.xml index 220dd36b..5e18f68f 100644 --- a/app/src/main/res/layout/dialog_web_guide.xml +++ b/app/src/main/res/layout/dialog_web_guide.xml @@ -4,51 +4,78 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" - tools:background="@color/dialog_bg"> + android:paddingBottom="@dimen/bottom_bar_height" + tools:background="?colorDialogBg"> - - - - - + android:layout_above="@id/dialog_web_guide_tv_know" + android:layout_alignParentTop="true" + android:gravity="center" + android:orientation="vertical"> + + + + + + + + + + + + + + + android:layout_marginBottom="@dimen/margin_def" + android:paddingStart="50dp" + android:paddingEnd="50dp" + android:text="@string/i_know" /> \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_web_menu.xml b/app/src/main/res/layout/dialog_web_menu.xml index 40143ab6..f0281bd7 100644 --- a/app/src/main/res/layout/dialog_web_menu.xml +++ b/app/src/main/res/layout/dialog_web_menu.xml @@ -8,6 +8,19 @@ android:orientation="vertical" android:paddingTop="@dimen/round_radius"> + + @@ -18,28 +31,26 @@ android:layout_weight="1" android:orientation="vertical" android:padding="10dp" - tools:background="@color/foreground" tools:layout_width="100dp"> + android:src="@drawable/ic_share" + android:tint="?colorIconSurface" /> @@ -50,7 +61,6 @@ android:layout_weight="1" android:orientation="vertical" android:padding="10dp" - tools:background="@color/foreground" tools:layout_width="100dp"> + android:tint="?colorIconSurface" /> @@ -82,7 +91,6 @@ android:layout_weight="1" android:orientation="vertical" android:padding="10dp" - tools:background="@color/foreground" tools:layout_width="100dp"> + android:tint="?colorIconSurface" /> @@ -115,7 +122,6 @@ android:layout_weight="1" android:orientation="vertical" android:padding="10dp" - tools:background="@color/foreground" tools:layout_width="100dp"> + android:tint="?colorIconSurface" /> @@ -153,28 +158,56 @@ android:layout_weight="1" android:orientation="vertical" android:padding="10dp" - tools:background="@color/foreground" tools:layout_width="100dp"> + android:src="@drawable/ic_browser" + android:tint="?colorIconSurface" /> + + + + + + + + @@ -185,7 +218,6 @@ android:layout_weight="1" android:orientation="vertical" android:padding="10dp" - tools:background="@color/foreground" tools:layout_width="100dp"> + android:tint="?colorIconSurface" /> + + + + + + + + + + + + + + + + + + + + @@ -218,28 +315,57 @@ android:layout_weight="1" android:orientation="vertical" android:padding="10dp" - tools:background="@color/foreground" tools:layout_width="100dp"> + android:src="@drawable/ic_refresh" + android:tint="?colorIconSurface" /> + + + + + + + + @@ -250,7 +376,6 @@ android:layout_weight="1" android:orientation="vertical" android:padding="10dp" - tools:background="@color/foreground" tools:layout_width="100dp"> + android:tint="?colorIconSurface" /> @@ -280,9 +404,9 @@ + android:background="?colorLine" /> + android:tint="?colorIconSurface" + tools:ignore="UseAppTint" /> - + + + + - + + + + + + + + + - + - - - - - + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_web_quick.xml b/app/src/main/res/layout/dialog_web_quick.xml index 700f680d..d8e473f3 100644 --- a/app/src/main/res/layout/dialog_web_quick.xml +++ b/app/src/main/res/layout/dialog_web_quick.xml @@ -2,28 +2,49 @@ + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_web_share.xml b/app/src/main/res/layout/dialog_web_share.xml index eed00e35..e3128f83 100644 --- a/app/src/main/res/layout/dialog_web_share.xml +++ b/app/src/main/res/layout/dialog_web_share.xml @@ -14,8 +14,8 @@ android:layout_marginBottom="@dimen/round_radius" android:gravity="center" android:singleLine="true" - android:text="选择分享方式" - android:textColor="@color/text_surface" + android:text="@string/choose_how_you_want_to_share" + android:textColor="?colorTextSurface" android:textSize="@dimen/text_title" /> + android:padding="10dp"> + android:tint="?colorIconSurface" /> @@ -60,8 +59,7 @@ android:layout_width="90dp" android:layout_height="wrap_content" android:orientation="vertical" - android:padding="10dp" - tools:background="@color/foreground"> + android:padding="10dp"> + android:tint="?colorIconSurface" /> @@ -94,7 +92,7 @@ android:layout_width="match_parent" android:layout_height="1dp" android:layout_marginTop="@dimen/round_radius" - android:background="@color/line" /> + android:background="?colorLine" /> + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> diff --git a/app/src/main/res/layout/dialog_web_vp_item.xml b/app/src/main/res/layout/dialog_web_vp_item.xml index 89e7eedc..a76f3e0e 100644 --- a/app/src/main/res/layout/dialog_web_vp_item.xml +++ b/app/src/main/res/layout/dialog_web_vp_item.xml @@ -4,25 +4,25 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:background="@color/dialog_bg"> + tools:background="?colorDialogBg"> - - - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_article_list.xml b/app/src/main/res/layout/fragment_article_list.xml new file mode 100644 index 00000000..a978dc24 --- /dev/null +++ b/app/src/main/res/layout/fragment_article_list.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_book.xml b/app/src/main/res/layout/fragment_book.xml new file mode 100644 index 00000000..5accb8b9 --- /dev/null +++ b/app/src/main/res/layout/fragment_book.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_bookmark.xml b/app/src/main/res/layout/fragment_bookmark.xml new file mode 100644 index 00000000..085c5ef0 --- /dev/null +++ b/app/src/main/res/layout/fragment_bookmark.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_collection_article.xml b/app/src/main/res/layout/fragment_collection_article.xml index 9677075a..23840eac 100644 --- a/app/src/main/res/layout/fragment_collection_article.xml +++ b/app/src/main/res/layout/fragment_collection_article.xml @@ -3,19 +3,19 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> + style="@style/StateView_Center"> - - - + diff --git a/app/src/main/res/layout/fragment_collection_link.xml b/app/src/main/res/layout/fragment_collection_link.xml index 9677075a..23840eac 100644 --- a/app/src/main/res/layout/fragment_collection_link.xml +++ b/app/src/main/res/layout/fragment_collection_link.xml @@ -3,19 +3,19 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> + style="@style/StateView_Center"> - - - + diff --git a/app/src/main/res/layout/fragment_explore.xml b/app/src/main/res/layout/fragment_explore.xml new file mode 100644 index 00000000..3d6f3adb --- /dev/null +++ b/app/src/main/res/layout/fragment_explore.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 26b9fc23..282105f1 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -1,41 +1,117 @@ - - + - + - + + + + + android:layout_height="match_parent" + app:bdl_closeHeight="@dimen/bottom_bar_height" + app:bdl_enable="true" + app:bdl_open="true" + app:bdl_openTopMargin="0dp" + tools:bdl_open="true"> + + - + android:layout_height="match_parent"> + + + + + + + + + + + + + + + + + + + + + + + + - + - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_host_interrupt.xml b/app/src/main/res/layout/fragment_host_interrupt.xml index b964b7ac..799a132c 100644 --- a/app/src/main/res/layout/fragment_host_interrupt.xml +++ b/app/src/main/res/layout/fragment_host_interrupt.xml @@ -4,16 +4,16 @@ android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> - - - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_knowledge_article.xml b/app/src/main/res/layout/fragment_knowledge_article.xml index 01f25009..a978dc24 100644 --- a/app/src/main/res/layout/fragment_knowledge_article.xml +++ b/app/src/main/res/layout/fragment_knowledge_article.xml @@ -2,15 +2,15 @@ + style="@style/StateView_Center"> - + android:background="?colorBackground"> - - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_knowledge_navigation.xml b/app/src/main/res/layout/fragment_knowledge_navigation.xml index 98b347ef..83c797e9 100644 --- a/app/src/main/res/layout/fragment_knowledge_navigation.xml +++ b/app/src/main/res/layout/fragment_knowledge_navigation.xml @@ -3,7 +3,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> - + style="@style/StateView_Center"> - - - + diff --git a/app/src/main/res/layout/fragment_login.xml b/app/src/main/res/layout/fragment_login.xml index 06d2971f..bb884dec 100644 --- a/app/src/main/res/layout/fragment_login.xml +++ b/app/src/main/res/layout/fragment_login.xml @@ -1,6 +1,7 @@ @@ -19,23 +20,27 @@ android:rotation="180" android:visibility="invisible" android:scaleType="fitCenter" - android:src="@drawable/ic_next"/> + android:tint="?colorIconMain" + android:src="@drawable/ic_next" + tools:ignore="UseAppTint" /> + android:textColor="?colorTextMain" + android:text="@string/go_to_sign_up"/> + android:tint="?colorIconMain" + android:src="@drawable/ic_next" + tools:ignore="UseAppTint" /> @@ -43,32 +48,27 @@ android:id="@+id/piv_login_account" android:layout_width="match_parent" android:layout_height="44dp" - android:layout_marginLeft="60dp" + android:layout_marginStart="60dp" android:layout_marginTop="100dp" - android:layout_marginRight="60dp" /> + android:layout_marginEnd="60dp" /> + android:layout_marginEnd="60dp" /> diff --git a/app/src/main/res/layout/fragment_main.xml b/app/src/main/res/layout/fragment_main.xml index 13a8efe9..19e3ae85 100644 --- a/app/src/main/res/layout/fragment_main.xml +++ b/app/src/main/res/layout/fragment_main.xml @@ -1,201 +1,38 @@ - - - - - - - - - - - - + android:background="?colorSurface"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:id="@+id/ll_bottom_bar" + android:layout_width="match_parent" + android:layout_height="@dimen/bottom_bar_height" + android:baselineAligned="false" + android:orientation="horizontal" /> + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_message_readed.xml b/app/src/main/res/layout/fragment_message_readed.xml new file mode 100644 index 00000000..fb4b998a --- /dev/null +++ b/app/src/main/res/layout/fragment_message_readed.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_message_unread.xml b/app/src/main/res/layout/fragment_message_unread.xml new file mode 100644 index 00000000..fb4b998a --- /dev/null +++ b/app/src/main/res/layout/fragment_message_unread.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_mine.xml b/app/src/main/res/layout/fragment_mine.xml index cc1fd59c..0688f07e 100644 --- a/app/src/main/res/layout/fragment_mine.xml +++ b/app/src/main/res/layout/fragment_mine.xml @@ -4,23 +4,24 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/foreground"> + android:background="?colorForeground"> + android:tint="?colorMineBlurOverlay" + tools:ignore="UseAppTint" /> - - @@ -57,43 +58,17 @@ android:layout_below="@+id/civ_user_icon" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" - android:textColor="@color/basic_ui_action_bar_text" + android:textColor="?colorOnMainOrSurface" android:textSize="@dimen/text_headline" tools:text="GOWEII" /> - - - - - - - - @@ -132,15 +107,15 @@ @@ -162,45 +137,46 @@ android:background="@drawable/bg_press_color_surface" android:gravity="center" android:orientation="horizontal" - android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + android:tint="?colorIconMain" + tools:ignore="UseAppTint" /> + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> @@ -211,34 +187,35 @@ android:background="@drawable/bg_press_color_surface" android:gravity="center" android:orientation="horizontal" - android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + android:tint="?colorIconMain" + tools:ignore="UseAppTint" /> + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> @@ -249,34 +226,35 @@ android:background="@drawable/bg_press_color_surface" android:gravity="center" android:orientation="horizontal" - android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + android:tint="?colorIconMain" + tools:ignore="UseAppTint" /> + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> @@ -287,34 +265,74 @@ android:background="@drawable/bg_press_color_surface" android:gravity="center" android:orientation="horizontal" - android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + android:tint="?colorIconMain" + tools:ignore="UseAppTint" /> + + + + + + + + + + + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> @@ -325,34 +343,35 @@ android:background="@drawable/bg_press_color_surface" android:gravity="center" android:orientation="horizontal" - android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + android:tint="?colorIconMain" + tools:ignore="UseAppTint" /> + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> @@ -363,34 +382,44 @@ android:background="@drawable/bg_press_color_surface" android:gravity="center" android:orientation="horizontal" - android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + android:tint="?colorIconMain" + tools:ignore="UseAppTint" /> + + + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> @@ -401,34 +430,34 @@ android:background="@drawable/bg_press_color_surface" android:gravity="center" android:orientation="horizontal" - android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> + android:tint="?colorIconMain" + tools:ignore="UseAppTint" /> + android:src="@drawable/ic_enter" + android:tint="?colorIconThird" /> @@ -436,14 +465,14 @@ - + - + - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_project.xml b/app/src/main/res/layout/fragment_project.xml index 2ba2ea1c..179c0ffb 100644 --- a/app/src/main/res/layout/fragment_project.xml +++ b/app/src/main/res/layout/fragment_project.xml @@ -3,7 +3,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> - + style="@style/StateView_Center"> - + android:background="?colorBackground"> - - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_question.xml b/app/src/main/res/layout/fragment_question.xml new file mode 100644 index 00000000..7963def2 --- /dev/null +++ b/app/src/main/res/layout/fragment_question.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_register.xml b/app/src/main/res/layout/fragment_register.xml index 7f0412f4..ee68aae8 100644 --- a/app/src/main/res/layout/fragment_register.xml +++ b/app/src/main/res/layout/fragment_register.xml @@ -1,41 +1,46 @@ + android:layout_height="match_parent" + android:orientation="vertical"> + android:gravity="center"> + android:src="@drawable/ic_next" + android:tint="?colorIconMain" + tools:ignore="UseAppTint" /> + android:layout_marginStart="10dp" + android:layout_marginEnd="10dp" + android:text="@string/go_to_sign_in" + android:textColor="?colorTextMain" + android:textSize="@dimen/text_content" /> + android:src="@drawable/ic_next" + android:tint="?colorIconMain" + android:visibility="invisible" + tools:ignore="UseAppTint" /> @@ -43,41 +48,36 @@ android:id="@+id/piv_register_account" android:layout_width="match_parent" android:layout_height="44dp" - android:layout_marginLeft="60dp" + android:layout_marginStart="60dp" android:layout_marginTop="100dp" - android:layout_marginRight="60dp" /> + android:layout_marginEnd="60dp" /> + android:layout_marginEnd="60dp" /> + android:layout_marginEnd="60dp" /> diff --git a/app/src/main/res/layout/fragment_search_history.xml b/app/src/main/res/layout/fragment_search_history.xml index e112f30a..74c2ee0e 100644 --- a/app/src/main/res/layout/fragment_search_history.xml +++ b/app/src/main/res/layout/fragment_search_history.xml @@ -3,26 +3,26 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/foreground" + android:background="?colorForeground" android:orientation="vertical"> - + android:layout_gravity="center_vertical"> - + style="@style/StateView_Center"> - + android:background="?colorBackground"> - - + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_user_article.xml b/app/src/main/res/layout/fragment_user_article.xml index b24b0133..8e88f42a 100644 --- a/app/src/main/res/layout/fragment_user_article.xml +++ b/app/src/main/res/layout/fragment_user_article.xml @@ -4,25 +4,25 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> + app:abc_titleText="@string/square" /> + style="@style/StateView_Center"> - - - + diff --git a/app/src/main/res/layout/fragment_wx.xml b/app/src/main/res/layout/fragment_wx.xml index 4ed93578..75905708 100644 --- a/app/src/main/res/layout/fragment_wx.xml +++ b/app/src/main/res/layout/fragment_wx.xml @@ -3,7 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/background" + android:background="?colorBackground" android:orientation="vertical"> - + style="@style/StateView_Center"> - + android:background="?colorBackground"> - - + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_about_me_info.xml b/app/src/main/res/layout/layout_about_me_info.xml new file mode 100644 index 00000000..bfd175de --- /dev/null +++ b/app/src/main/res/layout/layout_about_me_info.xml @@ -0,0 +1,253 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_about_me_sponsor.xml b/app/src/main/res/layout/layout_about_me_sponsor.xml new file mode 100644 index 00000000..62721285 --- /dev/null +++ b/app/src/main/res/layout/layout_about_me_sponsor.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_agent_web_error.xml b/app/src/main/res/layout/layout_agent_web_error.xml deleted file mode 100644 index 39a381e9..00000000 --- a/app/src/main/res/layout/layout_agent_web_error.xml +++ /dev/null @@ -1,8 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_app_qrcode_share.xml b/app/src/main/res/layout/layout_app_qrcode_share.xml new file mode 100644 index 00000000..88526cd8 --- /dev/null +++ b/app/src/main/res/layout/layout_app_qrcode_share.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/layout_article_collect_view_checked.xml b/app/src/main/res/layout/layout_article_collect_view_checked.xml new file mode 100644 index 00000000..f21ed0ce --- /dev/null +++ b/app/src/main/res/layout/layout_article_collect_view_checked.xml @@ -0,0 +1,27 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_article_collect_view_unchecked.xml b/app/src/main/res/layout/layout_article_collect_view_unchecked.xml new file mode 100644 index 00000000..9903c9c5 --- /dev/null +++ b/app/src/main/res/layout/layout_article_collect_view_unchecked.xml @@ -0,0 +1,29 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_article_qrcode_share.xml b/app/src/main/res/layout/layout_article_qrcode_share.xml new file mode 100644 index 00000000..0d1ab24f --- /dev/null +++ b/app/src/main/res/layout/layout_article_qrcode_share.xml @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/layout_brvah_load_more.xml b/app/src/main/res/layout/layout_brvah_load_more.xml new file mode 100644 index 00000000..87effc3b --- /dev/null +++ b/app/src/main/res/layout/layout_brvah_load_more.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_collect_view_checked.xml b/app/src/main/res/layout/layout_collect_view_checked.xml index 923217f1..e1d2dd22 100644 --- a/app/src/main/res/layout/layout_collect_view_checked.xml +++ b/app/src/main/res/layout/layout_collect_view_checked.xml @@ -3,12 +3,9 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - xmlns:tools="http://schemas.android.com/tools" - tools:layout_width="150dp" - tools:layout_height="150dp" app:heart_centerX="-0.5" app:heart_centerY="-0.5" - app:heart_color="@color/accent" - app:heart_edgeColor="@color/accent" - app:heart_strokeColor="@color/transparent" + app:heart_color="?colorHeartOuter" + app:heart_edgeColor="?colorHeartOuter" + app:heart_strokeColor="?colorTransparent" app:heart_strokeWidth="0dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_collect_view_unchecked.xml b/app/src/main/res/layout/layout_collect_view_unchecked.xml index 26039340..aa579be3 100644 --- a/app/src/main/res/layout/layout_collect_view_unchecked.xml +++ b/app/src/main/res/layout/layout_collect_view_unchecked.xml @@ -1,14 +1,11 @@ \ No newline at end of file + app:heart_color="?colorSurface" + app:heart_edgeColor="?colorSurface" + app:heart_strokeColor="?colorHeartOuter" + app:heart_strokeWidth="1dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_shici_refresh_header.xml b/app/src/main/res/layout/layout_shici_refresh_header.xml new file mode 100644 index 00000000..6a0db6b8 --- /dev/null +++ b/app/src/main/res/layout/layout_shici_refresh_header.xml @@ -0,0 +1,34 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_state_error.xml b/app/src/main/res/layout/layout_state_empty_center.xml similarity index 64% rename from app/src/main/res/layout/layout_state_error.xml rename to app/src/main/res/layout/layout_state_empty_center.xml index af62f5a1..1298ce85 100644 --- a/app/src/main/res/layout/layout_state_error.xml +++ b/app/src/main/res/layout/layout_state_empty_center.xml @@ -1,5 +1,6 @@ @@ -7,22 +8,27 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:alpha="0.3" + android:gravity="center" android:layout_gravity="center" android:orientation="vertical"> + android:tint="?colorIconThird" + android:src="@drawable/ic_empty" + tools:ignore="UseAppTint" /> + android:text="@string/nothing" + android:textColor="?colorTextThird" /> diff --git a/app/src/main/res/layout/layout_state_empty_top.xml b/app/src/main/res/layout/layout_state_empty_top.xml new file mode 100644 index 00000000..380bcb55 --- /dev/null +++ b/app/src/main/res/layout/layout_state_empty_top.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_state_empty.xml b/app/src/main/res/layout/layout_state_error_center.xml similarity index 63% rename from app/src/main/res/layout/layout_state_empty.xml rename to app/src/main/res/layout/layout_state_error_center.xml index 85bb4158..a37da6d9 100644 --- a/app/src/main/res/layout/layout_state_empty.xml +++ b/app/src/main/res/layout/layout_state_error_center.xml @@ -1,5 +1,6 @@ @@ -7,22 +8,27 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:alpha="0.3" + android:gravity="center" android:layout_gravity="center" android:orientation="vertical"> + android:tint="?colorIconThird" + android:src="@drawable/ic_error" + tools:ignore="UseAppTint" /> + android:gravity="center" + android:text="@string/how_did_it_go_wrong_again" + android:textColor="?colorTextThird" /> diff --git a/app/src/main/res/layout/layout_state_error_top.xml b/app/src/main/res/layout/layout_state_error_top.xml new file mode 100644 index 00000000..c85c056a --- /dev/null +++ b/app/src/main/res/layout/layout_state_error_top.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_state_loading.xml b/app/src/main/res/layout/layout_state_loading_center.xml similarity index 90% rename from app/src/main/res/layout/layout_state_loading.xml rename to app/src/main/res/layout/layout_state_loading_center.xml index 2655872b..54747958 100644 --- a/app/src/main/res/layout/layout_state_loading.xml +++ b/app/src/main/res/layout/layout_state_loading_center.xml @@ -8,7 +8,7 @@ android:layout_width="50dp" android:layout_height="50dp" android:layout_gravity="center" - android:indeterminateTint="@color/main" + android:indeterminateTint="?colorMain" android:indeterminateTintMode="src_atop" /> \ No newline at end of file diff --git a/app/src/main/res/layout/layout_state_loading_top.xml b/app/src/main/res/layout/layout_state_loading_top.xml new file mode 100644 index 00000000..9cfb6612 --- /dev/null +++ b/app/src/main/res/layout/layout_state_loading_top.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_tts_notification_big.xml b/app/src/main/res/layout/layout_tts_notification_big.xml new file mode 100644 index 00000000..507ecfb0 --- /dev/null +++ b/app/src/main/res/layout/layout_tts_notification_big.xml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_tts_notification_small.xml b/app/src/main/res/layout/layout_tts_notification_small.xml new file mode 100644 index 00000000..d60a0751 --- /dev/null +++ b/app/src/main/res/layout/layout_tts_notification_small.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_web_share_qrcode.xml b/app/src/main/res/layout/layout_web_share_qrcode.xml deleted file mode 100644 index 43601fd7..00000000 --- a/app/src/main/res/layout/layout_web_share_qrcode.xml +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_article.xml b/app/src/main/res/layout/rv_item_article.xml index e33c5294..e99dcaa2 100644 --- a/app/src/main/res/layout/rv_item_article.xml +++ b/app/src/main/res/layout/rv_item_article.xml @@ -28,9 +28,9 @@ android:id="@+id/tv_new" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginRight="5dp" - android:text="新" - android:textColor="@color/text_main" + android:layout_marginEnd="5dp" + android:text="@string/tag_new" + android:textColor="?colorTextMain" android:textSize="@dimen/text_notes" /> @@ -40,11 +40,10 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" - android:layout_toRightOf="@+id/ll_new" - android:background="@drawable/bg_press_color_surface_radius_small" - android:textColor="@color/text_second" + android:layout_toEndOf="@+id/ll_new" + android:textColor="@color/selector_text_color_article_author" android:textSize="@dimen/text_notes" - tools:text="林帅并不帅" /> + tools:text="@tools:sample/full_names" /> + tools:text="TAG" /> + tools:text="2025-06-16" /> @@ -88,15 +87,15 @@ + tools:visibility="gone" /> + tools:text="@tools:sample/full_names" /> @@ -150,9 +148,9 @@ android:id="@+id/tv_top" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginRight="5dp" - android:text="置顶" - android:textColor="@color/text_accent" + android:layout_marginEnd="5dp" + android:text="@string/tag_top" + android:textColor="?colorTextAccent" android:textSize="@dimen/text_notes" /> @@ -162,7 +160,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/ll_middle" - android:layout_toRightOf="@+id/ll_top" + android:layout_toEndOf="@+id/ll_top" android:layout_marginTop="10dp" android:orientation="horizontal"> @@ -170,9 +168,9 @@ android:id="@+id/tv_chapter_name" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/text_third" + android:textColor="@color/selector_text_color_article_chapter" android:textSize="@dimen/text_notes" - tools:text="动画效果·Lottie" /> + tools:text="Animation·Lottie" /> @@ -182,6 +180,6 @@ android:layout_height="20dp" tools:rl_checked="false" android:layout_alignBottom="@+id/ll_chapter" - android:layout_alignParentRight="true" /> + android:layout_alignParentEnd="true" /> \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_article_ad.xml b/app/src/main/res/layout/rv_item_article_ad.xml new file mode 100644 index 00000000..4a2c5804 --- /dev/null +++ b/app/src/main/res/layout/rv_item_article_ad.xml @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_article_share_cover.xml b/app/src/main/res/layout/rv_item_article_share_cover.xml new file mode 100644 index 00000000..2ab21e2e --- /dev/null +++ b/app/src/main/res/layout/rv_item_article_share_cover.xml @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_book.xml b/app/src/main/res/layout/rv_item_book.xml new file mode 100644 index 00000000..43d66592 --- /dev/null +++ b/app/src/main/res/layout/rv_item_book.xml @@ -0,0 +1,27 @@ + + + + + + diff --git a/app/src/main/res/layout/rv_item_book_chapter.xml b/app/src/main/res/layout/rv_item_book_chapter.xml new file mode 100644 index 00000000..48ef6fe2 --- /dev/null +++ b/app/src/main/res/layout/rv_item_book_chapter.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/rv_item_bookmark.xml b/app/src/main/res/layout/rv_item_bookmark.xml new file mode 100644 index 00000000..295a8b13 --- /dev/null +++ b/app/src/main/res/layout/rv_item_bookmark.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/rv_item_choice_daily_news_platform.xml b/app/src/main/res/layout/rv_item_choice_daily_news_platform.xml new file mode 100644 index 00000000..8e1d90ba --- /dev/null +++ b/app/src/main/res/layout/rv_item_choice_daily_news_platform.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_coin_rank.xml b/app/src/main/res/layout/rv_item_coin_rank.xml index db32ba0f..9caef4f6 100644 --- a/app/src/main/res/layout/rv_item_coin_rank.xml +++ b/app/src/main/res/layout/rv_item_coin_rank.xml @@ -3,7 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/surface"> + android:background="?colorSurface"> @@ -53,11 +53,11 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" - android:layout_marginLeft="10dp" - android:layout_toRightOf="@+id/fl_index" + android:layout_marginStart="10dp" + android:layout_toEndOf="@+id/fl_index" android:includeFontPadding="false" android:lineSpacingExtra="0dp" - android:textColor="@color/text_surface" + android:textColor="?colorTextSurface" android:textSize="@dimen/text_content" tools:text="g**eii" /> @@ -65,10 +65,10 @@ android:id="@+id/tv_coin_count" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" android:layout_centerVertical="true" android:includeFontPadding="false" - android:textColor="@color/text_main" + android:textColor="?colorTextMain" android:textSize="@dimen/text_content" tools:text="520" /> diff --git a/app/src/main/res/layout/rv_item_coin_record.xml b/app/src/main/res/layout/rv_item_coin_record.xml index 1eca6419..3922ac92 100644 --- a/app/src/main/res/layout/rv_item_coin_record.xml +++ b/app/src/main/res/layout/rv_item_coin_record.xml @@ -3,7 +3,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@color/surface" + android:background="?colorSurface" android:padding="16dp"> @@ -24,9 +24,9 @@ android:includeFontPadding="false" android:lineSpacingExtra="0dp" android:layout_marginTop="5dp" - android:textColor="@color/text_second" + android:textColor="?colorTextSecond" android:textSize="@dimen/text_content" - tools:text="签到 积分10+21" /> + tools:text="@string/check_in_points_is" /> diff --git a/app/src/main/res/layout/rv_item_collection_link.xml b/app/src/main/res/layout/rv_item_collection_link.xml index 34ea3984..93cb5965 100644 --- a/app/src/main/res/layout/rv_item_collection_link.xml +++ b/app/src/main/res/layout/rv_item_collection_link.xml @@ -11,55 +11,55 @@ @@ -77,25 +77,25 @@ android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="16dp" - android:layout_marginRight="16dp" - android:textColor="@color/text_surface" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:textColor="?colorTextSurface" android:textSize="@dimen/text_content" - tools:text="关键字关键字关键字关键字关键字关键字关键字关键字关键字关键字" /> + tools:text="@tools:sample/lorem/random" /> + tools:text="@tools:sample/lorem/random" /> diff --git a/app/src/main/res/layout/rv_item_dialog_web_menu.xml b/app/src/main/res/layout/rv_item_dialog_web_menu.xml index d9a861b2..9455e5a2 100644 --- a/app/src/main/res/layout/rv_item_dialog_web_menu.xml +++ b/app/src/main/res/layout/rv_item_dialog_web_menu.xml @@ -5,7 +5,7 @@ android:layout_height="wrap_content" android:orientation="vertical" android:padding="10dp" - tools:background="@color/foreground" + tools:background="?colorForeground" tools:layout_width="100dp"> + android:tint="?colorIconMain" /> + tools:text="@string/open_it_in_a_browser" /> \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_host_interrupt.xml b/app/src/main/res/layout/rv_item_host_interrupt.xml index 1e90608f..606e92a2 100644 --- a/app/src/main/res/layout/rv_item_host_interrupt.xml +++ b/app/src/main/res/layout/rv_item_host_interrupt.xml @@ -4,10 +4,10 @@ android:layout_width="match_parent" android:layout_height="50dp" android:background="@drawable/bg_press_color_surface" - android:paddingLeft="16dp" - android:paddingRight="16dp"> + android:paddingStart="16dp" + android:paddingEnd="16dp"> - + tools:text="wanandroid.com" /> + android:tint="?colorIconThird" + tools:ignore="UseAppTint" /> \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_host_interrupt_footer.xml b/app/src/main/res/layout/rv_item_host_interrupt_footer.xml index 60f55a04..81300a11 100644 --- a/app/src/main/res/layout/rv_item_host_interrupt_footer.xml +++ b/app/src/main/res/layout/rv_item_host_interrupt_footer.xml @@ -1,10 +1,11 @@ + android:paddingStart="16dp" + android:paddingEnd="16dp"> + android:tint="?colorIconSurface" + tools:ignore="UseAppTint" /> \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_knowledge.xml b/app/src/main/res/layout/rv_item_knowledge.xml index b6ed945d..2680fcde 100644 --- a/app/src/main/res/layout/rv_item_knowledge.xml +++ b/app/src/main/res/layout/rv_item_knowledge.xml @@ -4,27 +4,27 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/bg_press_color_foreground" + android:background="@drawable/bg_press_color_surface" android:orientation="vertical"> + tools:text="@tools:sample/full_names" /> diff --git a/app/src/main/res/layout/rv_item_knowledge_child.xml b/app/src/main/res/layout/rv_item_knowledge_child.xml index ee2687e6..d37786a1 100644 --- a/app/src/main/res/layout/rv_item_knowledge_child.xml +++ b/app/src/main/res/layout/rv_item_knowledge_child.xml @@ -5,11 +5,11 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dp" - android:background="@drawable/bg_press_color_foreground_top_radius_max" - android:paddingLeft="15dp" + android:background="@drawable/bg_press_color_surface_top_radius_max" + android:paddingStart="15dp" android:paddingTop="5dp" - android:paddingRight="15dp" + android:paddingEnd="15dp" android:paddingBottom="5dp" - android:textColor="@color/text_third" + android:textColor="?colorTextSurface" android:textSize="@dimen/text_auxiliary" - tools:text="公众号文章" /> \ No newline at end of file + tools:text="@tools:sample/full_names" /> \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_language.xml b/app/src/main/res/layout/rv_item_language.xml new file mode 100644 index 00000000..5a0bfb9c --- /dev/null +++ b/app/src/main/res/layout/rv_item_language.xml @@ -0,0 +1,41 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_message_readed.xml b/app/src/main/res/layout/rv_item_message_readed.xml new file mode 100644 index 00000000..9f458c39 --- /dev/null +++ b/app/src/main/res/layout/rv_item_message_readed.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_message_unread.xml b/app/src/main/res/layout/rv_item_message_unread.xml new file mode 100644 index 00000000..e326b573 --- /dev/null +++ b/app/src/main/res/layout/rv_item_message_unread.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_mine_share_article.xml b/app/src/main/res/layout/rv_item_mine_share_article.xml index 1b3ebbec..675955fd 100644 --- a/app/src/main/res/layout/rv_item_mine_share_article.xml +++ b/app/src/main/res/layout/rv_item_mine_share_article.xml @@ -10,19 +10,19 @@ diff --git a/app/src/main/res/layout/rv_item_open.xml b/app/src/main/res/layout/rv_item_open.xml index 4cb9539f..5de3603e 100644 --- a/app/src/main/res/layout/rv_item_open.xml +++ b/app/src/main/res/layout/rv_item_open.xml @@ -10,7 +10,7 @@ android:id="@+id/tv_project" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textColor="@color/text_surface" + android:textColor="?colorTextSurface" android:textSize="@dimen/text_content" tools:text="goweii/AnyLayer" /> @@ -22,8 +22,8 @@ android:lineSpacingExtra="0dp" android:layout_marginTop="5dp" android:layout_below="@+id/tv_project" - android:textColor="@color/text_second" + android:textColor="?colorTextSecond" android:textSize="@dimen/text_auxiliary" - tools:text="高拓展高自定义性ActionBar,完美替代Android系统默认,完美替代Android系统默认,完美替代Android系统默认" /> + tools:text="@tools:sample/lorem/random" /> \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_readlater.xml b/app/src/main/res/layout/rv_item_read_later.xml similarity index 68% rename from app/src/main/res/layout/rv_item_readlater.xml rename to app/src/main/res/layout/rv_item_read_later.xml index b9211152..100adb2e 100644 --- a/app/src/main/res/layout/rv_item_readlater.xml +++ b/app/src/main/res/layout/rv_item_read_later.xml @@ -17,36 +17,36 @@ android:id="@+id/tv_copy" android:layout_width="wrap_content" android:layout_height="match_parent" - android:background="@color/third" + android:background="?colorThird" android:gravity="center" - android:paddingLeft="16dp" - android:paddingRight="16dp" - android:text="复制链接" - android:textColor="@color/text_invert" + android:paddingStart="16dp" + android:paddingEnd="16dp" + android:text="@string/copy_the_link" + android:textColor="?colorTextInvert" android:textSize="@dimen/text_content" /> @@ -63,23 +63,23 @@ android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginLeft="16dp" - android:layout_marginRight="16dp" - android:textColor="@color/text_surface" + android:layout_marginStart="16dp" + android:layout_marginEnd="16dp" + android:textColor="?colorTextSurface" android:textSize="@dimen/text_content" - tools:text="关键字关键字关键字关键字关键字关键字关键字关键字关键字关键字" /> + tools:text="@tools:sample/lorem/random" /> diff --git a/app/src/main/res/layout/rv_item_read_record.xml b/app/src/main/res/layout/rv_item_read_record.xml new file mode 100644 index 00000000..100adb2e --- /dev/null +++ b/app/src/main/res/layout/rv_item_read_record.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/rv_item_search_history.xml b/app/src/main/res/layout/rv_item_search_history.xml index 2e0db2db..bc0d8c44 100644 --- a/app/src/main/res/layout/rv_item_search_history.xml +++ b/app/src/main/res/layout/rv_item_search_history.xml @@ -10,13 +10,16 @@ android:layout_height="wrap_content" android:layout_margin="5dp" android:background="@drawable/bg_press_color_foreground_top_radius_max" - android:paddingLeft="15dp" + android:paddingStart="15dp" android:paddingTop="5dp" - android:paddingRight="15dp" + android:lines="1" + android:maxLines="1" + android:ellipsize="end" + android:paddingEnd="15dp" android:paddingBottom="5dp" - android:textColor="@color/text_third" + android:textColor="?colorTextThird" android:textSize="@dimen/text_auxiliary" - tools:text="公众号文章" /> + tools:text="@tools:sample/lorem/random" /> + tools:visibility="visible" + tools:ignore="UseAppTint" /> \ No newline at end of file diff --git a/app/src/main/res/layout/rv_item_search_hot.xml b/app/src/main/res/layout/rv_item_search_hot.xml index cbc828b9..f65335b1 100644 --- a/app/src/main/res/layout/rv_item_search_hot.xml +++ b/app/src/main/res/layout/rv_item_search_hot.xml @@ -6,10 +6,13 @@ android:layout_height="wrap_content" android:layout_margin="5dp" android:background="@drawable/bg_press_color_foreground_top_radius_max" - android:paddingLeft="15dp" + android:ellipsize="end" + android:lines="1" + android:maxLines="1" + android:paddingStart="15dp" android:paddingTop="5dp" - android:paddingRight="15dp" + android:paddingEnd="15dp" android:paddingBottom="5dp" - android:textColor="@color/text_third" + android:textColor="?colorTextThird" android:textSize="@dimen/text_auxiliary" - tools:text="公众号文章" /> \ No newline at end of file + tools:text="@tools:sample/full_names" /> \ No newline at end of file diff --git a/app/src/main/res/layout/tab_item_main.xml b/app/src/main/res/layout/tab_item_main.xml new file mode 100644 index 00000000..138e5098 --- /dev/null +++ b/app/src/main/res/layout/tab_item_main.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 036d09bc..120472d1 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_gold.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_gold.xml new file mode 100644 index 00000000..f961acb8 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_gold.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_gold_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_gold_round.xml new file mode 100644 index 00000000..f961acb8 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_gold_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_green.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_green.xml new file mode 100644 index 00000000..8902b56a --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_green.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_green_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_green_round.xml new file mode 100644 index 00000000..8902b56a --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_green_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_pink.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_pink.xml new file mode 100644 index 00000000..7a6aaa27 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_pink.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_pink_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_pink_round.xml new file mode 100644 index 00000000..7a6aaa27 --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_pink_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_red.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_red.xml new file mode 100644 index 00000000..dd43339a --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_red.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_red_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_red_round.xml new file mode 100644 index 00000000..dd43339a --- /dev/null +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_red_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 036d09bc..120472d1 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png index a568efc9..8b271ab7 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_gold.png b/app/src/main/res/mipmap-hdpi/ic_launcher_gold.png new file mode 100644 index 00000000..de31d2e4 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_gold.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_gold_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_gold_foreground.png new file mode 100644 index 00000000..c68076d8 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_gold_foreground.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_gold_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_gold_round.png new file mode 100644 index 00000000..7aac373c Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_gold_round.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_green.png b/app/src/main/res/mipmap-hdpi/ic_launcher_green.png new file mode 100644 index 00000000..97f1a28f Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_green.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_green_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_green_foreground.png new file mode 100644 index 00000000..c68076d8 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_green_foreground.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_green_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_green_round.png new file mode 100644 index 00000000..8e85e02b Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_green_round.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_pink.png b/app/src/main/res/mipmap-hdpi/ic_launcher_pink.png new file mode 100644 index 00000000..50078914 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_pink.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_pink_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_pink_foreground.png new file mode 100644 index 00000000..c68076d8 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_pink_foreground.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_pink_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_pink_round.png new file mode 100644 index 00000000..e25990aa Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_pink_round.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_red.png b/app/src/main/res/mipmap-hdpi/ic_launcher_red.png new file mode 100644 index 00000000..9240a64b Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_red.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_red_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_red_foreground.png new file mode 100644 index 00000000..c68076d8 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_red_foreground.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_red_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_red_round.png new file mode 100644 index 00000000..3bb7c1b5 Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_red_round.png differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index 4d1b7a83..4455cb42 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png index e934ac2a..17eeafa5 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_gold.png b/app/src/main/res/mipmap-mdpi/ic_launcher_gold.png new file mode 100644 index 00000000..0aa207de Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_gold.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_gold_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_gold_foreground.png new file mode 100644 index 00000000..992b1802 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_gold_foreground.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_gold_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_gold_round.png new file mode 100644 index 00000000..5de4c0e3 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_gold_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_green.png b/app/src/main/res/mipmap-mdpi/ic_launcher_green.png new file mode 100644 index 00000000..51198467 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_green.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_green_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_green_foreground.png new file mode 100644 index 00000000..992b1802 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_green_foreground.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_green_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_green_round.png new file mode 100644 index 00000000..57b27761 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_green_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_pink.png b/app/src/main/res/mipmap-mdpi/ic_launcher_pink.png new file mode 100644 index 00000000..531634f6 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_pink.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_pink_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_pink_foreground.png new file mode 100644 index 00000000..992b1802 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_pink_foreground.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_pink_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_pink_round.png new file mode 100644 index 00000000..87693185 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_pink_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_red.png b/app/src/main/res/mipmap-mdpi/ic_launcher_red.png new file mode 100644 index 00000000..827c06f9 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_red.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_red_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_red_foreground.png new file mode 100644 index 00000000..992b1802 Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_red_foreground.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_red_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_red_round.png new file mode 100644 index 00000000..219f09df Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_red_round.png differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index 213e615c..f051cf2c 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png index d9c7d23a..042cbcb6 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_gold.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_gold.png new file mode 100644 index 00000000..8fcef72d Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_gold.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_gold_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_gold_foreground.png new file mode 100644 index 00000000..8e62d7e4 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_gold_foreground.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_gold_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_gold_round.png new file mode 100644 index 00000000..78c5e440 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_gold_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_green.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_green.png new file mode 100644 index 00000000..73ad7dca Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_green.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_green_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_green_foreground.png new file mode 100644 index 00000000..8e62d7e4 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_green_foreground.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_green_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_green_round.png new file mode 100644 index 00000000..f17bb983 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_green_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_pink.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_pink.png new file mode 100644 index 00000000..efa6a820 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_pink.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_pink_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_pink_foreground.png new file mode 100644 index 00000000..8e62d7e4 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_pink_foreground.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_pink_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_pink_round.png new file mode 100644 index 00000000..5b023cd7 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_pink_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_red.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_red.png new file mode 100644 index 00000000..1d635bba Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_red.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_red_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_red_foreground.png new file mode 100644 index 00000000..8e62d7e4 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_red_foreground.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_red_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_red_round.png new file mode 100644 index 00000000..508f2d96 Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_red_round.png differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 4bfe6979..94efb60f 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 6f501c86..7a2f2ad8 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_gold.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_gold.png new file mode 100644 index 00000000..c5cbc782 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_gold.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_gold_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_gold_foreground.png new file mode 100644 index 00000000..60dfa514 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_gold_foreground.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_gold_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_gold_round.png new file mode 100644 index 00000000..b27f58fb Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_gold_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_green.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_green.png new file mode 100644 index 00000000..fb1c6a23 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_green.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_green_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_green_foreground.png new file mode 100644 index 00000000..60dfa514 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_green_foreground.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_green_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_green_round.png new file mode 100644 index 00000000..d6441c5e Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_green_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_pink.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_pink.png new file mode 100644 index 00000000..ed786a86 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_pink.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_pink_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_pink_foreground.png new file mode 100644 index 00000000..60dfa514 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_pink_foreground.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_pink_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_pink_round.png new file mode 100644 index 00000000..6fc22619 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_pink_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_red.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_red.png new file mode 100644 index 00000000..964c9052 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_red.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_red_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_red_foreground.png new file mode 100644 index 00000000..60dfa514 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_red_foreground.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_red_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_red_round.png new file mode 100644 index 00000000..577f5c8e Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_red_round.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index 01fced29..6164daa9 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 2cee39aa..99905e38 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_gold.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_gold.png new file mode 100644 index 00000000..cbfe40cd Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_gold.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_gold_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_gold_foreground.png new file mode 100644 index 00000000..e8eef0d2 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_gold_foreground.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_gold_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_gold_round.png new file mode 100644 index 00000000..befadc2a Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_gold_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_green.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_green.png new file mode 100644 index 00000000..64325452 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_green.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_green_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_green_foreground.png new file mode 100644 index 00000000..e8eef0d2 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_green_foreground.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_green_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_green_round.png new file mode 100644 index 00000000..5c965e1e Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_green_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pink.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pink.png new file mode 100644 index 00000000..5a9e980b Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pink.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pink_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pink_foreground.png new file mode 100644 index 00000000..e8eef0d2 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pink_foreground.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pink_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pink_round.png new file mode 100644 index 00000000..063b9013 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_pink_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_red.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_red.png new file mode 100644 index 00000000..f0c58605 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_red.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_red_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_red_foreground.png new file mode 100644 index 00000000..e8eef0d2 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_red_foreground.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_red_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_red_round.png new file mode 100644 index 00000000..78e91772 Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_red_round.png differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index b0b9c444..6d256217 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/app/src/main/res/resources.properties b/app/src/main/res/resources.properties new file mode 100644 index 00000000..465ad7e1 --- /dev/null +++ b/app/src/main/res/resources.properties @@ -0,0 +1 @@ +unqualifiedResLocale=zh-CN \ No newline at end of file diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml deleted file mode 100644 index c0e501ca..00000000 --- a/app/src/main/res/values-night/colors.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - @color/main - @color/transparent - @color/main - - @color/main - @color/main - - @color/surface_alpha - @color/surface_alpha - @color/surface_alpha - #33000000 - diff --git a/app/src/main/res/values-night/ic_launcher_background.xml b/app/src/main/res/values-night/ic_launcher_background.xml deleted file mode 100644 index 080e6123..00000000 --- a/app/src/main/res/values-night/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - @color/background - \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml new file mode 100644 index 00000000..93134ed1 --- /dev/null +++ b/app/src/main/res/values-night/themes.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes_gold.xml b/app/src/main/res/values-night/themes_gold.xml new file mode 100644 index 00000000..0947fa03 --- /dev/null +++ b/app/src/main/res/values-night/themes_gold.xml @@ -0,0 +1,107 @@ + + + + #D5C591 + #99D5C591 + #B8AF91 + #11C8AF6F + #999A8140 + #F65442 + #3DF48B + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes_green.xml b/app/src/main/res/values-night/themes_green.xml new file mode 100644 index 00000000..6b6775c8 --- /dev/null +++ b/app/src/main/res/values-night/themes_green.xml @@ -0,0 +1,107 @@ + + + + #3DF48B + #993DF48B + #73F8AB + #1173F8AB + #992CA861 + #C8A753 + #73a3f5 + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes_pink.xml b/app/src/main/res/values-night/themes_pink.xml new file mode 100644 index 00000000..192545e9 --- /dev/null +++ b/app/src/main/res/values-night/themes_pink.xml @@ -0,0 +1,107 @@ + + + + #FA85AA + #99FA85AA + #FAA9C2 + #11FAA9C2 + #99AC5B75 + #C8A753 + #3DF48B + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes_red.xml b/app/src/main/res/values-night/themes_red.xml new file mode 100644 index 00000000..c42add56 --- /dev/null +++ b/app/src/main/res/values-night/themes_red.xml @@ -0,0 +1,107 @@ + + + + #FF4242 + #99FF4242 + #FF677B + #11FF877A + #99AF372A + #C8A753 + #3DF48B + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 41e42b35..d19c084c 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -8,4 +8,25 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs_theme.xml b/app/src/main/res/values/attrs_theme.xml new file mode 100644 index 00000000..3d2779a7 --- /dev/null +++ b/app/src/main/res/values/attrs_theme.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml deleted file mode 100644 index 12a56d65..00000000 --- a/app/src/main/res/values/colors.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - @color/main - @color/transparent - @color/main - - @color/main - @color/main - - @color/background_alpha - @color/background_mask - @color/background_mask - #22000000 - #FFA07C - @color/accent - diff --git a/app/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml deleted file mode 100644 index 930f6a4d..00000000 --- a/app/src/main/res/values/ic_launcher_background.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - #4285F4 - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7f0b8df2..e8894190 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,2 @@ - 玩安卓 - + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b599357b..f7d2c02d 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -1,72 +1,47 @@ - - - - - - - - - - - - - + + + + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 00000000..f0dc536a --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes_gold.xml b/app/src/main/res/values/themes_gold.xml new file mode 100644 index 00000000..1b0f3bcf --- /dev/null +++ b/app/src/main/res/values/themes_gold.xml @@ -0,0 +1,111 @@ + + + + #FFDE6D + #99FFDE6D + #EFDEA3 + #11D3AB47 + #999C7922 + #F5422F + #27AE60 + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes_green.xml b/app/src/main/res/values/themes_green.xml new file mode 100644 index 00000000..bf046320 --- /dev/null +++ b/app/src/main/res/values/themes_green.xml @@ -0,0 +1,111 @@ + + + + #27AE60 + #9927AE60 + #3AAF6B + #113AAF6B + #99106835 + #ff9f1c + #4282f4 + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes_pink.xml b/app/src/main/res/values/themes_pink.xml new file mode 100644 index 00000000..530a2935 --- /dev/null +++ b/app/src/main/res/values/themes_pink.xml @@ -0,0 +1,111 @@ + + + + #FA6594 + #99FA6594 + #F89BB8 + #11F89BB8 + #99AC4666 + #ff9f1c + #27AE60 + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/themes_red.xml b/app/src/main/res/values/themes_red.xml new file mode 100644 index 00000000..e2f04a4c --- /dev/null +++ b/app/src/main/res/values/themes_red.xml @@ -0,0 +1,111 @@ + + + + #ff2525 + #99ff2525 + #F3465D + #11F36657 + #99982A1E + #ff9f1c + #27AE60 + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/file_path.xml b/app/src/main/res/xml/file_path.xml index e50eeb57..5a16e534 100644 --- a/app/src/main/res/xml/file_path.xml +++ b/app/src/main/res/xml/file_path.xml @@ -1,9 +1,16 @@ - + + + + \ No newline at end of file diff --git a/basic.gradle b/basic.gradle deleted file mode 100644 index 80db2a1f..00000000 --- a/basic.gradle +++ /dev/null @@ -1,56 +0,0 @@ -apply plugin: 'kotlin-android-extensions' -apply plugin: 'kotlin-android' - -android { - compileSdkVersion rootProject.ext.android.compileSdkVersion - buildToolsVersion rootProject.ext.android.buildToolsVersion - defaultConfig { - minSdkVersion rootProject.ext.android.minSdkVersion - targetSdkVersion rootProject.ext.android.targetSdkVersion - versionCode rootProject.ext.android.versionCode - versionName rootProject.ext.android.versionName - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" - multiDexEnabled true - } - buildTypes { - release { - debuggable false - } - debug { - debuggable true - } - } - compileOptions { - sourceCompatibility rootProject.ext.compileOptions.sourceCompatibility - targetCompatibility rootProject.ext.compileOptions.targetCompatibility - } - sourceSets { - main { - jniLibs.srcDirs = ['libs'] - } - } - resourcePrefix project.name + "_" -} - -dependencies { - implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation rootProject.ext.dependencies.appcompatV7 - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} - -repositories { - mavenCentral() -} - -configurations.all { - resolutionStrategy.eachDependency { DependencyResolveDetails details -> - def requested = details.requested - if (requested.group == 'com.android.support') { - if (!requested.name.startsWith("multidex")) { - details.useVersion rootProject.ext.versions.appcompat - } else { - details.useVersion rootProject.ext.versions.multidex - } - } - } -} \ No newline at end of file diff --git a/basic_core/build.gradle b/basic/core/build.gradle similarity index 52% rename from basic_core/build.gradle rename to basic/core/build.gradle index 344ae41d..07dc3965 100644 --- a/basic_core/build.gradle +++ b/basic/core/build.gradle @@ -1,21 +1,25 @@ apply plugin: 'com.android.library' -apply from: "../basic.gradle" +apply from: "${rootDir.path}/gradle/basic.gradle" android { + namespace "per.goweii.basic.core" } dependencies { - api project(":basic_ui") - + api project(":basic:ui") implementation rootProject.ext.dependencies.multidex - + api rootProject.ext.dependencies.localbroadcastmanager api rootProject.ext.dependencies.butterknife api rootProject.ext.dependencies.eventbus api rootProject.ext.dependencies.gson api rootProject.ext.dependencies.glide - annotationProcessor rootProject.ext.dependencies.glideCompiler - implementation rootProject.ext.dependencies.glideOkhttp3 - api rootProject.ext.dependencies.rxHttp + kapt rootProject.ext.dependencies.glideCompiler + api (rootProject.ext.dependencies.glideOkhttp3) { + transitive = false + } + api (rootProject.ext.dependencies.rxHttp) { + exclude group: 'com.google.code.gson' + } api rootProject.ext.dependencies.anyPermission api rootProject.ext.dependencies.swipeBack api rootProject.ext.dependencies.lazyfragment diff --git a/basic/core/proguard-rules.pro b/basic/core/proguard-rules.pro new file mode 100644 index 00000000..f1ae8eb3 --- /dev/null +++ b/basic/core/proguard-rules.pro @@ -0,0 +1,31 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# 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 *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +-keep class com.google.gson.** {*;} +-keep class sun.misc.Unsafe {*;} +-keep class com.google.gson.stream.** {*;} +-keep class com.google.gson.examples.android.model.** {*;} +-keep class com.google.** { + ; + ; +} +-dontwarn class com.google.gson.** \ No newline at end of file diff --git a/basic_core/src/main/AndroidManifest.xml b/basic/core/src/main/AndroidManifest.xml similarity index 91% rename from basic_core/src/main/AndroidManifest.xml rename to basic/core/src/main/AndroidManifest.xml index 56af8fbe..c522624f 100644 --- a/basic_core/src/main/AndroidManifest.xml +++ b/basic/core/src/main/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/basic_core/src/main/java/per/goweii/basic/core/CoreInit.java b/basic/core/src/main/java/per/goweii/basic/core/CoreInit.java similarity index 84% rename from basic_core/src/main/java/per/goweii/basic/core/CoreInit.java rename to basic/core/src/main/java/per/goweii/basic/core/CoreInit.java index fdd0868a..3ceb3b58 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/CoreInit.java +++ b/basic/core/src/main/java/per/goweii/basic/core/CoreInit.java @@ -14,18 +14,18 @@ public class CoreInit { private SimpleCallback mOnGoLoginCallback = null; - private static class Holder{ + private static class Holder { private static final CoreInit INSTANCE = new CoreInit(); } - private CoreInit(){ + private CoreInit() { } - public static CoreInit getInstance(){ + public static CoreInit getInstance() { return Holder.INSTANCE; } - public void setOnGoLoginCallback(SimpleCallback callback){ + public void setOnGoLoginCallback(SimpleCallback callback) { mOnGoLoginCallback = callback; } diff --git a/basic_core/src/main/java/per/goweii/basic/core/adapter/FixedFragmentPagerAdapter.java b/basic/core/src/main/java/per/goweii/basic/core/adapter/FixedFragmentPagerAdapter.java similarity index 84% rename from basic_core/src/main/java/per/goweii/basic/core/adapter/FixedFragmentPagerAdapter.java rename to basic/core/src/main/java/per/goweii/basic/core/adapter/FixedFragmentPagerAdapter.java index f8bb05dc..cc6abe9d 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/adapter/FixedFragmentPagerAdapter.java +++ b/basic/core/src/main/java/per/goweii/basic/core/adapter/FixedFragmentPagerAdapter.java @@ -1,15 +1,14 @@ package per.goweii.basic.core.adapter; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; /** * @author CuiZhen * @date 2019/5/11 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class FixedFragmentPagerAdapter extends FragmentPagerAdapter { @@ -30,6 +29,7 @@ public FixedFragmentPagerAdapter(FragmentManager fm) { super(fm); } + @NonNull @Override public Fragment getItem(int i) { return mFragments[i]; diff --git a/basic_core/src/main/java/per/goweii/basic/core/adapter/MultiFragmentPagerAdapter.java b/basic/core/src/main/java/per/goweii/basic/core/adapter/MultiFragmentPagerAdapter.java similarity index 83% rename from basic_core/src/main/java/per/goweii/basic/core/adapter/MultiFragmentPagerAdapter.java rename to basic/core/src/main/java/per/goweii/basic/core/adapter/MultiFragmentPagerAdapter.java index 144a4984..daa91922 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/adapter/MultiFragmentPagerAdapter.java +++ b/basic/core/src/main/java/per/goweii/basic/core/adapter/MultiFragmentPagerAdapter.java @@ -1,17 +1,15 @@ package per.goweii.basic.core.adapter; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; import java.util.List; /** * @author CuiZhen * @date 2019/5/12 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class MultiFragmentPagerAdapter extends FragmentStatePagerAdapter { @@ -51,6 +49,7 @@ public CharSequence getPageTitle(int position) { public interface FragmentCreator { F create(E data, int pos); + String getTitle(E data); } } diff --git a/basic/core/src/main/java/per/goweii/basic/core/adapter/TabFragmentPagerAdapter.java b/basic/core/src/main/java/per/goweii/basic/core/adapter/TabFragmentPagerAdapter.java new file mode 100644 index 00000000..b0c5c020 --- /dev/null +++ b/basic/core/src/main/java/per/goweii/basic/core/adapter/TabFragmentPagerAdapter.java @@ -0,0 +1,226 @@ +package per.goweii.basic.core.adapter; + +import android.annotation.SuppressLint; +import android.view.GestureDetector; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.widget.LinearLayout; + +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; + +import java.util.List; + +/** + * @author CuiZhen + * @date 2019/5/11 + * GitHub: https://github.com/goweii + */ +public class TabFragmentPagerAdapter extends FragmentPagerAdapter implements ViewPager.OnPageChangeListener { + + private final ViewPager mViewPager; + private final LinearLayout mTabContainer; + private final int mTabItemRes; + private List> mPages = null; + + public TabFragmentPagerAdapter(@NonNull FragmentManager fm, + @NonNull ViewPager viewPager, + @NonNull LinearLayout tabContainer, + @LayoutRes int tabItemRes) { + super(fm); + mViewPager = viewPager; + mTabContainer = tabContainer; + mTabItemRes = tabItemRes; + mViewPager.setAdapter(this); + mViewPager.addOnPageChangeListener(this); + } + + public final void setPages(@NonNull List> pages) { + mViewPager.setOffscreenPageLimit(pages.size()); + mTabContainer.removeAllViews(); + mPages = pages; + for (Page page : mPages) { + initPageTab(page); + } + notifyDataSetChanged(); + } + + @Override + public void notifyDataSetChanged() { + super.notifyDataSetChanged(); + notifyPageDataChanged(); + } + + public void notifyPageDataChanged() { + int currPos = mViewPager.getCurrentItem(); + for (int i = 0; i < mPages.size(); i++) { + mPages.get(i).notifyAdapterBindData(currPos == i); + } + } + + private void initPageTab(@NonNull Page page) { + page.view = LayoutInflater.from(mTabContainer.getContext()).inflate(mTabItemRes, mTabContainer, false); + LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) page.view.getLayoutParams(); + params.height = LinearLayout.LayoutParams.MATCH_PARENT; + params.width = 0; + params.weight = 1; + mTabContainer.addView(page.view, params); + final GestureDetector mGestureDetector = new GestureDetector(page.view.getContext(), new GestureDetector.OnGestureListener() { + @Override + public boolean onDown(MotionEvent e) { + return true; + } + + @Override + public void onShowPress(MotionEvent e) { + } + + @Override + public boolean onSingleTapUp(MotionEvent e) { + switchCurrentItem(page); + return true; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + return false; + } + + @Override + public void onLongPress(MotionEvent e) { + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + return false; + } + }); + mGestureDetector.setOnDoubleTapListener(new GestureDetector.OnDoubleTapListener() { + @Override + public boolean onSingleTapConfirmed(MotionEvent e) { + return false; + } + + @Override + public boolean onDoubleTap(MotionEvent e) { + page.notifyAdapterDoubleTap(); + return true; + } + + @Override + public boolean onDoubleTapEvent(MotionEvent e) { + return false; + } + }); + page.view.setOnTouchListener(new View.OnTouchListener() { + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View v, MotionEvent event) { + return mGestureDetector.onTouchEvent(event); + } + }); + } + + private void switchCurrentItem(Page page) { + for (int i = 0; i < getCount(); i++) { + Fragment fragment = getItem(i); + if (page.fragment == fragment) { + mViewPager.setCurrentItem(i); + break; + } + } + } + + @Override + public int getItemPosition(@NonNull Object object) { + for (int i = 0; i < mPages.size(); i++) { + if (mPages.get(i).getFragment() == object) { + return i; + } + } + return POSITION_NONE; + } + + @Override + public long getItemId(int position) { + return mPages.get(position).getId(); + } + + @NonNull + @Override + public Fragment getItem(int i) { + return mPages.get(i).getFragment(); + } + + @Override + public int getCount() { + return mPages == null ? 0 : mPages.size(); + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + for (int i = 0; i < mPages.size(); i++) { + Page page = mPages.get(i); + page.notifyAdapterBindData(position == i); + } + } + + @Override + public void onPageScrollStateChanged(int state) { + } + + public static class Page { + private final int id; + @NonNull + private final Fragment fragment; + @NonNull + private final T data; + @NonNull + private final TabAdapter adapter; + private View view; + + public Page(int id, @NonNull Fragment fragment, @NonNull T data, @NonNull TabAdapter adapter) { + this.id = id; + this.fragment = fragment; + this.data = data; + this.adapter = adapter; + } + + public int getId() { + return id; + } + + @NonNull + public Fragment getFragment() { + return fragment; + } + + @NonNull + public T getData() { + return data; + } + + public void notifyAdapterBindData(boolean selected) { + adapter.onBindData(view, data, selected); + } + + public void notifyAdapterDoubleTap() { + adapter.onDoubleTap(fragment); + } + + public interface TabAdapter { + void onBindData(@NonNull View view, @NonNull T data, boolean selected); + + void onDoubleTap(Fragment fragment); + } + } +} diff --git a/basic/core/src/main/java/per/goweii/basic/core/base/App.java b/basic/core/src/main/java/per/goweii/basic/core/base/App.java new file mode 100644 index 00000000..062472aa --- /dev/null +++ b/basic/core/src/main/java/per/goweii/basic/core/base/App.java @@ -0,0 +1,296 @@ +package per.goweii.basic.core.base; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.ActivityManager; +import android.app.Application; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.Process; +import android.text.TextUtils; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.multidex.MultiDex; + +import java.util.ArrayList; +import java.util.List; + +import per.goweii.basic.utils.ProcessUtils; + +/** + * @author Cuizhen + * @date 2018/6/25-上午10:39 + */ +class App extends Application implements Application.ActivityLifecycleCallbacks { + @SuppressLint("StaticFieldLeak") + private static Application application = null; + private static final List activities = new ArrayList<>(); + + public static Application getApp() { + if (application == null) { + throw new NullPointerException("App is not registered in the manifest"); + } else { + return application; + } + } + + public static Context getAppContext() { + return getApp().getApplicationContext(); + } + + public static List getActivities() { + return activities; + } + + public static boolean isAppAlive() { + if (application == null) return false; + return activities.size() != 0; + } + + /** + * 判断Android程序是否在前台运行 + */ + public static boolean isForeground() { + ActivityManager activityManager = (ActivityManager) getAppContext().getSystemService(Context.ACTIVITY_SERVICE); + if (activityManager == null) { + return false; + } + List appProcesses = activityManager.getRunningAppProcesses(); + if (appProcesses == null) { + return false; + } + String packageName = getAppContext().getPackageName(); + for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { + if (appProcess.processName.equals(packageName) && appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { + return true; + } + } + return false; + } + + /** + * APP从后台切换回前台 + */ + public static void bringToForeground() { + if (!isForeground()) { + Activity currentActivity = currentActivity(); + if (currentActivity != null) { + Intent intent = new Intent(getAppContext(), currentActivity.getClass()); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + getAppContext().startActivity(intent); + } + } + } + + /** + * 获取当前Activity + */ + @Nullable + public static Activity currentActivity() { + if (activities.isEmpty()) { + return null; + } + return activities.get(activities.size() - 1); + } + + /** + * 按照指定类名找到activity + */ + @Nullable + public static Activity findActivity(@Nullable Class cls) { + if (cls == null) { + return null; + } + if (activities.isEmpty()) { + return null; + } + for (Activity activity : activities) { + if (activity.getClass().equals(cls)) { + return activity; + } + } + return null; + } + + /** + * 结束当前Activity + */ + public static void finishCurrentActivity() { + finishActivity(currentActivity()); + } + + /** + * 结束指定的Activity + */ + public static void finishActivity(@Nullable Activity activity) { + if (activity == null) { + return; + } + if (activities.isEmpty()) { + return; + } + activities.remove(activity); + activity.finish(); + activity = null; + } + + /** + * 结束指定类名的Activity + */ + public static void finishActivity(@Nullable Class cls) { + if (cls == null) { + return; + } + if (activities.isEmpty()) { + return; + } + for (int i = activities.size() - 1; i >= 0; i--) { + Activity activity = activities.get(i); + if (cls.equals(activity.getClass())) { + finishActivity(activity); + } + } + } + + /** + * 结束所有Activity + */ + public static void finishAllActivity() { + if (activities.isEmpty()) { + return; + } + for (int i = activities.size() - 1; i >= 0; i--) { + Activity activity = activities.get(i); + if (!activity.isFinishing()) { + activity.finish(); + } + } + activities.clear(); + } + + public static void exitApp() { + finishAllActivity(); + killProcess(); + } + + public static void killProcess() { + Process.killProcess(Process.myPid()); + } + + public static void restartApp() { + final Intent intent = getApp().getPackageManager().getLaunchIntentForPackage(getApp().getPackageName()); + if (intent != null) { + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); + getApp().startActivity(intent); + killProcess(); + } else { + finishActivityWithoutCount(1); + if (!activities.isEmpty()) { + activities.get(0).recreate(); + } + } + } + + public static void recreate() { + if (!activities.isEmpty()) { + for (Activity activity : activities) { + activity.recreate(); + } + } + } + + public static void finishActivityWithoutCount(int count) { + if (activities.isEmpty()) { + return; + } + if (count <= 0) { + finishAllActivity(); + return; + } + for (int i = activities.size() - 1; i >= count; i--) { + finishActivity(activities.get(i)); + } + } + + public static void finishActivityWithout(@Nullable Class cls) { + if (cls == null) { + finishAllActivity(); + return; + } + if (activities.isEmpty()) { + return; + } + for (int i = activities.size() - 1; i >= 0; i--) { + Activity activity = activities.get(i); + if (!cls.equals(activity.getClass())) { + finishActivity(activity); + } + } + } + + public static void finishActivityWithout(@Nullable Activity activity) { + if (activity == null) { + finishAllActivity(); + return; + } + finishActivityWithout(activity.getClass()); + } + + public static boolean isMainProcess() { + String mainProcessName = getAppContext().getPackageName(); + String processName = getCurrentProcessName(); + return TextUtils.equals(processName, mainProcessName); + } + + @NonNull + public static String getCurrentProcessName() { + return ProcessUtils.INSTANCE.getCurrentProcessName(getAppContext()); + } + + @Override + protected void attachBaseContext(Context context) { + super.attachBaseContext(context); + application = this; + MultiDex.install(this); + } + + @Override + public void onCreate() { + super.onCreate(); + registerActivityLifecycleCallbacks(this); + } + + @Override + public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { + activities.add(activity); + } + + @Override + public void onActivityStarted(@NonNull Activity activity) { + } + + @Override + public void onActivityResumed(@NonNull Activity activity) { + } + + @Override + public void onActivityPaused(@NonNull Activity activity) { + } + + @Override + public void onActivityStopped(@NonNull Activity activity) { + } + + @Override + public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) { + } + + @Override + public void onActivityDestroyed(@NonNull Activity activity) { + activities.remove(activity); + } + +} diff --git a/basic_core/src/main/java/per/goweii/basic/core/base/AppLike.java b/basic/core/src/main/java/per/goweii/basic/core/base/AppLike.java similarity index 99% rename from basic_core/src/main/java/per/goweii/basic/core/base/AppLike.java rename to basic/core/src/main/java/per/goweii/basic/core/base/AppLike.java index 0ecae4da..ca003a10 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/base/AppLike.java +++ b/basic/core/src/main/java/per/goweii/basic/core/base/AppLike.java @@ -12,9 +12,14 @@ */ interface AppLike { void attachBaseContext(Context context); + void onCreate(Application app); + void onConfigurationChanged(Application app, Configuration newConfig); + void onTerminate(Application app); + void onLowMemory(Application app); + void onTrimMemory(Application app, int level); } diff --git a/basic_core/src/main/java/per/goweii/basic/core/base/BaseActivity.java b/basic/core/src/main/java/per/goweii/basic/core/base/BaseActivity.java similarity index 95% rename from basic_core/src/main/java/per/goweii/basic/core/base/BaseActivity.java rename to basic/core/src/main/java/per/goweii/basic/core/base/BaseActivity.java index 02fe28af..dd172fff 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/base/BaseActivity.java +++ b/basic/core/src/main/java/per/goweii/basic/core/base/BaseActivity.java @@ -4,6 +4,8 @@ import android.app.Activity; import android.os.Bundle; +import androidx.viewbinding.ViewBinding; + import org.greenrobot.eventbus.EventBus; import butterknife.ButterKnife; @@ -20,7 +22,7 @@ * @version v1.0.0 * @date 2018/4/4-下午1:23 */ -public abstract class BaseActivity

                                      extends MvpActivity

                                      { +public abstract class BaseActivity

                                      extends MvpActivity { private LoadingDialog mLoadingDialog = null; private LoadingBarManager mLoadingBarManager = null; private Unbinder mUnbinder = null; diff --git a/basic_core/src/main/java/per/goweii/basic/core/base/BaseApp.java b/basic/core/src/main/java/per/goweii/basic/core/base/BaseApp.java similarity index 64% rename from basic_core/src/main/java/per/goweii/basic/core/base/BaseApp.java rename to basic/core/src/main/java/per/goweii/basic/core/base/BaseApp.java index 86ccbc27..09d0824e 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/base/BaseApp.java +++ b/basic/core/src/main/java/per/goweii/basic/core/base/BaseApp.java @@ -2,16 +2,6 @@ import android.content.Context; import android.content.res.Configuration; -import android.support.annotation.NonNull; - -import com.scwang.smartrefresh.layout.SmartRefreshLayout; -import com.scwang.smartrefresh.layout.api.DefaultRefreshFooterCreator; -import com.scwang.smartrefresh.layout.api.DefaultRefreshHeaderCreator; -import com.scwang.smartrefresh.layout.api.RefreshFooter; -import com.scwang.smartrefresh.layout.api.RefreshHeader; -import com.scwang.smartrefresh.layout.api.RefreshLayout; -import com.scwang.smartrefresh.layout.footer.ClassicsFooter; -import com.scwang.smartrefresh.layout.header.ClassicsHeader; import java.util.ArrayList; import java.util.List; @@ -33,38 +23,6 @@ public abstract class BaseApp extends App { private final List mAppLikeList = new ArrayList<>(APP_LIKE_LIST.size()); - static { -// ClassicsHeader.REFRESH_HEADER_PULLING = "下拉可以刷新"; -// ClassicsHeader.REFRESH_HEADER_REFRESHING = "正在刷新..."; -// ClassicsHeader.REFRESH_HEADER_LOADING = "正在加载..."; -// ClassicsHeader.REFRESH_HEADER_RELEASE = "释放立即刷新"; -// ClassicsHeader.REFRESH_HEADER_FINISH = "刷新完成"; -// ClassicsHeader.REFRESH_HEADER_FAILED = "刷新失败"; -// ClassicsHeader.REFRESH_HEADER_SECONDARY = "释放进入二楼"; -// ClassicsHeader.REFRESH_HEADER_UPDATE = "上次更新 M-d HH:mm"; - SmartRefreshLayout.setDefaultRefreshHeaderCreator(new DefaultRefreshHeaderCreator() { - @NonNull - @Override - public RefreshHeader createRefreshHeader(@NonNull Context context, @NonNull RefreshLayout layout) { - return new ClassicsHeader(context); - } - }); -// ClassicsFooter.REFRESH_FOOTER_PULLING = "上拉加载更多"; -// ClassicsFooter.REFRESH_FOOTER_RELEASE = "释放立即加载"; -// ClassicsFooter.REFRESH_FOOTER_REFRESHING = "正在刷新..."; -// ClassicsFooter.REFRESH_FOOTER_LOADING = "正在加载..."; -// ClassicsFooter.REFRESH_FOOTER_FINISH = "加载完成"; -// ClassicsFooter.REFRESH_FOOTER_FAILED = "加载失败"; -// ClassicsFooter.REFRESH_FOOTER_NOTHING = "没有更多数据了"; - SmartRefreshLayout.setDefaultRefreshFooterCreator(new DefaultRefreshFooterCreator() { - @NonNull - @Override - public RefreshFooter createRefreshFooter(@NonNull Context context, @NonNull RefreshLayout layout) { - return new ClassicsFooter(context); - } - }); - } - @Override protected void attachBaseContext(Context context) { super.attachBaseContext(context); diff --git a/basic_core/src/main/java/per/goweii/basic/core/base/BaseAppLike.java b/basic/core/src/main/java/per/goweii/basic/core/base/BaseAppLike.java similarity index 100% rename from basic_core/src/main/java/per/goweii/basic/core/base/BaseAppLike.java rename to basic/core/src/main/java/per/goweii/basic/core/base/BaseAppLike.java diff --git a/basic_core/src/main/java/per/goweii/basic/core/base/BaseEntity.java b/basic/core/src/main/java/per/goweii/basic/core/base/BaseEntity.java similarity index 100% rename from basic_core/src/main/java/per/goweii/basic/core/base/BaseEntity.java rename to basic/core/src/main/java/per/goweii/basic/core/base/BaseEntity.java diff --git a/basic_core/src/main/java/per/goweii/basic/core/base/BaseFragment.java b/basic/core/src/main/java/per/goweii/basic/core/base/BaseFragment.java similarity index 94% rename from basic_core/src/main/java/per/goweii/basic/core/base/BaseFragment.java rename to basic/core/src/main/java/per/goweii/basic/core/base/BaseFragment.java index 89194c6f..76eece9c 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/base/BaseFragment.java +++ b/basic/core/src/main/java/per/goweii/basic/core/base/BaseFragment.java @@ -1,5 +1,7 @@ package per.goweii.basic.core.base; +import androidx.viewbinding.ViewBinding; + import org.greenrobot.eventbus.EventBus; import butterknife.ButterKnife; @@ -13,7 +15,7 @@ * @version v1.0.0 * @date 2018/3/10-下午12:38 */ -public abstract class BaseFragment

                                      extends MvpFragment

                                      { +public abstract class BaseFragment

                                      extends MvpFragment { private LoadingDialog mLoadingDialog = null; private LoadingBarManager mLoadingBarManager = null; private Unbinder mUnbinder = null; diff --git a/basic_core/src/main/java/per/goweii/basic/core/base/BasePresenter.java b/basic/core/src/main/java/per/goweii/basic/core/base/BasePresenter.java similarity index 100% rename from basic_core/src/main/java/per/goweii/basic/core/base/BasePresenter.java rename to basic/core/src/main/java/per/goweii/basic/core/base/BasePresenter.java diff --git a/basic_core/src/main/java/per/goweii/basic/core/base/BaseView.java b/basic/core/src/main/java/per/goweii/basic/core/base/BaseView.java similarity index 100% rename from basic_core/src/main/java/per/goweii/basic/core/base/BaseView.java rename to basic/core/src/main/java/per/goweii/basic/core/base/BaseView.java diff --git a/basic/core/src/main/java/per/goweii/basic/core/common/Config.java b/basic/core/src/main/java/per/goweii/basic/core/common/Config.java new file mode 100644 index 00000000..aab424e0 --- /dev/null +++ b/basic/core/src/main/java/per/goweii/basic/core/common/Config.java @@ -0,0 +1,11 @@ +package per.goweii.basic.core.common; + +/** + * 可变的参数 + * + * @author Cuizhen + */ +public final class Config { + + public static final long HTTP_TIMEOUT = 5000; +} \ No newline at end of file diff --git a/basic_core/src/main/java/per/goweii/basic/core/common/Constant.java b/basic/core/src/main/java/per/goweii/basic/core/common/Constant.java similarity index 100% rename from basic_core/src/main/java/per/goweii/basic/core/common/Constant.java rename to basic/core/src/main/java/per/goweii/basic/core/common/Constant.java diff --git a/basic/core/src/main/java/per/goweii/basic/core/glide/GlideHelper.java b/basic/core/src/main/java/per/goweii/basic/core/glide/GlideHelper.java new file mode 100644 index 00000000..e0c47f2b --- /dev/null +++ b/basic/core/src/main/java/per/goweii/basic/core/glide/GlideHelper.java @@ -0,0 +1,446 @@ +package per.goweii.basic.core.glide; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.widget.ImageView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestBuilder; +import com.bumptech.glide.RequestManager; +import com.bumptech.glide.load.DecodeFormat; +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; +import com.bumptech.glide.load.resource.gif.GifDrawable; +import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.request.target.BitmapImageViewTarget; +import com.bumptech.glide.request.target.ImageViewTarget; +import com.bumptech.glide.request.target.SimpleTarget; +import com.bumptech.glide.request.transition.Transition; + +import per.goweii.basic.core.glide.progress.OnProgressListener; +import per.goweii.basic.core.glide.progress.ProgressInterceptor; +import per.goweii.basic.utils.Utils; +import per.goweii.basic.utils.listener.SimpleCallback; +import per.goweii.basic.utils.listener.SimpleListener; + +/** + * Glide图片加载框架的帮助类 + * + * @author Cuizhen + * @date 2018/8/29-上午9:51 + */ +public class GlideHelper { + + private final RequestManager mManager; + private RequestBuilder mBuilder; + private RequestBuilder mBuilderBmp; + private RequestBuilder mBuilderGif; + private boolean mCache = true; + private Drawable mPlaceHolder = null; + private Drawable mErrorHolder = null; + private OnGlideProgressListener mOnGlideProgressListener = null; + private OnProgressListener mProgressListener = null; + private Handler mProgressHandler = null; + private String mImageUrl; + private BitmapTransformation mBitmapTransformation = null; + + private GlideHelper(Context context) { + mManager = Glide.with(checkContext(context)); + } + + public static GlideHelper with(Context context) { + return new GlideHelper(context); + } + + public void pauseRequests() { + mManager.pauseRequests(); + } + + public void resumeRequests() { + mManager.resumeRequests(); + } + + public GlideHelper highQuality() { + mManager.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_ARGB_8888)); + return this; + } + + public GlideHelper cache(boolean cache) { + this.mCache = cache; + return this; + } + + public GlideHelper placeHolder(Drawable placeHolder) { + this.mPlaceHolder = placeHolder; + return this; + } + + public GlideHelper errorHolder(Drawable errorHolder) { + this.mErrorHolder = errorHolder; + return this; + } + + public GlideHelper transformation(BitmapTransformation transformation) { + this.mBitmapTransformation = transformation; + return this; + } + + @SuppressLint("HandlerLeak") + public GlideHelper onProgressListener(OnGlideProgressListener listener) { + mOnGlideProgressListener = listener; + mProgressHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + if (mOnGlideProgressListener != null) { + float progress = (float) msg.obj; + mOnGlideProgressListener.onProgress(progress); + } + } + }; + return this; + } + + public enum As { + DRAWABLE, GIF, BITMAP + } + + private As as = As.DRAWABLE; + + public GlideHelper asDrawable() { + as = As.DRAWABLE; + return this; + } + + public GlideHelper asGif() { + as = As.GIF; + return this; + } + + public GlideHelper asBitmap() { + as = As.BITMAP; + return this; + } + + public GlideHelper load(String url) { + this.mImageUrl = url; + switch (as) { + case DRAWABLE: + mBuilder = getBuilder().load(url); + break; + case GIF: + mBuilderGif = getGifBuilder().load(url); + break; + case BITMAP: + mBuilderBmp = getBmpBuilder().load(url); + break; + } + return this; + } + + public GlideHelper load(Uri uri) { + switch (as) { + case DRAWABLE: + mBuilder = getBuilder().load(uri); + break; + case GIF: + mBuilderGif = getGifBuilder().load(uri); + break; + case BITMAP: + mBuilderBmp = getBmpBuilder().load(uri); + break; + } + return this; + } + + public GlideHelper load(int resId) { + switch (as) { + case DRAWABLE: + mBuilder = getBuilder().load(resId); + break; + case GIF: + mBuilderGif = getGifBuilder().load(resId); + break; + case BITMAP: + mBuilderBmp = getBmpBuilder().load(resId); + break; + } + return this; + } + + public GlideHelper load(Bitmap bitmap) { + switch (as) { + case DRAWABLE: + mBuilder = getBuilder().load(bitmap); + break; + case GIF: + mBuilderGif = getGifBuilder().load(bitmap); + break; + case BITMAP: + mBuilderBmp = getBmpBuilder().load(bitmap); + break; + } + return this; + } + + public void into(ImageView imageView) { + if (mOnGlideProgressListener != null && mProgressHandler != null && mImageUrl != null) { + mProgressListener = new OnProgressListener() { + @Override + public void onProgress(float progress) { + notifyProgressChanged(progress); + } + }; + ProgressInterceptor.addProgressListener(mImageUrl, mProgressListener); + } + RequestOptions options = getOptions(); + if (imageView.getMeasuredWidth() > 0 && imageView.getMeasuredHeight() > 0) { + options = options.override(imageView.getMeasuredWidth(), imageView.getMeasuredHeight()); + } + switch (as) { + case DRAWABLE: + getBuilder().apply(options).into(new ImageViewTarget(imageView) { + @Override + protected void setResource(@Nullable Drawable resource) { + imageView.setImageDrawable(resource); + } + + @Override + public void onLoadStarted(@Nullable Drawable placeholder) { + super.onLoadStarted(placeholder); + notifyLoadStarted(); + } + + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + super.onResourceReady(resource, transition); + notifyResourceReady(); + } + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + notifyLoadFailed(); + } + }); + break; + case GIF: + getGifBuilder().apply(options).into(new ImageViewTarget(imageView) { + @Override + protected void setResource(@Nullable GifDrawable resource) { + imageView.setImageDrawable(resource); + } + + @Override + public void onLoadStarted(@Nullable Drawable placeholder) { + super.onLoadStarted(placeholder); + notifyLoadStarted(); + } + + @Override + public void onResourceReady(@NonNull GifDrawable resource, @Nullable Transition transition) { + super.onResourceReady(resource, transition); + notifyResourceReady(); + } + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + notifyLoadFailed(); + } + }); + break; + case BITMAP: + getBmpBuilder().apply(options).into(new BitmapImageViewTarget(imageView) { + @Override + public void onLoadStarted(@Nullable Drawable placeholder) { + super.onLoadStarted(placeholder); + notifyLoadStarted(); + } + + @Override + public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { + super.onResourceReady(resource, transition); + notifyResourceReady(); + } + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + notifyLoadFailed(); + } + }); + break; + } + } + + private void notifyProgressChanged(float progress) { + if (mProgressHandler != null) { + Message msg = mProgressHandler.obtainMessage(); + msg.obj = progress; + mProgressHandler.sendMessage(msg); + } + } + + private void notifyLoadStarted() { + notifyProgressChanged(0F); + } + + private void notifyResourceReady() { + notifyProgressChanged(1F); + if (mProgressListener != null) { + ProgressInterceptor.removeProgressListener(mProgressListener); + } + } + + private void notifyLoadFailed() { + notifyProgressChanged(-1F); + if (mProgressListener != null) { + ProgressInterceptor.removeProgressListener(mProgressListener); + } + } + + public void preload() { + switch (as) { + case DRAWABLE: + getBuilder().apply(getOptions()).preload(); + break; + case GIF: + getGifBuilder().apply(getOptions()).preload(); + break; + case BITMAP: + getBmpBuilder().apply(getOptions()).preload(); + break; + } + } + + public void getGif(final SimpleCallback callback) { + getGif(callback, null); + } + + public void getGif(final SimpleCallback callback, final SimpleListener onFail) { + getGifBuilder().apply(getOptions()).into(new SimpleTarget() { + @Override + public void onResourceReady(@NonNull GifDrawable resource, @Nullable Transition transition) { + if (callback != null) { + callback.onResult(resource); + } + } + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + if (onFail != null) { + onFail.onResult(); + } + } + }); + } + + public void getDrawable(final SimpleCallback callback) { + getDrawable(callback, null); + } + + public void getDrawable(final SimpleCallback callback, final SimpleListener onFail) { + getBuilder().apply(getOptions()).into(new SimpleTarget() { + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Transition transition) { + if (callback != null) { + callback.onResult(resource); + } + } + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + if (onFail != null) { + onFail.onResult(); + } + } + }); + } + + public void getBitmap(final SimpleCallback callback) { + getBitmap(callback, null); + } + + public void getBitmap(final SimpleCallback onSuccess, final SimpleListener onFail) { + getBmpBuilder().apply(getOptions()).into(new SimpleTarget() { + @Override + public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { + if (onSuccess != null) { + onSuccess.onResult(resource); + } + } + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + super.onLoadFailed(errorDrawable); + if (onFail != null) { + onFail.onResult(); + } + } + }); + } + + @SuppressLint("CheckResult") + private RequestOptions getOptions() { + RequestOptions options = new RequestOptions(); + if (mPlaceHolder != null) { + options.placeholder(mPlaceHolder); + } + if (mErrorHolder != null) { + options.error(mErrorHolder); + } + if (mCache) { + options.diskCacheStrategy(DiskCacheStrategy.ALL); + } else { + options.skipMemoryCache(true) + .diskCacheStrategy(DiskCacheStrategy.NONE); + } + if (mBitmapTransformation != null) { + options.transform(mBitmapTransformation); + } + return options; + } + + private Context checkContext(Context context) { + if (context != null) { + return context; + } + return Utils.getAppContext(); + } + + private RequestBuilder getBmpBuilder() { + if (mBuilderBmp == null) { + mBuilderBmp = mManager.asBitmap(); + } + return mBuilderBmp; + } + + private RequestBuilder getGifBuilder() { + if (mBuilderGif == null) { + mBuilderGif = mManager.asGif(); + } + return mBuilderGif; + } + + private RequestBuilder getBuilder() { + if (mBuilder == null) { + mBuilder = mManager.asDrawable(); + } + return mBuilder; + } + + public interface OnGlideProgressListener { + void onProgress(float progress); + } +} diff --git a/basic_core/src/main/java/per/goweii/basic/core/glide/progress/OnProgressListener.java b/basic/core/src/main/java/per/goweii/basic/core/glide/progress/OnProgressListener.java similarity index 81% rename from basic_core/src/main/java/per/goweii/basic/core/glide/progress/OnProgressListener.java rename to basic/core/src/main/java/per/goweii/basic/core/glide/progress/OnProgressListener.java index 5b1bb730..02900295 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/glide/progress/OnProgressListener.java +++ b/basic/core/src/main/java/per/goweii/basic/core/glide/progress/OnProgressListener.java @@ -1,6 +1,6 @@ package per.goweii.basic.core.glide.progress; -import android.support.annotation.WorkerThread; +import androidx.annotation.WorkerThread; /** * 描述: diff --git a/basic_core/src/main/java/per/goweii/basic/core/glide/progress/ProgressAppGlideModule.java b/basic/core/src/main/java/per/goweii/basic/core/glide/progress/ProgressAppGlideModule.java similarity index 78% rename from basic_core/src/main/java/per/goweii/basic/core/glide/progress/ProgressAppGlideModule.java rename to basic/core/src/main/java/per/goweii/basic/core/glide/progress/ProgressAppGlideModule.java index c3f5767a..08897295 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/glide/progress/ProgressAppGlideModule.java +++ b/basic/core/src/main/java/per/goweii/basic/core/glide/progress/ProgressAppGlideModule.java @@ -1,7 +1,8 @@ package per.goweii.basic.core.glide.progress; import android.content.Context; -import android.support.annotation.NonNull; + +import androidx.annotation.NonNull; import com.bumptech.glide.Glide; import com.bumptech.glide.Registry; @@ -13,6 +14,7 @@ import java.io.InputStream; import okhttp3.OkHttpClient; +import per.goweii.basic.utils.LogUtils; import per.goweii.rxhttp.request.utils.HttpsCompat; /** @@ -27,6 +29,7 @@ public class ProgressAppGlideModule extends AppGlideModule { @Override public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) { registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(getOkHttpClient())); + LogUtils.d("ProgressAppGlideModule", "registerComponents down"); } @Override @@ -35,9 +38,12 @@ public boolean isManifestParsingEnabled() { } private static OkHttpClient getOkHttpClient() { + LogUtils.d("ProgressAppGlideModule", "getOkHttpClient"); OkHttpClient.Builder builder = new OkHttpClient.Builder(); + HttpsCompat.ignoreSSLForOkHttp(builder); HttpsCompat.enableTls12ForOkHttp(builder); builder.addInterceptor(new ProgressInterceptor()); + LogUtils.d("ProgressAppGlideModule", "getOkHttpClient down"); return builder.build(); } } diff --git a/basic_core/src/main/java/per/goweii/basic/core/glide/progress/ProgressInterceptor.java b/basic/core/src/main/java/per/goweii/basic/core/glide/progress/ProgressInterceptor.java similarity index 94% rename from basic_core/src/main/java/per/goweii/basic/core/glide/progress/ProgressInterceptor.java rename to basic/core/src/main/java/per/goweii/basic/core/glide/progress/ProgressInterceptor.java index 41e0b005..b08d5c5b 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/glide/progress/ProgressInterceptor.java +++ b/basic/core/src/main/java/per/goweii/basic/core/glide/progress/ProgressInterceptor.java @@ -1,8 +1,9 @@ package per.goweii.basic.core.glide.progress; -import android.support.annotation.NonNull; import android.text.TextUtils; +import androidx.annotation.NonNull; + import java.io.IOException; import java.lang.ref.WeakReference; import java.util.Collections; @@ -22,12 +23,13 @@ * @date 2018/9/17 */ public class ProgressInterceptor implements Interceptor { - public static final String TAG = ProgressInterceptor.class.getSimpleName(); + private static final String TAG = ProgressInterceptor.class.getSimpleName(); private static final Map> LISTENER_MAP = Collections.synchronizedMap(new HashMap<>()); @Override public Response intercept(Chain chain) throws IOException { + LogUtils.d(TAG, "intercept chain"); Request request = chain.request(); Response response = chain.proceed(request); String url = request.url().toString(); @@ -41,7 +43,7 @@ public Response intercept(Chain chain) throws IOException { .build(); } - private final class OnProgressResponseListener implements ProgressResponseBody.OnResponseListener { + private static final class OnProgressResponseListener implements ProgressResponseBody.OnResponseListener { private final OnProgressListener mListener; diff --git a/basic_core/src/main/java/per/goweii/basic/core/glide/progress/ProgressResponseBody.java b/basic/core/src/main/java/per/goweii/basic/core/glide/progress/ProgressResponseBody.java similarity index 97% rename from basic_core/src/main/java/per/goweii/basic/core/glide/progress/ProgressResponseBody.java rename to basic/core/src/main/java/per/goweii/basic/core/glide/progress/ProgressResponseBody.java index 6871b01a..5a364874 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/glide/progress/ProgressResponseBody.java +++ b/basic/core/src/main/java/per/goweii/basic/core/glide/progress/ProgressResponseBody.java @@ -1,7 +1,7 @@ package per.goweii.basic.core.glide.progress; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.io.IOException; diff --git a/basic_core/src/main/java/per/goweii/basic/core/glide/transformation/BlurTransformation.java b/basic/core/src/main/java/per/goweii/basic/core/glide/transformation/BlurTransformation.java similarity index 74% rename from basic_core/src/main/java/per/goweii/basic/core/glide/transformation/BlurTransformation.java rename to basic/core/src/main/java/per/goweii/basic/core/glide/transformation/BlurTransformation.java index e54d2e77..7d11dc61 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/glide/transformation/BlurTransformation.java +++ b/basic/core/src/main/java/per/goweii/basic/core/glide/transformation/BlurTransformation.java @@ -1,14 +1,15 @@ package per.goweii.basic.core.glide.transformation; import android.graphics.Bitmap; -import android.support.annotation.NonNull; + +import androidx.annotation.NonNull; import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; import java.security.MessageDigest; -import per.goweii.burred.Blurred; +import per.goweii.basic.core.utils.FastBlur; /** * 描述:高斯模糊 @@ -34,9 +35,19 @@ public BlurTransformation(float percent) { @Override protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { - return Blurred.with(toTransform).percent(percent).blur(); + if (percent <= 0) { + return toTransform; + } + float radius = Math.min(toTransform.getWidth(), toTransform.getHeight()) * percent; + float scale = 1; + if (radius > 25) { + scale = scale / (radius / 25); + radius = 25; + } + return FastBlur.get().process(toTransform, radius, scale, false, false); } + @NonNull @Override public String toString() { return "BlurTransformation(percent=" + percent + ")"; diff --git a/basic/core/src/main/java/per/goweii/basic/core/glide/transformation/RoundTransformation.java b/basic/core/src/main/java/per/goweii/basic/core/glide/transformation/RoundTransformation.java new file mode 100644 index 00000000..abb96e79 --- /dev/null +++ b/basic/core/src/main/java/per/goweii/basic/core/glide/transformation/RoundTransformation.java @@ -0,0 +1,62 @@ +package per.goweii.basic.core.glide.transformation; + +import android.graphics.Bitmap; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.RectF; + +import androidx.annotation.NonNull; +import androidx.annotation.Px; + +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; + +import java.security.MessageDigest; + +public class RoundTransformation extends BitmapTransformation { + private static final String ID = BlurTransformation.class.getName(); + + private float radius = 0f; + + public RoundTransformation(@Px int radius) { + this.radius = radius; + } + + @Override + protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { + return roundCrop(pool, toTransform); + } + + private Bitmap roundCrop(@NonNull BitmapPool pool, Bitmap source) { + Bitmap result = pool.get(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); + Canvas canvas = new Canvas(result); + Paint paint = new Paint(); + paint.setShader(new BitmapShader(source, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP)); + paint.setAntiAlias(true); + RectF rectF = new RectF(0f, 0f, source.getWidth(), source.getHeight()); + canvas.drawRoundRect(rectF, radius, radius, paint); + return result; + } + + @NonNull + @Override + public String toString() { + return "RoundTransformation(radius=" + radius + ")"; + } + + @Override + public boolean equals(Object o) { + return o instanceof RoundTransformation && ((RoundTransformation) o).radius == radius; + } + + @Override + public int hashCode() { + return (ID.hashCode() + Float.valueOf(radius).hashCode()); + } + + @Override + public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { + messageDigest.update((ID + radius).getBytes(CHARSET)); + } +} \ No newline at end of file diff --git a/basic/core/src/main/java/per/goweii/basic/core/glide/transformation/ScaleDownTransformation.java b/basic/core/src/main/java/per/goweii/basic/core/glide/transformation/ScaleDownTransformation.java new file mode 100644 index 00000000..5557c351 --- /dev/null +++ b/basic/core/src/main/java/per/goweii/basic/core/glide/transformation/ScaleDownTransformation.java @@ -0,0 +1,69 @@ +package per.goweii.basic.core.glide.transformation; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Rect; + +import androidx.annotation.NonNull; + +import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; +import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; + +import java.security.MessageDigest; + +import per.goweii.basic.utils.display.DisplayInfoUtils; + +/** + * 描述:高斯模糊 + * + * @author Cuizhen + * @date 2018/9/13 + */ +public class ScaleDownTransformation extends BitmapTransformation { + + private static final String ID = ScaleDownTransformation.class.getName(); + + private final float maxWidth; + private final float maxHeight; + + public ScaleDownTransformation() { + maxWidth = DisplayInfoUtils.getInstance().getWidthPixels(); + maxHeight = DisplayInfoUtils.getInstance().getHeightPixels(); + } + + private final Canvas mCanvas = new Canvas(); + + @Override + protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { + if (toTransform.getWidth() <= maxWidth && toTransform.getHeight() <= maxHeight) { + return toTransform; + } + float sx = maxWidth / toTransform.getWidth(); + float sy = maxHeight / toTransform.getHeight(); + float s = Math.min(sx, sy); + int sw = (int) (toTransform.getWidth() * s); + int sh = (int) (toTransform.getHeight() * s); + Bitmap output = pool.get(sw, sh, toTransform.getConfig()); + mCanvas.setBitmap(output); + mCanvas.drawBitmap(toTransform, + new Rect(0, 0, toTransform.getWidth(), toTransform.getHeight()), + new Rect(0, 0, sw, sh), + null); + return output; + } + + @Override + public boolean equals(Object o) { + return o instanceof ScaleDownTransformation; + } + + @Override + public int hashCode() { + return (ID.hashCode()); + } + + @Override + public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { + messageDigest.update((ID).getBytes(CHARSET)); + } +} diff --git a/basic_core/src/main/java/per/goweii/basic/core/mvp/CacheActivity.java b/basic/core/src/main/java/per/goweii/basic/core/mvp/CacheActivity.java similarity index 84% rename from basic_core/src/main/java/per/goweii/basic/core/mvp/CacheActivity.java rename to basic/core/src/main/java/per/goweii/basic/core/mvp/CacheActivity.java index 98380e97..b67eddb4 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/mvp/CacheActivity.java +++ b/basic/core/src/main/java/per/goweii/basic/core/mvp/CacheActivity.java @@ -3,14 +3,14 @@ import android.util.SparseArray; import android.view.View; -import per.goweii.swipeback.SwipeBackActivity; +import androidx.appcompat.app.AppCompatActivity; /** * @author Cuizhen * @version v1.0.0 * @date 2018/4/4-下午1:23 */ -public abstract class CacheActivity extends SwipeBackActivity { +public abstract class CacheActivity extends AppCompatActivity { private SparseArray mViewCaches = null; diff --git a/basic_core/src/main/java/per/goweii/basic/core/mvp/CacheFragment.java b/basic/core/src/main/java/per/goweii/basic/core/mvp/CacheFragment.java similarity index 89% rename from basic_core/src/main/java/per/goweii/basic/core/mvp/CacheFragment.java rename to basic/core/src/main/java/per/goweii/basic/core/mvp/CacheFragment.java index 14c27e1b..1d97c051 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/mvp/CacheFragment.java +++ b/basic/core/src/main/java/per/goweii/basic/core/mvp/CacheFragment.java @@ -1,10 +1,11 @@ package per.goweii.basic.core.mvp; -import android.support.annotation.IdRes; -import android.support.v4.app.Fragment; import android.util.SparseArray; import android.view.View; +import androidx.annotation.IdRes; +import androidx.fragment.app.Fragment; + /** * 描述: * diff --git a/basic_core/src/main/java/per/goweii/basic/core/mvp/MvpActivity.java b/basic/core/src/main/java/per/goweii/basic/core/mvp/MvpActivity.java similarity index 71% rename from basic_core/src/main/java/per/goweii/basic/core/mvp/MvpActivity.java rename to basic/core/src/main/java/per/goweii/basic/core/mvp/MvpActivity.java index 5d2f82ea..4231cd0a 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/mvp/MvpActivity.java +++ b/basic/core/src/main/java/per/goweii/basic/core/mvp/MvpActivity.java @@ -3,9 +3,14 @@ import android.app.Activity; import android.content.Context; import android.os.Bundle; -import android.support.annotation.Nullable; +import android.view.LayoutInflater; import android.view.View; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewbinding.ViewBinding; + import per.goweii.basic.utils.ClickHelper; @@ -14,14 +19,17 @@ * @version v1.0.0 * @date 2018/4/4-下午1:23 */ -public abstract class MvpActivity

                                      extends CacheActivity implements MvpView, View.OnClickListener { - +public abstract class MvpActivity

                                      extends CacheActivity implements MvpView, View.OnClickListener { public P presenter; + public V binding; /** - * 获取布局资源文件 + * 初始化ViewBinding + * + * @return V */ - protected abstract int getLayoutId(); + @Nullable + protected abstract V initViewBinding(@NonNull LayoutInflater inflater); /** * 初始化presenter @@ -42,14 +50,14 @@ public abstract class MvpActivity

                                      extends CacheActivity /** * 点击事件,可连续点击 */ - protected boolean onClick1(final View v){ + protected boolean onClick1(final View v) { return false; } /** * 点击事件,500毫秒第一次 */ - protected void onClick2(final View v){ + protected void onClick2(final View v) { } protected void initWindow() { @@ -59,8 +67,9 @@ protected void initWindow() { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initWindow(); - if (getLayoutId() > 0) { - setContentView(getLayoutId()); + binding = initViewBinding(getLayoutInflater()); + if (binding != null) { + setContentView(binding.getRoot()); } presenter = initPresenter(); if (presenter != null) { diff --git a/basic_core/src/main/java/per/goweii/basic/core/mvp/MvpFragment.java b/basic/core/src/main/java/per/goweii/basic/core/mvp/MvpFragment.java similarity index 72% rename from basic_core/src/main/java/per/goweii/basic/core/mvp/MvpFragment.java rename to basic/core/src/main/java/per/goweii/basic/core/mvp/MvpFragment.java index efa789fb..3583d806 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/mvp/MvpFragment.java +++ b/basic/core/src/main/java/per/goweii/basic/core/mvp/MvpFragment.java @@ -2,14 +2,16 @@ import android.content.Context; import android.os.Bundle; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.LayoutRes; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.viewbinding.ViewBinding; + import per.goweii.basic.utils.ClickHelper; import per.goweii.lazyfragment.LazyFragment; @@ -18,8 +20,9 @@ * @version v1.0.0 * @date 2018/3/10-下午12:38 */ -public abstract class MvpFragment extends LazyFragment implements MvpView, View.OnClickListener { +public abstract class MvpFragment extends LazyFragment implements MvpView, View.OnClickListener { protected T presenter; + public V binding; /** * 获取布局资源文件 @@ -28,7 +31,9 @@ public abstract class MvpFragment extends LazyFragment i */ @Override @LayoutRes - protected abstract int getLayoutRes(); + protected final int getLayoutRes() { + return 0; + } /** * 初始化presenter @@ -38,6 +43,14 @@ public abstract class MvpFragment extends LazyFragment i @Nullable protected abstract T initPresenter(); + /** + * 初始化ViewBinding + * + * @return V + */ + @Nullable + protected abstract V initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container); + /** * 绑定事件 */ @@ -51,14 +64,14 @@ public abstract class MvpFragment extends LazyFragment i /** * 点击事件,可连续点击 */ - protected boolean onClick1(final View v){ + protected boolean onClick1(final View v) { return false; } /** * 点击事件,500毫秒第一次 */ - protected void onClick2(final View v){ + protected void onClick2(final View v) { } @Override @@ -69,7 +82,13 @@ public void onCreate(@Nullable Bundle savedInstanceState) { @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - return super.onCreateView(inflater, container, savedInstanceState); + View view = super.onCreateView(inflater, container, savedInstanceState); + binding = initViewBinding(inflater, container); + if (binding != null) { + view = binding.getRoot(); + } + mRootView = view; + return view; } @Override @@ -99,6 +118,7 @@ public void onDestroyView() { if (presenter != null) { presenter.detach(); } + binding = null; super.onDestroyView(); } diff --git a/basic_core/src/main/java/per/goweii/basic/core/mvp/MvpPresenter.java b/basic/core/src/main/java/per/goweii/basic/core/mvp/MvpPresenter.java similarity index 100% rename from basic_core/src/main/java/per/goweii/basic/core/mvp/MvpPresenter.java rename to basic/core/src/main/java/per/goweii/basic/core/mvp/MvpPresenter.java diff --git a/basic_core/src/main/java/per/goweii/basic/core/mvp/MvpView.java b/basic/core/src/main/java/per/goweii/basic/core/mvp/MvpView.java similarity index 98% rename from basic_core/src/main/java/per/goweii/basic/core/mvp/MvpView.java rename to basic/core/src/main/java/per/goweii/basic/core/mvp/MvpView.java index 1fbca011..6f911064 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/mvp/MvpView.java +++ b/basic/core/src/main/java/per/goweii/basic/core/mvp/MvpView.java @@ -9,9 +9,14 @@ */ public interface MvpView { Context getContext(); + void showLoadingDialog(); + void dismissLoadingDialog(); + void showLoadingBar(); + void dismissLoadingBar(); + void clearLoading(); } diff --git a/basic_core/src/main/java/per/goweii/basic/core/permission/PermissionUtils.java b/basic/core/src/main/java/per/goweii/basic/core/permission/PermissionUtils.java similarity index 99% rename from basic_core/src/main/java/per/goweii/basic/core/permission/PermissionUtils.java rename to basic/core/src/main/java/per/goweii/basic/core/permission/PermissionUtils.java index 62924e91..0d75033f 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/permission/PermissionUtils.java +++ b/basic/core/src/main/java/per/goweii/basic/core/permission/PermissionUtils.java @@ -3,7 +3,8 @@ import android.Manifest; import android.content.Context; import android.os.Build; -import android.support.annotation.NonNull; + +import androidx.annotation.NonNull; import java.util.Arrays; @@ -17,8 +18,6 @@ /** * @author CuiZhen * @date 2019/11/3 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class PermissionUtils { diff --git a/basic_core/src/main/java/per/goweii/basic/core/receiver/LoginReceiver.java b/basic/core/src/main/java/per/goweii/basic/core/receiver/LoginReceiver.java similarity index 95% rename from basic_core/src/main/java/per/goweii/basic/core/receiver/LoginReceiver.java rename to basic/core/src/main/java/per/goweii/basic/core/receiver/LoginReceiver.java index 7ea96205..e64548d4 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/receiver/LoginReceiver.java +++ b/basic/core/src/main/java/per/goweii/basic/core/receiver/LoginReceiver.java @@ -5,7 +5,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.support.v4.content.LocalBroadcastManager; + +import androidx.localbroadcastmanager.content.LocalBroadcastManager; import per.goweii.basic.utils.Utils; import per.goweii.basic.utils.listener.SimpleCallback; @@ -13,8 +14,6 @@ /** * @author CuiZhen * @date 2019/5/16 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class LoginReceiver extends BroadcastReceiver { diff --git a/basic_core/src/main/java/per/goweii/basic/core/surface/activity/SingleFragActivity.java b/basic/core/src/main/java/per/goweii/basic/core/surface/activity/SingleFragActivity.java similarity index 74% rename from basic_core/src/main/java/per/goweii/basic/core/surface/activity/SingleFragActivity.java rename to basic/core/src/main/java/per/goweii/basic/core/surface/activity/SingleFragActivity.java index 56919882..2ed441b4 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/surface/activity/SingleFragActivity.java +++ b/basic/core/src/main/java/per/goweii/basic/core/surface/activity/SingleFragActivity.java @@ -2,14 +2,20 @@ import android.content.Context; import android.content.Intent; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; +import android.view.LayoutInflater; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import androidx.viewbinding.ViewBinding; import per.goweii.basic.core.R; import per.goweii.basic.core.base.BaseActivity; -import per.goweii.basic.core.surface.fragment.NotFoundFragment; +import per.goweii.basic.core.databinding.BasicCoreActivitySingleFragBinding; import per.goweii.basic.core.mvp.MvpPresenter; +import per.goweii.basic.core.surface.fragment.NotFoundFragment; /** * 描述: @@ -27,9 +33,10 @@ public static void start(Context context, Class fragmentClass) { context.startActivity(intent); } + @Nullable @Override - protected int getLayoutId() { - return R.layout.basic_core_activity_single_frag; + protected ViewBinding initViewBinding(@NonNull LayoutInflater inflater) { + return BasicCoreActivitySingleFragBinding.inflate(inflater); } @Override diff --git a/basic_core/src/main/java/per/goweii/basic/core/surface/fragment/NotFoundFragment.java b/basic/core/src/main/java/per/goweii/basic/core/surface/fragment/NotFoundFragment.java similarity index 67% rename from basic_core/src/main/java/per/goweii/basic/core/surface/fragment/NotFoundFragment.java rename to basic/core/src/main/java/per/goweii/basic/core/surface/fragment/NotFoundFragment.java index c0ec3343..7411fd0a 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/surface/fragment/NotFoundFragment.java +++ b/basic/core/src/main/java/per/goweii/basic/core/surface/fragment/NotFoundFragment.java @@ -2,10 +2,17 @@ import android.os.Bundle; import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.ViewGroup; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewbinding.ViewBinding; + import per.goweii.basic.core.R; import per.goweii.basic.core.base.BaseFragment; +import per.goweii.basic.core.databinding.BasicCoreFragmentNotFoundBinding; import per.goweii.basic.core.mvp.MvpPresenter; /** @@ -16,7 +23,7 @@ */ public class NotFoundFragment extends BaseFragment { - public static NotFoundFragment create(String msg){ + public static NotFoundFragment create(String msg) { NotFoundFragment fragment = new NotFoundFragment(); Bundle args = new Bundle(1); args.putString("msg", msg); @@ -24,9 +31,10 @@ public static NotFoundFragment create(String msg){ return fragment; } + @Nullable @Override - protected int getLayoutRes() { - return R.layout.basic_core_fragment_not_found; + protected ViewBinding initViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container) { + return BasicCoreFragmentNotFoundBinding.inflate(inflater, container, false); } @Override diff --git a/basic/core/src/main/java/per/goweii/basic/core/utils/BitmapProcessor.java b/basic/core/src/main/java/per/goweii/basic/core/utils/BitmapProcessor.java new file mode 100644 index 00000000..49dd2094 --- /dev/null +++ b/basic/core/src/main/java/per/goweii/basic/core/utils/BitmapProcessor.java @@ -0,0 +1,221 @@ +package per.goweii.basic.core.utils; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.Rect; +import android.view.View; +import android.widget.ImageView; + +/** + * @author CuiZhen + */ +class BitmapProcessor { + private static BitmapProcessor INSTANCE = null; + + private boolean mRealTimeMode = false; + + private PaintFlagsDrawFilter mFilter = null; + private Paint mPaint = null; + private Canvas mCanvas = null; + + static BitmapProcessor get() { + if (INSTANCE == null) { + synchronized (BitmapProcessor.class) { + if (INSTANCE == null) { + INSTANCE = new BitmapProcessor(); + } + } + } + return INSTANCE; + } + + private BitmapProcessor() { + } + + void realTimeMode(boolean realTimeMode) { + mRealTimeMode = realTimeMode; + if (mRealTimeMode) { + prepare(); + } else { + recycle(); + } + } + + private void prepare() { + if (mFilter == null) { + mFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + } + if (mPaint == null) { + mPaint = new Paint(); + } + if (mCanvas == null) { + mCanvas = new Canvas(); + } + } + + private void recycle() { + mFilter = null; + mPaint = null; + mCanvas = null; + } + + private Canvas reuseOrCreateCanvas() { + final Canvas canvas; + if (mRealTimeMode) { + prepare(); + canvas = mCanvas; + } else { + canvas = new Canvas(); + } + return canvas; + } + + private PaintFlagsDrawFilter reuseOrCreateFilter() { + final PaintFlagsDrawFilter filter; + if (mRealTimeMode) { + prepare(); + filter = mFilter; + } else { + filter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + } + return filter; + } + + private Paint reuseOrCreatePaint() { + final Paint paint; + if (mRealTimeMode) { + prepare(); + paint = mPaint; + } else { + paint = new Paint(); + } + return paint; + } + + Bitmap snapshot(View from, int bgColor, int fgColor, float scale, boolean antiAlias) { + final float newScale = scale > 0 ? scale : 1; + final int w = (int) (from.getWidth() * newScale); + final int h = (int) (from.getHeight() * newScale); + Bitmap output = Bitmap.createBitmap(w <= 0 ? 1 : w, h <= 0 ? 1 : h, Bitmap.Config.ARGB_8888); + final Canvas canvas = reuseOrCreateCanvas(); + canvas.setBitmap(output); + if (antiAlias) { + canvas.setDrawFilter(reuseOrCreateFilter()); + } else { + canvas.setDrawFilter(null); + } + canvas.save(); + canvas.scale(newScale, newScale); + if (bgColor != 0) { + canvas.drawColor(bgColor); + } + from.draw(canvas); + if (fgColor != 0) { + canvas.drawColor(fgColor); + } + canvas.restore(); + return output; + } + + Bitmap clip(Bitmap bitmap, View from, ImageView into, boolean fitXY, boolean antiAlias) { + int[] lf = new int[2]; + from.getLocationOnScreen(lf); + int[] lt = new int[2]; + into.getLocationOnScreen(lt); + int bw = bitmap.getWidth(); + int bh = bitmap.getHeight(); + float sx = (float) bw / (float) from.getWidth(); + float sh = (float) bh / (float) from.getHeight(); + Rect rf = new Rect( + (int) ((lt[0] - lf[0]) * sx), + (int) ((lt[1] - lf[1]) * sh), + (int) ((lt[0] - lf[0]) * sx + into.getWidth() * sx), + (int) ((lt[1] - lf[1]) * sh + into.getHeight() * sh) + ); + Rect rt = new Rect(0, 0, into.getWidth(), into.getHeight()); + if (!fitXY) { + float s = Math.max( + (float) into.getWidth() / (float) rf.width(), + (float) into.getHeight() / (float) rf.height() + ); + if (s > 1) { + rt.right = rf.width(); + rt.bottom = rf.height(); + } + } + Bitmap output = Bitmap.createBitmap(rt.width(), rt.height(), Bitmap.Config.ARGB_8888); + final Canvas canvas = reuseOrCreateCanvas(); + canvas.setBitmap(output); + if (antiAlias) { + canvas.setDrawFilter(reuseOrCreateFilter()); + } else { + canvas.setDrawFilter(null); + } + Paint paint = null; + if (antiAlias) { + paint = reuseOrCreatePaint(); + paint.setXfermode(null); + paint.setAntiAlias(true); + } + canvas.drawBitmap(bitmap, rf, rt, paint); + return output; + } + + private static class StopDrawException extends Exception { + } + + Bitmap scaleBitmap(Bitmap bitmap, float scale) { + return scaleBitmap(bitmap, scale, true); + } + + Bitmap scaleBitmap(Bitmap bitmap, float scale, boolean antiAlias) { + final int iw = bitmap.getWidth(); + final int ih = bitmap.getHeight(); + final int ow = (int) (iw * scale); + final int oh = (int) (ih * scale); + return scaleBitmap(bitmap, ow, oh, antiAlias); + } + + Bitmap scaleBitmap(Bitmap bitmap, int w, int h) { + return scaleBitmap(bitmap, w, h, true); + } + + Bitmap scaleBitmap(Bitmap bitmap, int w, int h, boolean antiAlias) { + final Bitmap.Config config = bitmap.getConfig(); + final Bitmap.Config newConfig; + switch (config) { + case RGB_565: + newConfig = Bitmap.Config.RGB_565; + break; + case ALPHA_8: + newConfig = Bitmap.Config.ALPHA_8; + break; + case ARGB_4444: + case ARGB_8888: + default: + newConfig = Bitmap.Config.ARGB_8888; + break; + } + Bitmap output = Bitmap.createBitmap(w, h, newConfig); + final Canvas canvas = reuseOrCreateCanvas(); + canvas.setBitmap(output); + if (antiAlias) { + canvas.setDrawFilter(reuseOrCreateFilter()); + } else { + canvas.setDrawFilter(null); + } + Paint paint = null; + if (antiAlias) { + paint = reuseOrCreatePaint(); + paint.setXfermode(null); + paint.setAntiAlias(true); + } + canvas.drawBitmap(bitmap, + new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()), + new Rect(0, 0, w, h), + paint); + return output; + } +} diff --git a/basic/core/src/main/java/per/goweii/basic/core/utils/FastBlur.java b/basic/core/src/main/java/per/goweii/basic/core/utils/FastBlur.java new file mode 100644 index 00000000..7c56f2e9 --- /dev/null +++ b/basic/core/src/main/java/per/goweii/basic/core/utils/FastBlur.java @@ -0,0 +1,292 @@ +package per.goweii.basic.core.utils; + +import android.graphics.Bitmap; + +import java.util.Objects; + +/** + * 毛玻璃处理 + */ +public final class FastBlur { + + private static FastBlur INSTANCE = null; + + private FastBlur() { + } + + public static FastBlur get() { + if (INSTANCE == null) { + synchronized (FastBlur.class) { + if (INSTANCE == null) { + INSTANCE = new FastBlur(); + } + } + } + return INSTANCE; + } + + /** + * 模糊 + * 采用FastBlur算法 + * 缩小-->模糊 + * + * @param originalBitmap 原图 + * @param radius 模糊半径 + * @param scale 缩放因子(>=1) + * @return 模糊Bitmap + */ + public Bitmap process(final Bitmap originalBitmap, + final float radius, + final float scale, + final boolean keepSize, + final boolean recycleOriginal) { + Objects.requireNonNull(originalBitmap, "待模糊Bitmap不能为空"); + final int newRadius = radius < 0 ? 0 : (int) radius; + final float newScale = scale <= 0 ? 1 : scale; + if (newRadius == 0) { + if (newScale == 1) { + return originalBitmap; + } + Bitmap scaleBitmap = BitmapProcessor.get().scaleBitmap(originalBitmap, newScale); + if (recycleOriginal) { + originalBitmap.recycle(); + } + return scaleBitmap; + } + if (newScale == 1) { + Bitmap output = blur(originalBitmap, newRadius); + if (recycleOriginal) { + originalBitmap.recycle(); + } + return output; + } + final int width = originalBitmap.getWidth(); + final int height = originalBitmap.getHeight(); + Bitmap input = BitmapProcessor.get().scaleBitmap(originalBitmap, newScale); + if (recycleOriginal) { + originalBitmap.recycle(); + } + Bitmap output = blur(input, newRadius); + input.recycle(); + if (!keepSize) { + return output; + } + Bitmap outputScaled = BitmapProcessor.get().scaleBitmap(output, width, height); + output.recycle(); + return outputScaled; + } + + public void recycle() { + INSTANCE = null; + } + + /** + * 模糊 + * 直接模糊原图 + * + * @param originalBitmap 原图 + * @param radius 模糊半径 + * @return 模糊Bitmap + */ + private static Bitmap blur(Bitmap originalBitmap, int radius) { + + if (radius <= 0) { + return originalBitmap; + } + + Bitmap bitmap = originalBitmap.copy(Bitmap.Config.ARGB_8888, true); + + int w = bitmap.getWidth(); + int h = bitmap.getHeight(); + + int[] pix = new int[w * h]; + bitmap.getPixels(pix, 0, w, 0, 0, w, h); + + int wm = w - 1; + int hm = h - 1; + int wh = w * h; + int div = radius + radius + 1; + + int[] r = new int[wh]; + int[] g = new int[wh]; + int[] b = new int[wh]; + int rsum, gsum, bsum, x, y, i, p, yp, yi, yw; + int[] vmin = new int[Math.max(w, h)]; + + int divsum = (div + 1) >> 1; + divsum *= divsum; + int[] dv = new int[256 * divsum]; + for (i = 0; i < 256 * divsum; i++) { + dv[i] = (i / divsum); + } + + yw = yi = 0; + + int[][] stack = new int[div][3]; + int stackpointer; + int stackstart; + int[] sir; + int rbs; + int r1 = radius + 1; + int routsum, goutsum, boutsum; + int rinsum, ginsum, binsum; + + for (y = 0; y < h; y++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + for (i = -radius; i <= radius; i++) { + p = pix[yi + Math.min(wm, Math.max(i, 0))]; + sir = stack[i + radius]; + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + rbs = r1 - Math.abs(i); + rsum += sir[0] * rbs; + gsum += sir[1] * rbs; + bsum += sir[2] * rbs; + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + } + stackpointer = radius; + + for (x = 0; x < w; x++) { + + r[yi] = dv[rsum]; + g[yi] = dv[gsum]; + b[yi] = dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (y == 0) { + vmin[x] = Math.min(x + radius + 1, wm); + } + p = pix[yw + vmin[x]]; + + sir[0] = (p & 0xff0000) >> 16; + sir[1] = (p & 0x00ff00) >> 8; + sir[2] = (p & 0x0000ff); + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[(stackpointer) % div]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi++; + } + yw += w; + } + for (x = 0; x < w; x++) { + rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0; + yp = -radius * w; + for (i = -radius; i <= radius; i++) { + yi = Math.max(0, yp) + x; + + sir = stack[i + radius]; + + sir[0] = r[yi]; + sir[1] = g[yi]; + sir[2] = b[yi]; + + rbs = r1 - Math.abs(i); + + rsum += r[yi] * rbs; + gsum += g[yi] * rbs; + bsum += b[yi] * rbs; + + if (i > 0) { + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + } else { + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + } + + if (i < hm) { + yp += w; + } + } + yi = x; + stackpointer = radius; + for (y = 0; y < h; y++) { + // Preserve alpha channel: ( 0xff000000 & pix[yi] ) + pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum]; + + rsum -= routsum; + gsum -= goutsum; + bsum -= boutsum; + + stackstart = stackpointer - radius + div; + sir = stack[stackstart % div]; + + routsum -= sir[0]; + goutsum -= sir[1]; + boutsum -= sir[2]; + + if (x == 0) { + vmin[y] = Math.min(y + r1, hm) * w; + } + p = x + vmin[y]; + + sir[0] = r[p]; + sir[1] = g[p]; + sir[2] = b[p]; + + rinsum += sir[0]; + ginsum += sir[1]; + binsum += sir[2]; + + rsum += rinsum; + gsum += ginsum; + bsum += binsum; + + stackpointer = (stackpointer + 1) % div; + sir = stack[stackpointer]; + + routsum += sir[0]; + goutsum += sir[1]; + boutsum += sir[2]; + + rinsum -= sir[0]; + ginsum -= sir[1]; + binsum -= sir[2]; + + yi += w; + } + } + + bitmap.setPixels(pix, 0, w, 0, 0, w, h); + + return (bitmap); + } +} diff --git a/basic/core/src/main/java/per/goweii/basic/core/utils/JsonFormatUtils.kt b/basic/core/src/main/java/per/goweii/basic/core/utils/JsonFormatUtils.kt new file mode 100644 index 00000000..b2eb643f --- /dev/null +++ b/basic/core/src/main/java/per/goweii/basic/core/utils/JsonFormatUtils.kt @@ -0,0 +1,16 @@ +package per.goweii.basic.core.utils + +import com.google.gson.Gson +import com.google.gson.GsonBuilder + +/** + * @author CuiZhen + * @date 2020/4/18 + */ +object JsonFormatUtils { + private val gson: Gson = GsonBuilder().setPrettyPrinting().create() + + fun toJson(any: Any?): String { + return gson.toJson(any) + } +} \ No newline at end of file diff --git a/basic_core/src/main/java/per/goweii/basic/core/utils/LoadingBarManager.java b/basic/core/src/main/java/per/goweii/basic/core/utils/LoadingBarManager.java similarity index 98% rename from basic_core/src/main/java/per/goweii/basic/core/utils/LoadingBarManager.java rename to basic/core/src/main/java/per/goweii/basic/core/utils/LoadingBarManager.java index 02309049..fc973de8 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/utils/LoadingBarManager.java +++ b/basic/core/src/main/java/per/goweii/basic/core/utils/LoadingBarManager.java @@ -8,8 +8,6 @@ /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class LoadingBarManager implements Runnable { diff --git a/basic_core/src/main/java/per/goweii/basic/core/utils/SmartRefreshUtils.java b/basic/core/src/main/java/per/goweii/basic/core/utils/SmartRefreshUtils.java similarity index 88% rename from basic_core/src/main/java/per/goweii/basic/core/utils/SmartRefreshUtils.java rename to basic/core/src/main/java/per/goweii/basic/core/utils/SmartRefreshUtils.java index dea2ec16..3a037ad4 100644 --- a/basic_core/src/main/java/per/goweii/basic/core/utils/SmartRefreshUtils.java +++ b/basic/core/src/main/java/per/goweii/basic/core/utils/SmartRefreshUtils.java @@ -1,11 +1,11 @@ package per.goweii.basic.core.utils; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; -import com.scwang.smartrefresh.layout.api.RefreshLayout; -import com.scwang.smartrefresh.layout.listener.OnLoadMoreListener; -import com.scwang.smartrefresh.layout.listener.OnRefreshListener; +import com.scwang.smart.refresh.layout.api.RefreshLayout; +import com.scwang.smart.refresh.layout.listener.OnLoadMoreListener; +import com.scwang.smart.refresh.layout.listener.OnRefreshListener; import per.goweii.basic.core.common.Config; @@ -16,15 +16,10 @@ * @date 2018/7/6-下午5:06 */ public class SmartRefreshUtils { - private static final int FIRST_PAGE = 0; - private final RefreshLayout mRefreshLayout; private RefreshListener mRefreshListener = null; private LoadMoreListener mLoadMoreListener = null; - private int currentPage = FIRST_PAGE; - private int perPageCount = 0; - public static SmartRefreshUtils with(RefreshLayout layout) { return new SmartRefreshUtils(layout); } diff --git a/basic/core/src/main/java/per/goweii/basic/core/widget/GrayLayout.kt b/basic/core/src/main/java/per/goweii/basic/core/widget/GrayLayout.kt new file mode 100644 index 00000000..d452d212 --- /dev/null +++ b/basic/core/src/main/java/per/goweii/basic/core/widget/GrayLayout.kt @@ -0,0 +1,68 @@ +package per.goweii.basic.core.widget + +import android.app.Activity +import android.content.Context +import android.graphics.Canvas +import android.graphics.ColorMatrix +import android.graphics.ColorMatrixColorFilter +import android.graphics.Paint +import android.util.AttributeSet +import android.view.ViewGroup +import android.widget.FrameLayout + +/** + * @author CuiZhen + * @date 2020/4/5 + */ +class GrayLayout : FrameLayout { + + companion object { + fun wrap(activity: Activity) { + val decorView = activity.window.decorView as ViewGroup + val decorChild = decorView.getChildAt(0) + decorView.removeViewInLayout(decorChild) + val grayLayout = GrayLayout(decorView.context) + grayLayout.addViewInLayout(decorChild, 0, LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)) + decorView.addView(grayLayout) + } + } + + constructor(context: Context) : super(context) + constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) + constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) + + private val colorMatrix = ColorMatrix().apply { + setSaturation(0F) + } + + private val paint = Paint().apply { + colorFilter = ColorMatrixColorFilter(colorMatrix) + } + + private var opened: Boolean = false + + fun isOpen(): Boolean { + return opened + } + + fun setOpened(opened: Boolean) { + this.opened = opened + if (opened) { + colorMatrix.setSaturation(0F) + } else { + colorMatrix.setSaturation(1F) + } + } + + override fun onDraw(canvas: Canvas) { + canvas.saveLayer(null, paint) + super.onDraw(canvas) + canvas.restore() + } + + override fun dispatchDraw(canvas: Canvas) { + canvas.saveLayer(null, paint) + super.dispatchDraw(canvas) + canvas.restore() + } +} \ No newline at end of file diff --git a/basic_core/src/main/res/layout/basic_core_activity_single_frag.xml b/basic/core/src/main/res/layout/basic_core_activity_single_frag.xml similarity index 100% rename from basic_core/src/main/res/layout/basic_core_activity_single_frag.xml rename to basic/core/src/main/res/layout/basic_core_activity_single_frag.xml diff --git a/basic_core/src/main/res/layout/basic_core_fragment_not_found.xml b/basic/core/src/main/res/layout/basic_core_fragment_not_found.xml similarity index 60% rename from basic_core/src/main/res/layout/basic_core_fragment_not_found.xml rename to basic/core/src/main/res/layout/basic_core_fragment_not_found.xml index a97033ea..e2baa512 100644 --- a/basic_core/src/main/res/layout/basic_core_fragment_not_found.xml +++ b/basic/core/src/main/res/layout/basic_core_fragment_not_found.xml @@ -4,16 +4,9 @@ android:layout_height="match_parent" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" + android:gravity="center" android:padding="15dp" - android:background="@color/invert"> - - + android:background="?colorBackground"> \ No newline at end of file diff --git a/basic/core/src/main/res/values/strings.xml b/basic/core/src/main/res/values/strings.xml new file mode 100644 index 00000000..85420055 --- /dev/null +++ b/basic/core/src/main/res/values/strings.xml @@ -0,0 +1,2 @@ + + diff --git a/basic/l10n/build.gradle b/basic/l10n/build.gradle new file mode 100644 index 00000000..f2ecc055 --- /dev/null +++ b/basic/l10n/build.gradle @@ -0,0 +1,11 @@ +apply plugin: 'com.android.library' +apply from: "${rootDir.path}/gradle/basic.gradle" + +android { + namespace "per.goweii.basic.l10n" + resourcePrefix "" +} + +dependencies { + api rootProject.ext.dependencies.androidxAppcompat +} diff --git a/basic_utils/proguard-rules.pro b/basic/l10n/proguard-rules.pro similarity index 94% rename from basic_utils/proguard-rules.pro rename to basic/l10n/proguard-rules.pro index f1b42451..481bb434 100644 --- a/basic_utils/proguard-rules.pro +++ b/basic/l10n/proguard-rules.pro @@ -18,4 +18,4 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/basic/l10n/src/main/AndroidManifest.xml b/basic/l10n/src/main/AndroidManifest.xml new file mode 100644 index 00000000..1a9db53d --- /dev/null +++ b/basic/l10n/src/main/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/basic/l10n/src/main/java/per/goweii/basic/l10n/L10nManager.java b/basic/l10n/src/main/java/per/goweii/basic/l10n/L10nManager.java new file mode 100644 index 00000000..3beb8b52 --- /dev/null +++ b/basic/l10n/src/main/java/per/goweii/basic/l10n/L10nManager.java @@ -0,0 +1,78 @@ +package per.goweii.basic.l10n; + +import android.content.Context; +import android.content.res.XmlResourceParser; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatDelegate; +import androidx.core.app.LocaleManagerCompat; +import androidx.core.os.LocaleListCompat; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; + +public class L10nManager { + private static final String ANDROID_NAMESPACE = "http://schemas.android.com/apk/res/android"; + + @Nullable + public static Locale getApplicationLocale() { + LocaleListCompat locales = AppCompatDelegate.getApplicationLocales(); + if (!locales.isEmpty()) { + return locales.get(0); + } + return null; + } + + @Nullable + public static Locale getSystemLocale(@NonNull Context context) { + LocaleListCompat locales = LocaleManagerCompat.getSystemLocales(context); + if (!locales.isEmpty()) { + return locales.get(0); + } + return null; + } + + public static void setApplicationLocales(@Nullable String languageCode) { + LocaleListCompat appLocale; + if (languageCode == null || languageCode.isEmpty()) { + appLocale = LocaleListCompat.getEmptyLocaleList(); + } else { + appLocale = LocaleListCompat.forLanguageTags(languageCode); + } + AppCompatDelegate.setApplicationLocales(appLocale); + } + + public static void setApplicationLocales(@Nullable Locale locale) { + LocaleListCompat appLocale; + if (locale == null) { + appLocale = LocaleListCompat.getEmptyLocaleList(); + } else { + appLocale = LocaleListCompat.create(locale); + } + AppCompatDelegate.setApplicationLocales(appLocale); + } + + @NonNull + public static List getSupportedLocales(@NonNull Context context) { + try (XmlResourceParser parser = context.getResources().getXml(R.xml.locale_config)) { + final List locales = new ArrayList<>(); + int event = parser.next(); + while (event != XmlResourceParser.END_DOCUMENT) { + if (parser.getEventType() == XmlResourceParser.START_TAG) { + if ("locale".equals(parser.getName())) { + String name = parser.getAttributeValue(ANDROID_NAMESPACE, "name"); + locales.add(Locale.forLanguageTag(name)); + } + } + event = parser.next(); + } + return locales; + } catch (Exception e) { + e.printStackTrace(); + } + return Collections.emptyList(); + } +} diff --git a/basic/l10n/src/main/res/values-ar/strings.xml b/basic/l10n/src/main/res/values-ar/strings.xml new file mode 100644 index 00000000..064f5414 --- /dev/null +++ b/basic/l10n/src/main/res/values-ar/strings.xml @@ -0,0 +1,302 @@ + + + وان أندرويد + + فشل الاتصال بالشبكة، يرجى التحقق من إعدادات الشبكة + حدث خطأ في الوصول، يرجى المحاولة لاحقًا + + تأكيد + إلغاء + إغلاق + + + تم العثور على إصدار جديد + تم العثور على إصدار تجريبي + تحديث الآن + ليس الآن + + + جارٍ تنزيل التحديث... + تثبيت الآن + + + عدم السماح مؤقتًا + اذهب للسماح + اذهب للإعدادات + غير معروف + التقويم + الكاميرا + جهات الاتصال + الموقع + الميكروفون + الهاتف + الرسائل + المستشعرات + مساحة التخزين + ذو صلة + نحتاج إلى إذن %s لضمان التشغيل السليم للتطبيق، يرجى النقر للسماح. + لقد رفضت إذن %s، قد يؤثر ذلك على الوظائف، يرجى الذهاب للإعدادات لتمكين الإذن. + + + "سياسة الخصوصية" + شكرًا لاستخدامك وان أندرويد!\nقمنا بتحديث "سياسة الخصوصية" وفقًا لأحدث المتطلبات التنظيمية، ونوضح ما يلي:\n1. لجلب الوظائف الأساسية، سنجمع ونستخدم المعلومات الضرورية؛\n2. بناءً على إذنك الصريح، سنطلب إذن التخزين لحفظ صور مشاركة المقالات، ويمكنك رفض أو إلغاء الإذن؛\n3. سنتخذ تدابير أمنية لحماية معلوماتك؛\n4. لن نحصل أو نشارك أو نقدم معلوماتك لطرف ثالث دون إذنك. + + %d B + %d %% + "فشل التنزيل، يمكنك الذهاب إلى مستودع Github: أنا - إعدادات النظام - حولنا - مستودع المشروع" + + هذه الصفحة مقدمة من %s + العودة إلى الصفحة الرئيسية + مشاركة إلى الساحة + المفضلة + إشارة مرجعية + تحديث + العودة للأعلى + حظر الويب + خروج + استخدم المسافة للفصل بين الكلمات المفتاحية + يرجى إدخال الرابط + معلومات عنا + التحقق من التحديثات + الموقع الرسمي + محتوى الموقع + مستودع المشروع + طلب نسخة تجريبية مغلقة + تم تطوير هذا التطبيق بشكل مستقل بواسطة Goweii + كلمة مرور وان + عن المؤلف + مرحبًا بك + نقاطي + لوحة الصدارة + حدث خطأ + نسخ السجلات + إغلاق + إعادة التشغيل + مشاركاتي + مشاريع مفتوحة المصدر + إشاراتي المرجعية + مسح + لم يتم الحصول على إذن الكاميرا + انقر لإعادة المحاولة + العودة لمسح الرمز + الإعدادات + وضع السمة + اتبع النظام + وضع النهار + الوضع الليلي + تذكير الإشارات المرجعية + عند فتح التطبيق سيتم تذكيرك بأحدث الإشارات المرجعية + إظهار الأسئلة والأجوبة + إظهار تبويب الأسئلة والأجوبة في الصفحة الرئيسية بعد البدء + عرض السلايدر + عند التفعيل، سيتم عرض السلايدر في الصفحة الرئيسية + عرض المثبت + عند التفعيل، سيتم عرض المقالات المثبتة في الصفحة الرئيسية + البيانات الشخصية + معرف وان + اسم المستخدم + النقاط: + الترتيب: + اسم النطاق + مثال: wanandroid.com + إلغاء + تأكيد + فتح الرابط + فهمت + حفظ + التعرف على رمز QR في الصورة + يرجى الإدخال + لم أقرر بعد ماذا أفعل بهذه الكلمة، كيف اكتشفتها؟ + تجاهل + انقر مرتين في أي مكان لإضافة المقالة للمفضلة + اضغط مطولًا على صورة الويب لعرضها بالحجم الكامل + نسخ الرابط + فتح في المتصفح + كلمة مرور وان للويب + اختر طريقة المشاركة + صورة طويلة + رمز QR + دروس الكتب + الصفحة الرئيسية + اذهب للتسجيل + تسجيل الدخول + المستوى: + مجموعاتي + الأسئلة والأجوبة + جديد + مثبت + حذف + خاصتي + اضغط مطولًا على زر الرجوع لمفاجأة! + مشاركة + فتح + قائمة المقالات + نظام المعرفة + سجل القراءة + اضغط مطولًا لتخصيص قائمة الحظر + لا تحظر + مسح ذاكرة التخزين المؤقت + سياسة الخصوصية + تسجيل الخروج + مشاركة المقال + عنوان المقال + تحديث العنوان + حتى 100 حرف + رابط المقال + مثال: https://wanandroid.com/blog/show/2577 + 1. يمكنك مشاركة أي مقال جيد، ليس بالضرورة أن يكون أصليًا! سيتم إدراج المقالات المقدمة في علامة التبويب الساحة;\n2. سيتم قبول مدونات المواقع الرسمية مثل CSDN، Juejin، Jiashu مباشرة دون مراجعة;\n3. سيتم مراجعة المواقع الشخصية الأخرى، لا ترسل روابط غير صالحة حتى لا يؤثر ذلك على حسابك;\n4. إذا وجدت خطأ، يمكنك إرسال سجل الأخطاء لمساعدتنا في تحسين الموقع.\n5. نظرًا لأن الموقع مطور ومدار بشكل فردي، سنحاول مراجعة المقالات خلال 24 ساعة، ولكن قد يتأخر أحيانًا، يرجى التحلي بالصبر... + تم الإنشاء بواسطة تطبيق وان أندرويد + حفظ في الألبوم + شارك الآن + عنوان الموقع + جارٍ التحميل + موافق + الخروج من التطبيق + اشتر له كوب قهوة~ + اذهب لتسجيل الدخول + تسجيل + عمليات البحث الشائعة + عمليات البحث السابقة + إكمال + الساحة + من لا يثق بنفسه لا قيمة لجهده + جيانشو + مجموعة QQ + يو~\nالتطبيق رائع\nاشترِ لك كوب قهوة + اسحب للأعلى + انقر للحفظ في الألبوم + امسح الرمز لتنزيل وتثبيت التطبيق + لا يوجد شيء~ + كيف حدث الخطأ مرة أخرى~ + تم التعلم %d%% + تسجيل الدخول النقاط %s + تعديل + نصيحة + روابط الموقع + لم يتم التعلم + تم التعلم + تم النسخ + إفلات في دروس الكتب + هل أنت متأكد من مسح سجل البحث؟ + ليس مستخدمًا تجريبيًا + تفعيل تسجيل الدخول البيومتري + تسجيل الدخول باستخدام المصادقة البيومترية + اشترِ للمطور كوب قهوة!\nتطبيق مجاني بالكامل!\nتطبيق جميل وسهل الاستخدام ومجاني بالكامل!\nألا تشتري لي كوب قهوة؟\nلم يعد لدي طاقة للاستمرار في الصيانة! + اذهب للضيافة + يرجى تسجيل الدخول أولاً + تم التفعيل بنجاح، سيسري بعد إعادة تشغيل التطبيق + رمز التفعيل غير صالح + لقد وجدت رمز تفعيل!\nرمز التفعيل مرتبط بالحساب الحالي فقط، إذا غيرت الجهاز أو الحساب يجب إعادة التفعيل، بعد التفعيل سيتم إزالة جميع الإعلانات، هل تريد التفعيل الآن؟ + تفعيل + يرجى تسجيل الدخول لاستخدام هذه الميزة + هذه الميزة متاحة فقط لحسابات المطورين + [كلمة مرور وان] هذا رمز تفعيل، متاح فقط لحسابات معينة، يرجى تجاهل هذه الرسالة + افتح أجمل تطبيق وان أندرويد للتفعيل + ###توليد رمز التفعيل###\n\n!!!تحذير!!!\n\nهذه الميزة متاحة فقط للمطورين + [كلمة مرور وان] صديقك شارك معك رابطًا، يرجى تجاهل هذه الرسالة %s وافتح أجمل تطبيق وان أندرويد لعرض الصفحة أو المقالة + نسخ + اذهب للتحديث + لقد اكتشفت كلمة مرور غامضة!\nللأسف الإصدار الحالي لا يدعمها، يرجى التحديث من الإعدادات والمحاولة مرة أخرى! + لقد اكتشفت مستخدمًا غامضًا!\nهل تريد زيارة صفحته الرئيسية؟ + اذهب للصفحة الرئيسية + [كلمة مرور وان] صديقك شارك معك مستخدمًا غامضًا، يرجى تجاهل هذه الرسالة %s وافتح أجمل تطبيق وان أندرويد لكشف الغموض + لقد وجدت رابط صفحة ويب!\nهل تريد فتحه؟ + لقد وجدت رقم QQ!\n%s\nهل تريد فتح QQ وإضافة صديق؟ + إضافة صديق + [كلمة مرور وان] صديقك طلب لك قهوة لشخصين، يرجى تجاهل هذه الرسالة %s وافتح أجمل تطبيق وان أندرويد للاستلام + القائمة البيضاء فقط + حظر القائمة السوداء + يرجى إدخال اسم المستخدم + يرجى إدخال البريد الإلكتروني + يرجى إدخال كلمة المرور + اسحب للأسفل للتحديث + جارٍ التحديث + حرر للتحديث + تم التحديث + فشل التحديث + تم + مسح رمز QR يتطلب إذن الكاميرا + انقر للتقديم + تم الحفظ بنجاح + فشل الحفظ + فشل التفويض + تم التحميل بنجاح + فشل التحميل + جارٍ التحميل (%d%%) + تمت الإضافة للمفضلة + مجهول + تمت الإضافة إلى إشاراتي المرجعية + فشل الإضافة إلى إشاراتي المرجعية + تمت الإزالة من إشاراتي المرجعية + فشل الإزالة من إشاراتي المرجعية + بدون + تلاشي + تكبير + انزلاق من الأسفل + انزلاق من اليسار + انزلاق من اليمين + اضغط مطولًا على زر الرجوع لمزيد من القوائم المختصرة~ + التالي + انقر مرتين في أي مكان للإضافة السريعة للمفضلة~ + اضغط مطولًا على صورة الويب لمعاينة صورة أكبر~ + هل تريد المتابعة في القراءة؟ + رفض إذن الكاميرا سيمنع المسح + إعادة التقديم + تم رفض إذن الكاميرا + اذهب للإعدادات + غير قادر على الحصول على إذن الكاميرا + انقر للحصول عليه + اختيار صورة يتطلب إذن التخزين + رفض إذن التخزين سيمنع اختيار الصور + تم رفض إذن التخزين + لم يتم التعرف على رمز QR + فشل فتح الصورة + النظام + التنقل + س&ج + للحصول على تحديثات النسخة التجريبية، يرجى الانضمام إلى المجموعة (انظر حول المؤلف) وذكر رقم وان (انظر البيانات الشخصية).\n\nملاحظة: يجب تسجيل الدخول وتحديث التطبيق إلى أحدث إصدار رسمي قبل التقديم للنسخة التجريبية + تم العثور على إصدار جديد %s + تم العثور على إصدار تجريبي %s + هذا هو أحدث إصدار + %s(%d) + رسائل جديدة + رسائل سابقة + تم تفعيل تسجيل الدخول السريع بنجاح + تم تسجيل الدخول السريع بنجاح + فشل تسجيل الدخول السريع، يرجى استخدام كلمة المرور + هل تريد تفعيل تسجيل الدخول السريع؟ + ليس الآن + تسجيل الدخول السريع + تسجيل الدخول بكلمة المرور + يرجى التأكد من تطابق كلمة المرور مرتين + مقال + موقع + القائمة البيضاء + القائمة السوداء + هل أنت متأكد من حذف الكل؟ + الرابط فارغ + فشل الحذف + هل أنت متأكد من مسح ذاكرة التخزين المؤقت؟ + هل أنت متأكد من تسجيل الخروج؟ + + اللغة + + إعلان + + الرائج اليوم + + إعدادات الذكاء الاصطناعي + معطل + تفعيل الذكاء الاصطناعي + يمكن للذكاء الاصطناعي تلخيص المقالات + مزود النموذج + مفتاح API + النموذج + جارٍ التفكير... + استماع + ميزة الذكاء الاصطناعي غير مفعلة، يمكنك الذهاب للإعدادات لتفعيلها + مفتاح الذكاء الاصطناعي غير مهيأ + + قراءة المقال + قراءة صوتية للمقالات مع عناصر تحكم سريعة في التشغيل. + \ No newline at end of file diff --git a/basic/l10n/src/main/res/values-es/strings.xml b/basic/l10n/src/main/res/values-es/strings.xml new file mode 100644 index 00000000..2a3e1764 --- /dev/null +++ b/basic/l10n/src/main/res/values-es/strings.xml @@ -0,0 +1,302 @@ + + + WanAndroid + + Conexión de red fallida, por favor verifica la configuración de red + Error de acceso, inténtalo de nuevo más tarde + + Aceptar + Cancelar + Cerrar + + + Nueva versión encontrada + Versión beta encontrada + Actualizar ahora + No actualizar por ahora + + + Descargando actualización... + Instalar ahora + + + No autorizar por ahora + Autorizar + Ir a configuración + Desconocido + Calendario + Cámara + Contactos + Ubicación + Micrófono + Teléfono + SMS + Sensores + Almacenamiento + Relacionado + Necesitamos el permiso de %s para garantizar el funcionamiento normal de la aplicación. Haz clic para autorizar. + Has denegado el permiso de %s, lo que afectará el funcionamiento normal. Ve a configuración para habilitarlo. + + + "Política de Privacidad" + ¡Gracias por usar WanAndroid!\nHemos actualizado nuestra Política de Privacidad según los últimos requisitos regulatorios:\n1. Para proporcionarte funciones básicas de transacción, recopilaremos y usaremos la información necesaria;\n2. Con tu autorización explícita, obtendremos permiso de almacenamiento para guardar imágenes compartidas de artículos. Puedes rechazar o cancelar la autorización;\n3. Tomaremos medidas de seguridad para proteger tu información;\n4. Sin tu consentimiento, no obtendremos, compartiremos ni proporcionaremos tu información a terceros. + + %d B + %d %% + Descarga fallida, puedes descargar desde el repositorio de Github: Yo - Configuración del sistema - Sobre nosotros - Repositorio del proyecto + + Página proporcionada por %s + Volver a la página principal + Compartir en la plaza + Favorito + Marcador + Actualizar + Volver arriba + Bloqueo de página web + Salir + Usa espacios para separar varias palabras clave + Por favor, introduce la URL + Sobre nosotros + Buscar actualizaciones + Sitio web oficial + Contenido del sitio web + Repositorio del proyecto + Solicitar beta cerrada + Esta APP está desarrollada independientemente por Goweii + Contraseña Wan + Sobre el autor + Bienvenido + Mis puntos + Tabla de clasificación + Ocurrió un error + Copiar registros + Cerrar + Reiniciar + Mis compartidos + Proyectos de código abierto + Mis marcadores + Limpiar + No se obtuvo permiso de cámara + Haz clic para reintentar + Volver a escanear + Configuración + Modo de tema + Seguir el sistema + Modo claro + Modo oscuro + Recordatorio de marcadores + Recibir notificación de los últimos marcadores al abrir la app + Mostrar preguntas y respuestas + Mostrar la pestaña de preguntas y respuestas en la página principal después de iniciar + Mostrar carrusel + Mostrar carrusel en la página principal + Mostrar fijados + Mostrar artículos fijados en la página principal + Datos personales + ID Wan + Nombre de usuario + Puntos: + Ranking: + Dominio + Ej: wanandroid.com + Cancelar + Aceptar + Abrir enlace + Entendido + Guardar + Identificar código QR en la imagen + Por favor, introduce + Ni siquiera he pensado para qué usar esta contraseña, ¿cómo la encontraste? + Ignorar + Haz doble clic en cualquier lugar para marcar como favorito + Mantén presionada la imagen para verla en grande + Copiar enlace + Abrir en el navegador + Contraseña Wan en la web + Elige cómo compartir + Imagen larga + Código QR + Tutoriales de libros + Inicio + Registrarse + Iniciar sesión + Nivel: + Mis colecciones + Preguntas y respuestas + Nuevo + Fijado + Eliminar + Mío + Mantén presionado el botón de retroceso para una sorpresa~ + Compartir + Abrir + Lista de artículos + Sistema de conocimiento + Historial de lectura + Mantén presionado para personalizar la lista de bloqueo + No bloquear + Limpiar caché + Política de privacidad + Cerrar sesión + Compartir artículo + Título del artículo + Actualizar título + Hasta 100 caracteres + Enlace al artículo + Ej: https://wanandroid.com/blog/show/2577 + 1. ¡Puedes compartir cualquier buen artículo, no tiene que ser original! Los artículos enviados aparecerán en la pestaña Plaza;\n2. Los blogs oficiales como CSDN, Juejin, JianShu se aprobarán automáticamente;\n3. Otros sitios personales requerirán revisión. No envíes enlaces inválidos o tu cuenta podría verse afectada;\n4. Si encuentras errores, puedes enviar registros para mejorar el sitio juntos.\n5. El sitio es desarrollado y mantenido por una sola persona, se intentará revisar en 24 horas, pero puede haber retrasos. ¡Gracias por tu paciencia! + Generado por el cliente WanAndroid + Guardar en el álbum + Compartir ahora + Título del sitio web + Cargando + Aceptar + Salir de la aplicación + Invítale un café~ + Iniciar sesión + Registrarse + Búsquedas populares + Búsquedas históricas + Completar + Plaza + Quien no cree en sí mismo no tiene valor para esforzarse + JianShu + Grupo QQ + ¡Vaya!\nLa APP está muy bien hecha\nTe invito un café + Deslizar hacia arriba + Haz clic para guardar en el álbum + Escanea el código para descargar e instalar + Nada aquí~ + ¿Por qué falló de nuevo~? + %d%% aprendido + Check-in Puntos %s + Editar + Consejos + Enlaces del sitio + No aprendido + Completado + Copiado + Soltar en tutoriales de libros + ¿Seguro que quieres borrar el historial de búsqueda? + No eres usuario beta + Activar inicio de sesión biométrico + Iniciar sesión con biometría + ¡Invita al desarrollador a un café!\n¡Una APP completamente gratuita!\n¡Una APP tan útil y gratuita!\n¡Tan bonita, útil y gratuita!\n¿No me invitas un café para animarme?\n¡Ya casi no tengo energía para seguir manteniéndola! + Invitar + Por favor, inicia sesión primero + Activación exitosa, será efectiva tras reiniciar la APP + Código de activación inválido + ¡Has encontrado un código de activación!\nEl código solo está vinculado a la cuenta actual. Si cambias de dispositivo o cuenta, deberás activarlo de nuevo. Tras la activación, se eliminarán todos los anuncios. ¿Activar ahora? + Activar + Inicia sesión para usar esta función + Esta función solo está disponible para cuentas de desarrollador + [Contraseña Wan] Este es un código de activación, solo para cuentas específicas + Abre el cliente WanAndroid para activar + ###Generación de código de activación###\n\n¡¡¡Advertencia!!!\n\nSolo para desarrolladores + [Contraseña Wan] Tu amigo te ha compartido un enlace, ignora este mensaje %s Abre el cliente WanAndroid para ver la página o el artículo + Copiar + Actualizar + ¡Has encontrado una contraseña misteriosa!\nLamentablemente, la versión actual no la soporta. Actualiza la app en configuración e inténtalo de nuevo. + ¡Has encontrado un usuario misterioso!\n¿Quieres visitar su perfil? + Ir al perfil + [Contraseña Wan] Tu amigo te ha compartido un usuario misterioso, ignora este mensaje %s Abre el cliente WanAndroid para descubrirlo + ¡Has encontrado un enlace web!\n¿Quieres abrirlo? + ¡Has encontrado un número QQ!\n%s\n¿Abrir QQ para agregar como amigo? + Agregar amigo + [Contraseña Wan] Tu amigo te ha pedido un café doble, ignora este mensaje %s Abre el cliente WanAndroid para recibirlo + Solo lista blanca + Bloquear lista negra + Por favor, introduce tu nombre de usuario + Por favor, introduce tu correo electrónico + Por favor, introduce tu contraseña + Desliza hacia abajo para actualizar + Actualizando + Suelta para actualizar + Actualización completada + Actualización fallida + Hecho + Escanear código QR requiere permiso de cámara + Haz clic para solicitar + Guardado exitosamente + Error al guardar + Autorización fallida + Cargado exitosamente + Error al cargar + Cargando (%d%%) + Favorito + Anónimo + Añadido a mis marcadores + Error al añadir a mis marcadores + Eliminado de mis marcadores + Error al eliminar de mis marcadores + Ninguno + Aparecer + Zoom + Deslizar desde abajo + Deslizar desde la izquierda + Deslizar desde la derecha + Mantén presionado el botón de retroceso para más accesos directos~ + Siguiente + Haz doble clic en cualquier lugar para marcar rápidamente~ + Mantén presionada la imagen para previsualizar en grande~ + ¿Continuar leyendo? + Sin permiso de cámara no se puede escanear + Solicitar de nuevo + Permiso de cámara denegado + Ir a configuración + No se pudo obtener permiso de cámara + Haz clic para obtener + Seleccionar imagen requiere permiso de almacenamiento + Sin permiso de almacenamiento no se puede seleccionar imagen + Permiso de almacenamiento denegado + No se reconoció ningún código QR + Error al abrir la imagen + Sistema + Navegación + P&R + Para solicitar acceso a la beta cerrada, únete al grupo (ver Sobre el autor) e indica tu número de usuario (ver perfil).\n\nNota: Debes iniciar sesión y actualizar a la última versión oficial antes de solicitar la beta. + Nueva versión encontrada %s + Versión beta encontrada %s + Ya tienes la última versión + %s(%d) + Nuevos mensajes + Mensajes históricos + Inicio de sesión rápido habilitado + Inicio de sesión rápido exitoso + Error en inicio de sesión rápido, usa la contraseña + ¿Habilitar inicio de sesión rápido? + No habilitar por ahora + Inicio de sesión rápido + Inicio de sesión con contraseña + Asegúrate de que ambas contraseñas coincidan + Artículo + Sitio web + Lista blanca + Lista negra + ¿Seguro que quieres eliminar todo? + El enlace está vacío + Error al eliminar + ¿Seguro que quieres limpiar la caché? + ¿Seguro que quieres cerrar sesión? + + Idioma + + Anuncio + + Tendencias de hoy + + Configuración de IA + Desactivado + Habilitar IA + La IA puede ayudarte a resumir artículos + Proveedor del modelo + Clave API + Modelo + Pensando... + Escuchar + La función de IA no está habilitada, puedes ir a la configuración para habilitarla + La clave de IA no está configurada + + Lectura de artículos + Lectura de voz para artículos con controles de reproducción rápidos. + \ No newline at end of file diff --git a/basic/l10n/src/main/res/values-fr/strings.xml b/basic/l10n/src/main/res/values-fr/strings.xml new file mode 100644 index 00000000..44095ddb --- /dev/null +++ b/basic/l10n/src/main/res/values-fr/strings.xml @@ -0,0 +1,302 @@ + + + WanAndroid + + Échec de la connexion réseau, veuillez vérifier les paramètres réseau + Erreur d\'accès, veuillez réessayer plus tard + + Confirmer + Annuler + Fermer + + + Nouvelle version trouvée + Version bêta trouvée + Mettre à jour maintenant + Pas maintenant + + + Téléchargement de la mise à jour... + Installer maintenant + + + Pas maintenant + Autoriser + Aller aux paramètres + Inconnu + Calendrier + Appareil photo + Contacts + Localisation + Microphone + Téléphone + SMS + Capteurs + Stockage + Concerné + Nous avons besoin de la permission %s pour assurer le bon fonctionnement de l\'application. Veuillez cliquer pour autoriser. + Vous avez refusé la permission %s, cela peut affecter le fonctionnement. Veuillez l\'activer dans les paramètres. + + + « Politique de confidentialité » + Merci d\'utiliser WanAndroid !\nNous avons mis à jour notre politique de confidentialité conformément aux dernières exigences réglementaires :\n1. Pour vous fournir les fonctions de base, nous collectons et utilisons les informations nécessaires ;\n2. Avec votre autorisation explicite, nous accédons au stockage pour enregistrer les images partagées. Vous pouvez refuser ou annuler ;\n3. Nous prenons des mesures de sécurité pour protéger vos informations ;\n4. Sans votre consentement, nous ne collecterons, partagerons ni fournirons vos informations à des tiers. + + %d B + %d %% + "Échec du téléchargement, vous pouvez télécharger depuis le dépôt Github : Moi - Paramètres système - À propos de nous - Dépôt du projet" + + Page fournie par %s + Retour à l\'accueil + Partager sur la place + Favoris + Signet + Rafraîchir + Retour en haut + Blocage de page + Quitter + Séparez plusieurs mots-clés par des espaces + Veuillez saisir l\'URL + À propos de nous + Vérifier les mises à jour + Site officiel + Contenu du site + Dépôt du projet + Demander la bêta fermée + Cette application est développée indépendamment par Goweii + Mot de passe Wan + À propos de l\'auteur + Bienvenue + Mes points + Classement des points + Une erreur s\'est produite + Copier les journaux + Fermer + Redémarrer + Mes partages + Projets open source + Mes signets + Effacer + Permission appareil photo non obtenue + Cliquez pour réessayer + Retour au scan + Paramètres + Mode thème + Suivre le système + Mode clair + Mode sombre + Rappel de signet + Notifier les nouveaux signets à l\'ouverture de l\'application + Afficher les questions-réponses + Afficher l’onglet Questions-Réponses sur la page d’accueil après le démarrage + Afficher le carrousel + Afficher le carrousel sur la page d\'accueil + Afficher épinglé + Afficher les articles épinglés sur la page d\'accueil + Profil + WanID + Nom d\'utilisateur + Points : + Classement : + Nom de domaine + ex : wanandroid.com + Annuler + OK + Ouvrir le lien + Je comprends + Enregistrer + Reconnaître le QR code dans l\'image + Veuillez saisir + Je n\'ai même pas encore décidé à quoi sert ce mot de passe, comment l\'avez-vous trouvé ? + Ignorer + Double-cliquez n\'importe où pour ajouter aux favoris + Appuyez longuement sur l\'image pour l\'agrandir + Copier le lien + Ouvrir dans le navigateur + Mot de passe Wan Web + Choisissez le mode de partage + Image longue + QR code + Tutoriels de livres + Accueil + S\'inscrire + Connexion + Niveau : + Mes collections + Questions/Réponses + Nouveau + Épinglé + Supprimer + Mon + Appuyez longuement sur retour pour une surprise~ + Partager + Ouvrir + Liste des articles + Système de connaissances + Historique de lecture + Appuyez longuement pour personnaliser la liste de blocage + Ne pas bloquer + Vider le cache + Politique de confidentialité + Déconnexion + Partager l\'article + Titre de l\'article + Rafraîchir le titre + Jusqu\'à 100 caractères + Lien de l\'article + ex : https://wanandroid.com/blog/show/2577 + 1. Vous pouvez partager tout bon article, pas seulement les originaux ! Les articles soumis apparaîtront dans l\'onglet Place ;\n2. Les blogs officiels comme CSDN, Juejin, JianShu sont automatiquement approuvés ;\n3. Les sites personnels nécessitent une vérification ; n\'envoyez pas de liens invalides, cela pourrait affecter votre compte ;\n4. Si vous trouvez une erreur, envoyez un journal pour améliorer le site.\n5. Ce site est maintenu par une seule personne, l\'approbation se fait sous 24h, sauf imprévu. + Généré par le client WanAndroid + Enregistrer dans l\'album + Partager maintenant + Titre du site + Chargement + Accepter + Quitter l\'application + Offrez-lui un café~ + Se connecter + S\'inscrire + Recherches populaires + Recherches historiques + Terminer + Place + Celui qui ne croit pas en lui-même n\'a aucune valeur à s\'efforcer + JianShu + Groupe QQ + Yo~\nL\'application est super !\nOffrez-vous un café + Glisser vers le haut + Cliquez pour enregistrer dans l\'album + Scanner pour télécharger et installer + Rien~ + Encore une erreur~ + %d %% appris + Enregistrement Points %s + Éditer + Conseil + Liens du site + Non appris + Déjà appris + Copié + Relâcher dans le tutoriel de livre + Êtes-vous sûr de vouloir effacer l\'historique de recherche ? + Pas un utilisateur bêta + Activer la connexion biométrique + Se connecter avec la biométrie + Offrez un café au développeur !\nUne application totalement gratuite !\nUne application aussi pratique et gratuite !\nAussi belle, aussi pratique et gratuite !\nPas de café pour moi ?\nJe n\'ai plus d\'énergie pour continuer ! + Offrir + Veuillez vous connecter d\'abord + Activation réussie, effective après redémarrage + Code d\'activation invalide + Vous avez trouvé un code d\'activation !\nLe code est lié au compte actuel. Après changement d\'appareil ou de compte, il faut réactiver. Après activation, toutes les pubs seront supprimées. Activer maintenant ? + Activer + Veuillez vous connecter pour utiliser cette fonction + Cette fonction est réservée aux comptes développeur + [Wan Watchword] Ceci est un code d\'activation, réservé à certains comptes + Ouvrez le plus beau client WanAndroid pour activer + ###Génération de code d\'activation###\n\n!!! Attention !!!\n\nCette fonction est réservée aux développeurs + [Wan Watchword] Votre ami vous a partagé un lien, ouvrez le client WanAndroid pour le voir : %s + Copier + Mettre à jour + Vous avez trouvé un mot de passe mystérieux !\nMalheureusement, cette version ne le prend pas en charge. Mettez à jour dans les paramètres et réessayez ! + Vous avez trouvé un utilisateur mystérieux !\nVoulez-vous visiter sa page ? + Aller à la page d\'accueil + [Wan Watchword] Votre ami vous a partagé un utilisateur mystérieux, ouvrez le client WanAndroid pour le découvrir : %s + Vous avez trouvé un lien web !\nVoulez-vous l\'ouvrir ? + Vous avez trouvé un numéro QQ !\n%s\nVoulez-vous ouvrir QQ pour ajouter un ami ? + Ajouter un ami + [Wan Watchword] Votre ami vous a commandé un café pour deux, ouvrez le client WanAndroid pour le recevoir : %s + Liste blanche uniquement + Bloquer la liste noire + Veuillez saisir votre nom d\'utilisateur + Veuillez saisir votre e-mail + Veuillez saisir votre mot de passe + Tirer pour rafraîchir + Rafraîchissement + Relâcher pour rafraîchir + Rafraîchissement terminé + Échec du rafraîchissement + Terminé + Scanner le QR code nécessite la permission appareil photo + Cliquez pour demander + Enregistrement réussi + Échec de l\'enregistrement + Échec de l\'autorisation + Chargé avec succès + Échec du chargement + Chargement (%d %%) + Ajouté aux favoris + Anonyme + Ajouté à mes signets + Échec de l\'ajout aux signets + Retiré de mes signets + Échec du retrait des signets + Aucun + Fondu + Zoom + Glisser du bas + Glisser de la gauche + Glisser de la droite + Appuyez longuement sur retour pour plus de raccourcis~ + Suivant + Double-cliquez n\'importe où pour ajouter rapidement aux favoris~ + Appuyez longuement sur l\'image pour la prévisualiser en grand~ + Continuer la lecture ? + Refuser la permission appareil photo empêche le scan + Redemander + Permission appareil photo refusée + Aller aux paramètres + Impossible d\'obtenir la permission appareil photo + Cliquez pour obtenir + Sélectionner une image nécessite la permission stockage + Refuser la permission stockage empêche la sélection d\'image + Permission stockage refusée + Aucun QR code reconnu + Échec de l\'ouverture de l\'image + Système + Navigation + Q/R + Pour demander l\'accès à la bêta, rejoignez le groupe (voir À propos de l\'auteur) et indiquez votre numéro Wan (voir profil).\n\nNote : la bêta nécessite d\'être connecté et à jour. + Nouvelle version trouvée : %s + Version bêta trouvée : %s + Déjà à jour + %s(%d) + Nouveaux messages + Messages historiques + Connexion rapide activée + Connexion rapide réussie + Échec de la connexion rapide, veuillez utiliser le mot de passe + Activer la connexion rapide ? + Pas maintenant + Connexion rapide + Connexion par mot de passe + Veuillez confirmer que les deux mots de passe sont identiques + Article + Site web + Liste blanche + Liste noire + Voulez-vous tout supprimer ? + Lien vide + Échec de la suppression + Voulez-vous vider le cache ? + Voulez-vous vous déconnecter ? + + Langue + + Publicité + + Tendances du jour + + Paramètres IA + Désactivé + Activer l\'IA + L\'IA peut vous aider à résumer des articles + Fournisseur de modèle + Clé API + Modèle + Réflexion en cours… + Écouter + La fonction IA n\'est pas activée, vous pouvez aller dans les paramètres pour l\'activer + La clé IA n\'est pas configurée + + Lecture d\'article + Lecture vocale des articles avec commandes de lecture rapides. + \ No newline at end of file diff --git a/basic/l10n/src/main/res/values-ru/strings.xml b/basic/l10n/src/main/res/values-ru/strings.xml new file mode 100644 index 00000000..54a430c8 --- /dev/null +++ b/basic/l10n/src/main/res/values-ru/strings.xml @@ -0,0 +1,302 @@ + + + WanAndroid + + Не удалось подключиться к сети, пожалуйста, проверьте настройки сети + Ошибка доступа, попробуйте позже + + ОК + Отмена + Закрыть + + + Обнаружена новая версия + Обнаружена бета-версия + Обновить сейчас + Пока не обновлять + + + Загрузка обновления... + Установить сейчас + + + Не разрешать + Разрешить + Перейти к настройкам + Неизвестно + Календарь + Камера + Контакты + Местоположение + Микрофон + Телефон + SMS + Датчики + Хранилище + Связанные + Нам нужно разрешение %s для корректной работы приложения. Пожалуйста, нажмите "Разрешить". + Вы запретили разрешение %s, это повлияет на работу функций. Пожалуйста, включите разрешение в настройках. + + + «Политика конфиденциальности» + Спасибо за использование WanAndroid!\nМы обновили «Политику конфиденциальности» в соответствии с последними требованиями:\n1. Для предоставления основных функций мы собираем и используем необходимую информацию;\n2. С вашего разрешения мы получаем доступ к хранилищу для сохранения изображений, вы можете отказаться;\n3. Мы принимаем меры для защиты вашей информации;\n4. Без вашего согласия мы не получаем, не делимся и не передаем вашу информацию третьим лицам. + + %d B + %d %% + "Не удалось загрузить, скачайте с Github: Мое - Настройки системы - О нас - Репозиторий проекта" + + Страница предоставлена %s + Вернуться на главную + Поделиться на площадке + Избранное + Закладка + Обновить + Наверх + Блокировка страниц + Выйти + Используйте пробел для разделения ключевых слов + Пожалуйста, введите URL + О нас + Проверить обновления + Официальный сайт + Содержимое сайта + Репозиторий проекта + Подать заявку на бета-тест + Это приложение разработано Goweii + Кодовое слово Wan + Об авторе + Добро пожаловать + Мои баллы + Таблица лидеров + Что-то пошло не так + Скопировать логи + Закрыть + Перезагрузить + Мои публикации + Проекты с открытым исходным кодом + Мои закладки + Очистить + Нет разрешения на камеру + Нажмите, чтобы попробовать снова + Вернуться к сканированию + Настройки + Тема + Следовать системе + Светлая тема + Темная тема + Напоминания о закладках + Уведомлять о новых закладках при запуске приложения + Показать вопросы и ответы + Показывать вкладку «Вопросы и ответы» на главной странице после запуска + Показать карусель + Показывать карусель на главной странице + Показать закрепленные + Показывать закрепленные статьи на главной + Личные данные + Wan ID + Имя пользователя + Баллы: + Рейтинг: + Домен + например: wanandroid.com + Отмена + ОК + Открыть ссылку + Понятно + Сохранить + Распознать QR-код на изображении + Пожалуйста, введите + Я еще не придумал, что делать с этим кодовым словом. Как ты его нашел? + Игнорировать + Двойной клик для добавления статьи в избранное + Долгое нажатие для увеличения изображения + Скопировать ссылку + Открыть в браузере + Кодовое слово на сайте + Выберите способ поделиться + Длинное изображение + QR-код + Учебники + Главная + Зарегистрироваться + Войти + Уровень: + Мои коллекции + Вопросы и ответы + Новое + Закреплено + Удалить + Мое + Долгое нажатие на кнопку "Назад" — сюрприз! + Поделиться + Открыть + Список статей + Система знаний + История чтения + Долгое нажатие для настройки списка блокировки + Не блокировать + Очистить кэш + Политика конфиденциальности + Выйти из аккаунта + Поделиться статьей + Заголовок статьи + Обновить заголовок + До 100 символов + Ссылка на статью + например: https://wanandroid.com/blog/show/2577 + 1. Можно делиться любыми хорошими статьями, не обязательно авторскими! Публикации попадают во вкладку "Площадка";\n2. CSDN, Juejin, JianShu и другие официальные блоги проходят без модерации;\n3. Другие сайты проходят модерацию. Не отправляйте невалидные ссылки, это может повлиять на ваш аккаунт;\n4. Если вы нашли ошибку, отправьте лог, чтобы мы сделали сайт лучше.\n5. Сайт поддерживается одним человеком, модерация обычно в течение 24 часов, но иногда может быть задержка. + Сгенерировано клиентом WanAndroid + Сохранить в альбом + Поделиться сейчас + Заголовок сайта + Загрузка + Согласен + Выйти из приложения + Купите ему кофе~ + Войти + Регистрация + Популярные запросы + История поиска + Готово + Площадка + Тот, кто не верит в себя, не достоин усилий + JianShu + Группа QQ + О, приложение классное!\nУгостить тебя кофе? + Проведите вверх + Нажмите, чтобы сохранить в альбом + Сканируйте для загрузки и установки + Ничего нет~ + Опять что-то не так~ + Изучено %d%% + Регистрация Баллы %s + Редактировать + Подсказка + Ссылки на сайт + Не изучено + Изучено + Скопировано + Перетащите в учебник + Очистить историю поиска? + Не бета-пользователь + Включить вход по биометрии + Войти с помощью биометрии + Пожалуйста, угостите разработчика кофе!\nЭто полностью бесплатное приложение!\nТакое классное и бесплатное приложение!\nТакое красивое, удобное и бесплатное приложение!\nНе угостите меня кофе для бодрости?\nУ меня почти не осталось сил поддерживать проект! + Угостить + Пожалуйста, войдите + Активация успешна, вступит в силу после перезапуска + Недействительный код активации + Вы нашли код активации!\nКод привязан к текущему аккаунту, при смене устройства или аккаунта потребуется повторная активация. После активации реклама будет отключена. Активировать сейчас? + Активировать + Войдите, чтобы использовать эту функцию + Эта функция доступна только для аккаунтов разработчиков + [Кодовое слово Wan] Это код активации, только для определенных аккаунтов + Откройте лучший клиент WanAndroid для активации + ###Генерация кода активации###\n\n!!! ВНИМАНИЕ !!!\n\nТолько для разработчиков + [Кодовое слово Wan] Ваш друг поделился ссылкой, проигнорируйте это сообщение %s Откройте лучший клиент WanAndroid для просмотра + Копировать + Обновить + Вы нашли загадочное кодовое слово!\nК сожалению, текущая версия не поддерживает его. Обновите приложение в настройках и попробуйте снова! + Вы нашли загадочного пользователя!\nХотите посмотреть его профиль? + Перейти на главную + [Кодовое слово Wan] Ваш друг поделился загадочным пользователем, проигнорируйте это сообщение %s Откройте лучший клиент WanAndroid для просмотра + Вы нашли ссылку на страницу!\nХотите открыть? + Вы нашли номер QQ!\n%s\nОткрыть QQ и добавить в друзья? + Добавить в друзья + [Кодовое слово Wan] Ваш друг заказал двойной кофе, проигнорируйте это сообщение %s Откройте лучший клиент WanAndroid для получения + Только белый список + Блокировать черный список + Пожалуйста, введите имя пользователя + Пожалуйста, введите email + Пожалуйста, введите пароль + Потяните вниз для обновления + Обновление... + Отпустите для обновления + Обновление завершено + Не удалось обновить + Готово + Для сканирования QR-кода требуется разрешение на камеру + Нажмите для запроса + Сохранено успешно + Не удалось сохранить + Не удалось получить разрешение + Загружено успешно + Не удалось загрузить + Загрузка (%d%%) + В избранном + Аноним + Добавлено в закладки + Не удалось добавить в закладки + Удалено из закладок + Не удалось удалить из закладок + Нет + Появление + Масштаб + Снизу + Слева + Справа + Долгое нажатие на "Назад" — больше быстрых меню + Далее + Двойной клик для быстрого добавления в избранное + Долгое нажатие для предпросмотра изображения + Продолжить чтение? + Без разрешения на камеру невозможно сканировать + Запросить снова + Разрешение на камеру отклонено + Перейти к настройкам + Не удалось получить разрешение на камеру + Нажмите, чтобы получить + Для выбора изображения требуется разрешение на хранилище + Без разрешения на хранилище невозможно выбрать изображение + Разрешение на хранилище отклонено + QR-код не распознан + Не удалось открыть изображение + Система + Навигация + Q&A + Для доступа к бета-обновлениям присоединяйтесь к группе (см. "Об авторе") и укажите свой Wan ID (см. профиль).\n\nПримечание: для бета-обновлений требуется вход и последняя версия приложения + Обнаружена новая версия %s + Обнаружена бета-версия %s + Установлена последняя версия + %s(%d) + Новые сообщения + История сообщений + Быстрый вход включен + Быстрый вход выполнен + Не удалось выполнить быстрый вход, используйте пароль + Включить быстрый вход? + Пока не включать + Быстрый вход + Вход по паролю + Убедитесь, что пароли совпадают + Статья + Сайт + Белый список + Черный список + Удалить все? + Ссылка пуста + Не удалось удалить + Очистить кэш? + Выйти из аккаунта? + + Язык + + Реклама + + Тренды сегодня + + Настройки ИИ + Отключено + Включить ИИ + ИИ поможет вам суммировать статьи + Поставщик модели + API-ключ + Модель + Размышление... + Слушать + Функция ИИ не включена, вы можете перейти в настройки, чтобы включить ее + API-ключ ИИ не настроен + + Чтение статей + Голосовое чтение статей с быстрым управлением воспроизведением. + \ No newline at end of file diff --git a/basic/l10n/src/main/res/values-zh-rCN/strings.xml b/basic/l10n/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 00000000..60fd0abc --- /dev/null +++ b/basic/l10n/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,302 @@ + + + 玩安卓 + + 网络连接失败,请检查网络设置 + 访问出错,请稍后重试 + + 确定 + 取消 + 关闭 +Ï + + 发现新版本 + 发现内测版本 + 立即更新 + 暂不更新 + + + 下载更新中... + 立即安装 + + + 暂不授权 + 去授权 + 去设置 + 未知 + 日历 + 照相机 + 通讯录 + 定位 + 麦克风 + 手机 + 短信 + 传感器 + 存储空间 + 相关 + 我们需要获取%s权限以确保应用的正常运行,请点击去授权完成权限申请。 + 您已拒绝应用使用%s权限,这将影响功能的正常运行,请前往设置打开权限。 + + + 《隐私政策》 + 感谢您使用玩安卓!\n我们依据最新的监管要求更新了《隐私政策》特向您说明如下:\n1. 为向您提供交易相关基本功能,我们会收集、使用必要的信息;\n2. 基于您的明示授权我们会获取存储权限,用于保存文章分享图,您有权拒绝或取消授权;\n3. 我们会采取安全措施保护您的信息安全;\n4. 未经您同意,我们不会从第三方处获取、共享或向其提供您的信息。 + + %d B + %d %% + "下载失败,可前往Github仓库下载:我的-系统设置-关于我们-项目仓库" + + 网页由%s提供 + 回到首页 + 分享到广场 + 收藏 + 书签 + 刷新 + 回到顶部 + 网页拦截 + 退出 + 用空格隔开多个关键词 + 请输入网址 + 关于我们 + 检查更新 + 官方网站 + 网站内容 + 项目仓库 + 申请内测 + 本APP由Goweii独立开发 + 玩口令 + 关于作者 + 欢迎使用 + 我的积分 + 积分排行榜 + 出错了 + 复制日志 + 关闭 + 重启 + 我的分享 + 开源项目 + 我的书签 + 清空 + 没有获取到相机权限 + 点击重试 + 返回扫码 + 设置 + 主题模式 + 跟随系统 + 亮色模式 + 暗色模式 + 书签提醒 + 打开应用时以通知方式提醒最新添加书签 + 显示问答 + 开始后主页显示问答标签页 + 显示轮播 + 开启后首页显示轮播图 + 显示置顶 + 开启后首页显示置顶文章 + 个人资料 + 玩ID + 用户名 + 积分: + 排名: + 域名 + 如:wanandroid.com + 取消 + 确定 + 打开链接 + 我知道了 + 保存 + 识别图中二维码 + 请输入 + 我都还没有想好用这个口令来干什么,你怎么发现的? + 忽略 + 双击任意位置收藏文章 + 长按网页图片查看大图 + 复制链接 + 用浏览器打开 + 网页玩口令 + 选择分享方式 + 长图 + 二维码 + 书籍教程 + 首页 + 去注册 + 登录 + 等级: + 我的收藏 + 问答 + + 置顶 + 删除 + 我的 + 长按返回按钮有惊喜哦~ + 分享 + 打开 + 文章列表 + 知识体系 + 阅读历史 + 长按可自定义拦截列表 + 不拦截 + 清除缓存 + 隐私政策 + 退出登录 + 分享文章 + 文章标题 + 刷新标题 + 100字以内 + 文章链接 + 如:https://wanandroid.com/blog/show/2577 + 1. 只要是任何好文都可以分享哈,并不一定要是原创!投递的文章会进入广场 tab;\n2. CSDN,掘金,简书等官方博客站点会直接通过,不需要审核;\n3. 其他个人站点会进入审核阶段,不要投递任何无效链接,否则可能会对你的账号产生一定影响;\n4. 如果你发现错误,可以提交日志,让我们一起使网站变得更好。\n5. 由于本站为个人开发与维护,会尽力保证24小时内审核,当然有可能哪天太累,会延期,请保持佛系... + 由玩安卓客户端生成 + 保存到相册 + 立即分享 + 网站标题 + 加载中 + 同意 + 退出应用 + 请他喝杯咖啡~ + 去登录 + 注册 + 热门搜索 + 历史的搜索 + 完成 + 广场 + 不相信自己的人没有努力的价值 + 简书 + QQ群 + 呦~\nAPP做得不错呀\n请你喝杯咖啡 + 上滑 + 点击保存到相册 + 扫码下载安装 + 什么都没有~ + 怎么又出错~ + 已学%d%% + 签到 积分%s + 编辑 + 提示 + 网站链接 + 未学习 + 已学完 + 已复制 + 释放进入书籍教程 + 确定要清除搜索历史? + 非内测用户 + 开启生物认证登录 + 使用生物认证登录 + 请开发小哥哥喝杯咖啡吧!\n一个完全免费的APP!\n一个这么好用还完全免费的APP!\n一个这么好看又好用还完全免费的APP!\n不请我喝杯咖啡提提神?\n我都快没精力继续维护了! + 去请客 + 请先登录 + 激活成功,重启APP后生效 + 激活码无效 + 你发现了一个激活码!\n激活码仅与当前登录账户绑定,更换设备或账户后需重新激活,成功激活后将会去除所有广告,是否立即激活? + 激活 + 请登录后使用该功能 + 该功能仅限开发者账号使用 + 【玩口令】这是一个激活码口令,仅限特定账号使用,户制泽条消息 + 打开最美玩安卓客户端激活 + ###激活码生成###\n\n!!!警告!!!\n\n该功能仅限开发者使用 + 【玩口令】你的好友给你分享了一个链接,户制泽条消息%s打開最美玩安卓客户端即可查看该网页或者文章 + 复制 + 去更新 + 你发现了一个神秘口令!\n可惜当前版本不支持,快去设置中更新版本再试试吧! + 你发现了一个神秘用户!\n要不要去他主页看一下? + 去主页 + 【玩口令】你的好友给你分享了一个神秘用户,户制泽条消息%s打開最美玩安卓客户端揭开他/她的神秘面纱 + 你发现了一个网页链接!\n要不要去打开看一下? + 你发现了一个QQ号码!\n%s\n是否立即启动QQ加个好友? + 加好友 + 【玩口令】你的好友给你订了一份双人咖啡,户制泽条消息%s打開最美玩安卓客户端即可领取品尝 + 仅白名单 + 拦截黑名单 + 请输入用户名 + 请输入邮箱 + 请输入密码 + 下拉刷新 + 正在刷新 + 释放刷新 + 刷新完成 + 刷新失败 + 已完成 + 扫二维码需要相机权限 + 点击申请 + 保存成功 + 保存失败 + 授权失败 + 加载成功 + 加载失败 + 加载中(%d%%) + 已收藏 + 匿名 + 已加入我的书签 + 加入我的书签失败 + 已移出我的书签 + 移出我的书签失败 + + 渐显 + 缩放 + 底部滑入 + 左侧滑入 + 右侧滑入 + 长按返回按钮有更多快捷菜单~ + 下一个 + 双击任意位置可快速收藏~ + 长按网页图片可预览大图~ + 是否继续阅读? + 拒绝相机权限将无法扫码 + 重新申请 + 相机权限已被拒绝 + 去设置 + 无法获取相机权限 + 点击获取 + 选择图片需要存储权限 + 拒绝存储权限将无法选择图片 + 存储权限已被拒绝 + 没有识别到二维码 + 打开图片失败 + 体系 + 导航 + 问答 + 需要申请开通内测更新的小伙伴,请加群(见关于作者)说明内测更新并注明玩友号(见个人资料)。\n\n注:内测更新须提前登录并更新到最新正式版 + 发现新版本%s + 发现内测版本%s + 已是最新版 + %s(%d) + 新消息 + 历史消息 + 开启快捷登录成功 + 快捷登录成功 + 快捷登录失败,请使用密码登录 + 是否开启快捷登录 + 暂不开启 + 快捷登录 + 密码登录 + 请确认2次密码一致 + 文章 + 网址 + 白名单 + 黑名单 + 确定要全部删除吗? + 链接为空 + 删除失败 + 确定要清除缓存吗? + 确定要退出登录吗? + + 语言 + + 广告 + + 今日热点 + + AI 设置 + 已禁用 + 是否启用 AI + AI 可以帮你总结文章 + 模型提供商 + API Key + 模型 + 思考中... + 收听 + AI 功能未开启,可前往设置开启 + AI Key 未配置 + + 文章朗读 + 浏览文章时开启语音朗读,快捷控制播放状态 + \ No newline at end of file diff --git a/basic/l10n/src/main/res/values/strings.xml b/basic/l10n/src/main/res/values/strings.xml new file mode 100644 index 00000000..efc2515a --- /dev/null +++ b/basic/l10n/src/main/res/values/strings.xml @@ -0,0 +1,303 @@ + + + WanAndroid + + Network connection failed, please check your network settings + An error occurred, please try again later + + OK + Cancel + Close + + + New version found + Beta version found + Update now + Not now + + + Downloading update... + Install now + + + Not now + Authorize + Go to settings + Unknown + Calendar + Camera + Contacts + Location + Microphone + Phone + SMS + Sensors + Storage + Related + We need %s permission to ensure the normal operation of the app. Please click to authorize. + You have denied %s permission, which will affect the normal operation of the app. Please go to settings to enable it. + + + Privacy Policy + Thank you for using WanAndroid!\nWe have updated our Privacy Policy according to the latest regulatory requirements. Please note:\n1. We collect and use necessary information to provide you with basic transaction functions;\n2. With your explicit authorization, we will obtain storage permission to save article sharing images. You have the right to refuse or cancel authorization;\n3. We take security measures to protect your information;\n4. Without your consent, we will not obtain, share, or provide your information to third parties. + + %d B + %d %% + Download failed, you can download from Github: Mine - System Settings - About Us - Project Repository + + This webpage is provided by %s + Back to home page + Share to Square + Collect + Bookmark + Refresh + Back to top + Web blocking + Exit + Use spaces to separate multiple keywords + Please enter the URL + About us + Check for updates + Official website + Website content + Project repository + Apply for closed beta + This app is independently developed by Goweii + Wan password + About the author + Welcome + My points + Points leaderboard + Something went wrong + Copy logs + Close + Reboot + My sharing + Open source projects + My bookmarks + Clear + Failed to get camera permission + Click to retry + Return to scan + Settings + Theme mode + Follow system + Light mode + Dark mode + Bookmark reminders + Notify the latest added bookmarks when opening the app + Show carousel + Show carousel images on the home page after enabling + Show Q&A + Show the Q&A tab on the home page after starting + Show pinned + Show pinned articles on the home page after enabling + Personal data + Wan ID + Username + Points: + Ranking: + Domain name + e.g. wanandroid.com + Cancel + OK + Open link + Got it + Save + Identify QR code in the picture + Please enter + I haven\'t even decided what to do with this password yet, how did you find it? + Ignore + Double click anywhere to favorite the article + Long press the image on the webpage to view it in large size + Copy link + Open in browser + Wan password website + Choose how to share + Long image + QR code + Book tutorials + Home + Go to sign up + Sign in + Level: + My collections + Q&A + New + Top + Delete + Mine + Long press the back button for a surprise~ + Share + Open + Article list + Knowledge system + Reading history + Long press to customize the block list + Do not block + Clear cache + Privacy policy + Sign out + Share article + Article title + Refresh title + Within 100 characters + Article link + e.g. https://wanandroid.com/blog/show/2577 + 1. Any good article can be shared, not necessarily original! Submitted articles will enter the Square tab;\n2. Official blog sites like CSDN, Juejin, Jianshu will be approved directly, no review needed;\n3. Other personal sites will be reviewed, do not submit invalid links, otherwise it may affect your account;\n4. If you find errors, you can submit logs to help us improve the site.\n5. Since the site is developed and maintained by an individual, reviews will be done within 24 hours as much as possible, but may be delayed if too tired, please be patient... + Generated by WanAndroid client + Save to album + Share now + Website title + Loading + Agree + Quit app + Buy him a coffee~ + Go to sign in + Sign up + Popular searches + Search history + Complete + Square + Those who don\'t believe in themselves have no value in working hard + Jianshu + QQ group + Yo~\nThe app is great\nBuy you a coffee + Swipe up + Click to save to album + Scan to download and install + Nothing~ + How did it go wrong again~ + Learned %d%% + Check-in points %s + Edit + Tips + Site links + Not learned + Already learned + Copied + Release into book tutorials + Are you sure you want to clear search history? + Not a beta user + Enable biometric sign-in + Sign in with biometrics + Buy the developer a coffee!\nA completely free app!\nSuch a useful and free app!\nSuch a beautiful and useful free app!\nWon\'t you buy me a coffee?\nI\'m running out of energy to maintain it! + Go treat + Please sign in first + Activation successful, effective after restarting the app + Invalid activation code + You found an activation code!\nThe activation code is bound to the current account. You need to reactivate when changing device or account. After successful activation, all ads will be removed. Activate now? + Activate + Please sign in to use this feature + This feature is only available for developer accounts + [Wan password] This is an activation code passcode, only for specific accounts, please ignore this message + Open the most beautiful WanAndroid client to activate + ### Activation code generation ###\n\n!!! Warning !!!\n\nThis feature is for developers only + [Wan password] Your friend shared a link with you, please ignore this message %s Open the most beautiful WanAndroid client to view the webpage or article + Copy + Go to update + You discovered a mysterious password!\nUnfortunately, the current version does not support it. Please update in settings and try again! + You discovered a mysterious user!\nDo you want to visit their homepage? + Go to homepage + [Wan password] Your friend shared a mysterious user with you, please ignore this message %s Open the most beautiful WanAndroid client to unveil their mystery + You found a webpage link!\nDo you want to open it? + You found a QQ number!\n%s\nDo you want to add as a friend in QQ now? + Add friend + [Wan password] Your friend ordered a double coffee for you, please ignore this message %s Open the most beautiful WanAndroid client to claim and taste + Whitelist only + Block blacklists + Please enter username + Please enter email + Please enter password + Pull down to refresh + Refreshing + Release to refresh + Refresh completed + Refresh failed + Done + Scanning QR code requires camera permission + Click to apply + Saved successfully + Save failed + Authorization failed + Loaded successfully + Load failed + Loading (%d%%) + Collected + Anonymous + Added to my bookmarks + Failed to add to my bookmarks + Removed from my bookmarks + Failed to remove from my bookmarks + None + Fade in + Zoom + Slide in from bottom + Slide in from left + Slide in from right + Long press the back button for more shortcut menus~ + Next + Double click anywhere to quickly bookmark~ + Long press the web image to preview a larger image~ + Continue reading? + Denying camera permission will prevent scanning + Reapply + Camera permission has been denied + Go to settings + Unable to get camera permission + Click to get + Selecting an image requires storage permission + Denying storage permission will prevent image selection + Storage permission has been denied + No QR code recognized + Failed to open image + System + Navigation + Q&A + To apply for closed beta updates, please join the group (see About the Author) and mention your Wan ID (see Personal Data).\n\nNote: You must log in and update to the latest official version before applying for beta updates. + New version found %s + Beta version found %s + Already the latest version + %s(%d) + New messages + Historical messages + Quick sign-in enabled successfully + Quick sign-in successful + Quick sign-in failed, please use password sign-in + Enable quick sign-in? + Not now + Quick sign-in + Password sign-in + Please make sure the two passwords are the same + Article + Website + Whitelist + Blacklist + Are you sure you want to delete all? + The link is empty + Deletion failed + Are you sure you want to clear cache? + Are you sure you want to sign out? + + Language + + Ad + + Trending Today + + AI Settings + Disabled + Enable AI + AI can help you summarize articles + Model Provider + API Key + Model + Thinking... + Listen + AI feature is not enabled, you can go to settings to enable it + AI Key is not configured + + tts + Article Reading + Voice reading for articles with quick playback controls. + \ No newline at end of file diff --git a/basic/l10n/src/main/res/xml/locale_config.xml b/basic/l10n/src/main/res/xml/locale_config.xml new file mode 100644 index 00000000..96e15206 --- /dev/null +++ b/basic/l10n/src/main/res/xml/locale_config.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/basic/res/build.gradle b/basic/res/build.gradle new file mode 100644 index 00000000..bf10561d --- /dev/null +++ b/basic/res/build.gradle @@ -0,0 +1,11 @@ +apply plugin: 'com.android.library' +apply from: "${rootDir.path}/gradle/basic.gradle" + +android { + namespace "per.goweii.basic.res" + resourcePrefix "" +} + +dependencies { + api rootProject.ext.dependencies.androidxAppcompat +} diff --git a/basic_core/proguard-rules.pro b/basic/res/proguard-rules.pro similarity index 100% rename from basic_core/proguard-rules.pro rename to basic/res/proguard-rules.pro diff --git a/basic/res/src/main/AndroidManifest.xml b/basic/res/src/main/AndroidManifest.xml new file mode 100644 index 00000000..9b65eb06 --- /dev/null +++ b/basic/res/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/basic/res/src/main/res/anim/activity_no_anim.xml b/basic/res/src/main/res/anim/activity_no_anim.xml new file mode 100644 index 00000000..eb8610fa --- /dev/null +++ b/basic/res/src/main/res/anim/activity_no_anim.xml @@ -0,0 +1,3 @@ + + \ No newline at end of file diff --git a/basic_res/src/main/res/anim/zoom_small_in.xml b/basic/res/src/main/res/anim/zoom_small_in.xml similarity index 100% rename from basic_res/src/main/res/anim/zoom_small_in.xml rename to basic/res/src/main/res/anim/zoom_small_in.xml diff --git a/basic_res/src/main/res/anim/zoom_small_out.xml b/basic/res/src/main/res/anim/zoom_small_out.xml similarity index 100% rename from basic_res/src/main/res/anim/zoom_small_out.xml rename to basic/res/src/main/res/anim/zoom_small_out.xml diff --git a/basic_res/src/main/res/drawable-v21/bg_press_color_background_radius_max.xml b/basic/res/src/main/res/drawable-night-v21/bg_press_color_background_radius_max.xml similarity index 69% rename from basic_res/src/main/res/drawable-v21/bg_press_color_background_radius_max.xml rename to basic/res/src/main/res/drawable-night-v21/bg_press_color_background_radius_max.xml index 97b7bf0a..c181fc31 100644 --- a/basic_res/src/main/res/drawable-v21/bg_press_color_background_radius_max.xml +++ b/basic/res/src/main/res/drawable-night-v21/bg_press_color_background_radius_max.xml @@ -1,9 +1,9 @@ + android:color="?colorRippleDark"> - + diff --git a/basic_res/src/main/res/drawable-v21/bg_press_color_foreground.xml b/basic/res/src/main/res/drawable-night-v21/bg_press_color_foreground.xml similarity index 72% rename from basic_res/src/main/res/drawable-v21/bg_press_color_foreground.xml rename to basic/res/src/main/res/drawable-night-v21/bg_press_color_foreground.xml index 7458a43e..b473b997 100644 --- a/basic_res/src/main/res/drawable-v21/bg_press_color_foreground.xml +++ b/basic/res/src/main/res/drawable-night-v21/bg_press_color_foreground.xml @@ -1,11 +1,11 @@ + android:color="?colorRippleDark"> - + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable-v21/bg_press_color_foreground_top_radius_max.xml b/basic/res/src/main/res/drawable-night-v21/bg_press_color_foreground_top_radius_max.xml similarity index 83% rename from basic_res/src/main/res/drawable-v21/bg_press_color_foreground_top_radius_max.xml rename to basic/res/src/main/res/drawable-night-v21/bg_press_color_foreground_top_radius_max.xml index 3641bcf0..a190ce5a 100644 --- a/basic_res/src/main/res/drawable-v21/bg_press_color_foreground_top_radius_max.xml +++ b/basic/res/src/main/res/drawable-night-v21/bg_press_color_foreground_top_radius_max.xml @@ -1,5 +1,5 @@ + android:color="?colorRippleDark"> \ No newline at end of file diff --git a/basic_res/src/main/res/drawable-v21/bg_press_color_main_radius_def.xml b/basic/res/src/main/res/drawable-night-v21/bg_press_color_main_radius_def.xml similarity index 78% rename from basic_res/src/main/res/drawable-v21/bg_press_color_main_radius_def.xml rename to basic/res/src/main/res/drawable-night-v21/bg_press_color_main_radius_def.xml index 51d8f4ee..11e79a20 100644 --- a/basic_res/src/main/res/drawable-v21/bg_press_color_main_radius_def.xml +++ b/basic/res/src/main/res/drawable-night-v21/bg_press_color_main_radius_def.xml @@ -1,11 +1,11 @@ + android:color="?colorRippleDark"> - + diff --git a/basic_res/src/main/res/drawable-v21/bg_press_color_main_radius_max.xml b/basic/res/src/main/res/drawable-night-v21/bg_press_color_main_radius_max.xml similarity index 70% rename from basic_res/src/main/res/drawable-v21/bg_press_color_main_radius_max.xml rename to basic/res/src/main/res/drawable-night-v21/bg_press_color_main_radius_max.xml index d03a4453..21ae7a79 100644 --- a/basic_res/src/main/res/drawable-v21/bg_press_color_main_radius_max.xml +++ b/basic/res/src/main/res/drawable-night-v21/bg_press_color_main_radius_max.xml @@ -1,9 +1,9 @@ + android:color="?colorRippleDark"> - + diff --git a/basic_res/src/main/res/drawable-v21/bg_press_color_surface.xml b/basic/res/src/main/res/drawable-night-v21/bg_press_color_surface.xml similarity index 73% rename from basic_res/src/main/res/drawable-v21/bg_press_color_surface.xml rename to basic/res/src/main/res/drawable-night-v21/bg_press_color_surface.xml index ea496a98..e79b2484 100644 --- a/basic_res/src/main/res/drawable-v21/bg_press_color_surface.xml +++ b/basic/res/src/main/res/drawable-night-v21/bg_press_color_surface.xml @@ -1,11 +1,11 @@ + android:color="?colorRippleDark"> - + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable-v21/bg_press_color_surface_radius_def.xml b/basic/res/src/main/res/drawable-night-v21/bg_press_color_surface_radius_def.xml similarity index 77% rename from basic_res/src/main/res/drawable-v21/bg_press_color_surface_radius_def.xml rename to basic/res/src/main/res/drawable-night-v21/bg_press_color_surface_radius_def.xml index 7ba30b87..c20ca257 100644 --- a/basic_res/src/main/res/drawable-v21/bg_press_color_surface_radius_def.xml +++ b/basic/res/src/main/res/drawable-night-v21/bg_press_color_surface_radius_def.xml @@ -1,11 +1,11 @@ + android:color="?colorRippleDark"> - + diff --git a/basic_res/src/main/res/drawable-v21/bg_press_color_surface_radius_max.xml b/basic/res/src/main/res/drawable-night-v21/bg_press_color_surface_radius_max.xml similarity index 76% rename from basic_res/src/main/res/drawable-v21/bg_press_color_surface_radius_max.xml rename to basic/res/src/main/res/drawable-night-v21/bg_press_color_surface_radius_max.xml index aff7d73f..201fe384 100644 --- a/basic_res/src/main/res/drawable-v21/bg_press_color_surface_radius_max.xml +++ b/basic/res/src/main/res/drawable-night-v21/bg_press_color_surface_radius_max.xml @@ -1,11 +1,11 @@ + android:color="?colorRippleDark"> - + diff --git a/basic_res/src/main/res/drawable-v21/bg_press_color_surface_radius_small.xml b/basic/res/src/main/res/drawable-night-v21/bg_press_color_surface_radius_small.xml similarity index 77% rename from basic_res/src/main/res/drawable-v21/bg_press_color_surface_radius_small.xml rename to basic/res/src/main/res/drawable-night-v21/bg_press_color_surface_radius_small.xml index 288efdd7..76f5efe8 100644 --- a/basic_res/src/main/res/drawable-v21/bg_press_color_surface_radius_small.xml +++ b/basic/res/src/main/res/drawable-night-v21/bg_press_color_surface_radius_small.xml @@ -1,11 +1,11 @@ + android:color="?colorRippleDark"> - + diff --git a/basic_res/src/main/res/drawable-v21/bg_press_color_surface_top_radius_max.xml b/basic/res/src/main/res/drawable-night-v21/bg_press_color_surface_top_radius_max.xml similarity index 75% rename from basic_res/src/main/res/drawable-v21/bg_press_color_surface_top_radius_max.xml rename to basic/res/src/main/res/drawable-night-v21/bg_press_color_surface_top_radius_max.xml index 5f8d4eca..9cb8edff 100644 --- a/basic_res/src/main/res/drawable-v21/bg_press_color_surface_top_radius_max.xml +++ b/basic/res/src/main/res/drawable-night-v21/bg_press_color_surface_top_radius_max.xml @@ -1,11 +1,11 @@ + android:color="?colorRippleDark"> - + diff --git a/basic_res/src/main/res/drawable-v21/bg_press_color_transparent.xml b/basic/res/src/main/res/drawable-night-v21/bg_press_color_transparent.xml similarity index 72% rename from basic_res/src/main/res/drawable-v21/bg_press_color_transparent.xml rename to basic/res/src/main/res/drawable-night-v21/bg_press_color_transparent.xml index e065b338..57f54418 100644 --- a/basic_res/src/main/res/drawable-v21/bg_press_color_transparent.xml +++ b/basic/res/src/main/res/drawable-night-v21/bg_press_color_transparent.xml @@ -1,11 +1,11 @@ + android:color="?colorRippleDark"> - + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_circle_color_main.xml b/basic/res/src/main/res/drawable-night/bg_circle_color_main.xml similarity index 78% rename from basic_res/src/main/res/drawable/bg_circle_color_main.xml rename to basic/res/src/main/res/drawable-night/bg_circle_color_main.xml index b5e1f271..06ab090d 100644 --- a/basic_res/src/main/res/drawable/bg_circle_color_main.xml +++ b/basic/res/src/main/res/drawable-night/bg_circle_color_main.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_circle_color_surface.xml b/basic/res/src/main/res/drawable-night/bg_circle_color_surface.xml similarity index 77% rename from basic_res/src/main/res/drawable/bg_circle_color_surface.xml rename to basic/res/src/main/res/drawable-night/bg_circle_color_surface.xml index f0538490..a5cce96d 100644 --- a/basic_res/src/main/res/drawable/bg_circle_color_surface.xml +++ b/basic/res/src/main/res/drawable-night/bg_circle_color_surface.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_press_color_background_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_press_color_background_radius_max.xml similarity index 100% rename from basic_res/src/main/res/drawable/bg_press_color_background_radius_max.xml rename to basic/res/src/main/res/drawable-night/bg_press_color_background_radius_max.xml diff --git a/basic/res/src/main/res/drawable-night/bg_press_color_foreground.xml b/basic/res/src/main/res/drawable-night/bg_press_color_foreground.xml new file mode 100644 index 00000000..69a9e8dd --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_press_color_foreground.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_press_color_foreground_top_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_press_color_foreground_top_radius_max.xml similarity index 100% rename from basic_res/src/main/res/drawable/bg_press_color_foreground_top_radius_max.xml rename to basic/res/src/main/res/drawable-night/bg_press_color_foreground_top_radius_max.xml diff --git a/basic_res/src/main/res/drawable/bg_press_color_main_radius_def.xml b/basic/res/src/main/res/drawable-night/bg_press_color_main_radius_def.xml similarity index 100% rename from basic_res/src/main/res/drawable/bg_press_color_main_radius_def.xml rename to basic/res/src/main/res/drawable-night/bg_press_color_main_radius_def.xml diff --git a/basic_res/src/main/res/drawable/bg_press_color_main_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_press_color_main_radius_max.xml similarity index 100% rename from basic_res/src/main/res/drawable/bg_press_color_main_radius_max.xml rename to basic/res/src/main/res/drawable-night/bg_press_color_main_radius_max.xml diff --git a/basic/res/src/main/res/drawable-night/bg_press_color_surface.xml b/basic/res/src/main/res/drawable-night/bg_press_color_surface.xml new file mode 100644 index 00000000..dd3bb68b --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_press_color_surface.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_press_color_surface_radius_def.xml b/basic/res/src/main/res/drawable-night/bg_press_color_surface_radius_def.xml similarity index 100% rename from basic_res/src/main/res/drawable/bg_press_color_surface_radius_def.xml rename to basic/res/src/main/res/drawable-night/bg_press_color_surface_radius_def.xml diff --git a/basic_res/src/main/res/drawable/bg_press_color_surface_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_press_color_surface_radius_max.xml similarity index 100% rename from basic_res/src/main/res/drawable/bg_press_color_surface_radius_max.xml rename to basic/res/src/main/res/drawable-night/bg_press_color_surface_radius_max.xml diff --git a/basic_res/src/main/res/drawable/bg_press_color_surface_radius_small.xml b/basic/res/src/main/res/drawable-night/bg_press_color_surface_radius_small.xml similarity index 100% rename from basic_res/src/main/res/drawable/bg_press_color_surface_radius_small.xml rename to basic/res/src/main/res/drawable-night/bg_press_color_surface_radius_small.xml diff --git a/basic_res/src/main/res/drawable/bg_press_color_surface_top_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_press_color_surface_top_radius_max.xml similarity index 100% rename from basic_res/src/main/res/drawable/bg_press_color_surface_top_radius_max.xml rename to basic/res/src/main/res/drawable-night/bg_press_color_surface_top_radius_max.xml diff --git a/basic/res/src/main/res/drawable-night/bg_press_color_transparent.xml b/basic/res/src/main/res/drawable-night/bg_press_color_transparent.xml new file mode 100644 index 00000000..7410e9e1 --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_press_color_transparent.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_accent_radius_def.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_accent_radius_def.xml new file mode 100644 index 00000000..627a54f5 --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_accent_radius_def.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_accent_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_accent_radius_max.xml new file mode 100644 index 00000000..7483a53c --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_accent_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_alpha_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_alpha_radius_max.xml new file mode 100644 index 00000000..ea54e40a --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_alpha_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_alpha_radius_top_def.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_alpha_radius_top_def.xml new file mode 100644 index 00000000..12b190e9 --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_alpha_radius_top_def.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_background_radius_def.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_radius_def.xml similarity index 77% rename from basic_res/src/main/res/drawable/bg_solid_round_color_background_radius_def.xml rename to basic/res/src/main/res/drawable-night/bg_solid_round_color_background_radius_def.xml index c682da90..d2987af0 100644 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_background_radius_def.xml +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_radius_def.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_radius_max.xml new file mode 100644 index 00000000..2ebd7412 --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_background_radius_small.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_radius_small.xml similarity index 78% rename from basic_res/src/main/res/drawable/bg_solid_round_color_background_radius_small.xml rename to basic/res/src/main/res/drawable-night/bg_solid_round_color_background_radius_small.xml index 1161ded9..dd336564 100644 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_background_radius_small.xml +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_background_radius_small.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_black_alpha_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_black_alpha_radius_max.xml new file mode 100644 index 00000000..7e1e9076 --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_black_alpha_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_background_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_foreground_top_radius_max.xml similarity index 75% rename from basic_res/src/main/res/drawable/bg_solid_round_color_background_radius_max.xml rename to basic/res/src/main/res/drawable-night/bg_solid_round_color_foreground_top_radius_max.xml index 36254781..48172880 100644 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_background_radius_max.xml +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_foreground_top_radius_max.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_line_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_line_radius_max.xml new file mode 100644 index 00000000..03ffab09 --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_line_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_main_radius_def.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_main_radius_def.xml similarity index 79% rename from basic_res/src/main/res/drawable/bg_solid_round_color_main_radius_def.xml rename to basic/res/src/main/res/drawable-night/bg_solid_round_color_main_radius_def.xml index cc28ab74..62306dc3 100644 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_main_radius_def.xml +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_main_radius_def.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_main_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_main_radius_max.xml similarity index 78% rename from basic_res/src/main/res/drawable/bg_solid_round_color_main_radius_max.xml rename to basic/res/src/main/res/drawable-night/bg_solid_round_color_main_radius_max.xml index b5e1f271..06ab090d 100644 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_main_radius_max.xml +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_main_radius_max.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_mask_radius_def.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_mask_radius_def.xml new file mode 100644 index 00000000..eb5c1ed8 --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_mask_radius_def.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_accent_radius_def.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_radius_def.xml similarity index 79% rename from basic_res/src/main/res/drawable/bg_solid_round_color_accent_radius_def.xml rename to basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_radius_def.xml index ed761715..26ac1ebf 100644 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_accent_radius_def.xml +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_radius_def.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_surface_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_radius_max.xml similarity index 77% rename from basic_res/src/main/res/drawable/bg_solid_round_color_surface_radius_max.xml rename to basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_radius_max.xml index f0538490..a5cce96d 100644 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_surface_radius_max.xml +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_radius_max.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_surface_radius_small.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_radius_small.xml similarity index 79% rename from basic_res/src/main/res/drawable/bg_solid_round_color_surface_radius_small.xml rename to basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_radius_small.xml index 5501ca36..68783847 100644 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_surface_radius_small.xml +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_radius_small.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_surface_radius_top_def.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_radius_top_def.xml similarity index 87% rename from basic_res/src/main/res/drawable/bg_solid_round_color_surface_radius_top_def.xml rename to basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_radius_top_def.xml index 6fd1c504..66d0f5b8 100644 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_surface_radius_top_def.xml +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_radius_top_def.xml @@ -1,6 +1,6 @@ - + + + + + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_background_alpha_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_top_alpha_radius_max.xml similarity index 72% rename from basic_res/src/main/res/drawable/bg_solid_round_color_background_alpha_radius_max.xml rename to basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_top_alpha_radius_max.xml index 10538b88..0ad352f4 100644 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_background_alpha_radius_max.xml +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_top_alpha_radius_max.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_top_radius_def.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_top_radius_def.xml new file mode 100644 index 00000000..1a40f8f9 --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_top_radius_def.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_top_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_top_radius_max.xml new file mode 100644 index 00000000..ca3a4d43 --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_top_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_top_radius_small.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_top_radius_small.xml new file mode 100644 index 00000000..a6e275df --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_surface_top_radius_small.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_white_radius_def.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_white_radius_def.xml new file mode 100644 index 00000000..ff73303d --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_white_radius_def.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_solid_round_color_white_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_solid_round_color_white_radius_max.xml new file mode 100644 index 00000000..880a0dae --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_solid_round_color_white_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_stroke_3dp_round_color_main_radius_def.xml b/basic/res/src/main/res/drawable-night/bg_stroke_3dp_round_color_main_radius_def.xml new file mode 100644 index 00000000..80ae0d7e --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_stroke_3dp_round_color_main_radius_def.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_stroke_round_color_main_radius_def.xml b/basic/res/src/main/res/drawable-night/bg_stroke_round_color_main_radius_def.xml similarity index 68% rename from basic_res/src/main/res/drawable/bg_stroke_round_color_main_radius_def.xml rename to basic/res/src/main/res/drawable-night/bg_stroke_round_color_main_radius_def.xml index 4d8c8b89..4edb951b 100644 --- a/basic_res/src/main/res/drawable/bg_stroke_round_color_main_radius_def.xml +++ b/basic/res/src/main/res/drawable-night/bg_stroke_round_color_main_radius_def.xml @@ -1,5 +1,7 @@ - + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_stroke_round_color_main_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_stroke_round_color_main_radius_max.xml similarity index 66% rename from basic_res/src/main/res/drawable/bg_stroke_round_color_main_radius_max.xml rename to basic/res/src/main/res/drawable-night/bg_stroke_round_color_main_radius_max.xml index d8eb2557..351ad9c1 100644 --- a/basic_res/src/main/res/drawable/bg_stroke_round_color_main_radius_max.xml +++ b/basic/res/src/main/res/drawable-night/bg_stroke_round_color_main_radius_max.xml @@ -1,5 +1,7 @@ - + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_stroke_round_color_surface_radius_def.xml b/basic/res/src/main/res/drawable-night/bg_stroke_round_color_surface_radius_def.xml similarity index 84% rename from basic_res/src/main/res/drawable/bg_stroke_round_color_surface_radius_def.xml rename to basic/res/src/main/res/drawable-night/bg_stroke_round_color_surface_radius_def.xml index 92ad08b9..cc254207 100644 --- a/basic_res/src/main/res/drawable/bg_stroke_round_color_surface_radius_def.xml +++ b/basic/res/src/main/res/drawable-night/bg_stroke_round_color_surface_radius_def.xml @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_stroke_round_color_surface_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_stroke_round_color_surface_radius_max.xml similarity index 83% rename from basic_res/src/main/res/drawable/bg_stroke_round_color_surface_radius_max.xml rename to basic/res/src/main/res/drawable-night/bg_stroke_round_color_surface_radius_max.xml index 48506171..d5a0aa28 100644 --- a/basic_res/src/main/res/drawable/bg_stroke_round_color_surface_radius_max.xml +++ b/basic/res/src/main/res/drawable-night/bg_stroke_round_color_surface_radius_max.xml @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-night/bg_stroke_round_color_white_radius_max.xml b/basic/res/src/main/res/drawable-night/bg_stroke_round_color_white_radius_max.xml new file mode 100644 index 00000000..f9962d8e --- /dev/null +++ b/basic/res/src/main/res/drawable-night/bg_stroke_round_color_white_radius_max.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-v21/bg_press_color_background_radius_max.xml b/basic/res/src/main/res/drawable-v21/bg_press_color_background_radius_max.xml new file mode 100644 index 00000000..c181fc31 --- /dev/null +++ b/basic/res/src/main/res/drawable-v21/bg_press_color_background_radius_max.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-v21/bg_press_color_foreground.xml b/basic/res/src/main/res/drawable-v21/bg_press_color_foreground.xml new file mode 100644 index 00000000..b473b997 --- /dev/null +++ b/basic/res/src/main/res/drawable-v21/bg_press_color_foreground.xml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-v21/bg_press_color_foreground_top_radius_max.xml b/basic/res/src/main/res/drawable-v21/bg_press_color_foreground_top_radius_max.xml new file mode 100644 index 00000000..a190ce5a --- /dev/null +++ b/basic/res/src/main/res/drawable-v21/bg_press_color_foreground_top_radius_max.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-v21/bg_press_color_main_radius_def.xml b/basic/res/src/main/res/drawable-v21/bg_press_color_main_radius_def.xml new file mode 100644 index 00000000..11e79a20 --- /dev/null +++ b/basic/res/src/main/res/drawable-v21/bg_press_color_main_radius_def.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-v21/bg_press_color_main_radius_max.xml b/basic/res/src/main/res/drawable-v21/bg_press_color_main_radius_max.xml new file mode 100644 index 00000000..21ae7a79 --- /dev/null +++ b/basic/res/src/main/res/drawable-v21/bg_press_color_main_radius_max.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-v21/bg_press_color_surface.xml b/basic/res/src/main/res/drawable-v21/bg_press_color_surface.xml new file mode 100644 index 00000000..e79b2484 --- /dev/null +++ b/basic/res/src/main/res/drawable-v21/bg_press_color_surface.xml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-v21/bg_press_color_surface_radius_def.xml b/basic/res/src/main/res/drawable-v21/bg_press_color_surface_radius_def.xml new file mode 100644 index 00000000..c20ca257 --- /dev/null +++ b/basic/res/src/main/res/drawable-v21/bg_press_color_surface_radius_def.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-v21/bg_press_color_surface_radius_max.xml b/basic/res/src/main/res/drawable-v21/bg_press_color_surface_radius_max.xml new file mode 100644 index 00000000..201fe384 --- /dev/null +++ b/basic/res/src/main/res/drawable-v21/bg_press_color_surface_radius_max.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-v21/bg_press_color_surface_radius_small.xml b/basic/res/src/main/res/drawable-v21/bg_press_color_surface_radius_small.xml new file mode 100644 index 00000000..76f5efe8 --- /dev/null +++ b/basic/res/src/main/res/drawable-v21/bg_press_color_surface_radius_small.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-v21/bg_press_color_surface_top_radius_max.xml b/basic/res/src/main/res/drawable-v21/bg_press_color_surface_top_radius_max.xml new file mode 100644 index 00000000..9cb8edff --- /dev/null +++ b/basic/res/src/main/res/drawable-v21/bg_press_color_surface_top_radius_max.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-v21/bg_press_color_transparent.xml b/basic/res/src/main/res/drawable-v21/bg_press_color_transparent.xml new file mode 100644 index 00000000..57f54418 --- /dev/null +++ b/basic/res/src/main/res/drawable-v21/bg_press_color_transparent.xml @@ -0,0 +1,11 @@ + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable-xxhdpi/ic_back.png b/basic/res/src/main/res/drawable-xxhdpi/ic_back.png new file mode 100644 index 00000000..c10af283 Binary files /dev/null and b/basic/res/src/main/res/drawable-xxhdpi/ic_back.png differ diff --git a/basic/res/src/main/res/drawable-xxhdpi/ic_close.png b/basic/res/src/main/res/drawable-xxhdpi/ic_close.png new file mode 100644 index 00000000..51b51ee6 Binary files /dev/null and b/basic/res/src/main/res/drawable-xxhdpi/ic_close.png differ diff --git a/basic/res/src/main/res/drawable/bg_circle_color_main.xml b/basic/res/src/main/res/drawable/bg_circle_color_main.xml new file mode 100644 index 00000000..06ab090d --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_circle_color_main.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_circle_color_surface.xml b/basic/res/src/main/res/drawable/bg_circle_color_surface.xml new file mode 100644 index 00000000..a5cce96d --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_circle_color_surface.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_gradient_color_surface_to_transparent.xml b/basic/res/src/main/res/drawable/bg_gradient_color_surface_to_transparent.xml new file mode 100644 index 00000000..805e61ce --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_gradient_color_surface_to_transparent.xml @@ -0,0 +1,7 @@ + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_press_color_background_radius_max.xml b/basic/res/src/main/res/drawable/bg_press_color_background_radius_max.xml new file mode 100644 index 00000000..ec01a4ed --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_press_color_background_radius_max.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_press_color_foreground.xml b/basic/res/src/main/res/drawable/bg_press_color_foreground.xml new file mode 100644 index 00000000..69a9e8dd --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_press_color_foreground.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_press_color_foreground_top_radius_max.xml b/basic/res/src/main/res/drawable/bg_press_color_foreground_top_radius_max.xml new file mode 100644 index 00000000..f2322d9a --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_press_color_foreground_top_radius_max.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_press_color_main_radius_def.xml b/basic/res/src/main/res/drawable/bg_press_color_main_radius_def.xml new file mode 100644 index 00000000..b0a54a51 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_press_color_main_radius_def.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_press_color_main_radius_max.xml b/basic/res/src/main/res/drawable/bg_press_color_main_radius_max.xml new file mode 100644 index 00000000..07ae2b26 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_press_color_main_radius_max.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_press_color_surface.xml b/basic/res/src/main/res/drawable/bg_press_color_surface.xml new file mode 100644 index 00000000..dd3bb68b --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_press_color_surface.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_press_color_surface_radius_def.xml b/basic/res/src/main/res/drawable/bg_press_color_surface_radius_def.xml new file mode 100644 index 00000000..32cca93e --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_press_color_surface_radius_def.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_press_color_surface_radius_max.xml b/basic/res/src/main/res/drawable/bg_press_color_surface_radius_max.xml new file mode 100644 index 00000000..d6b7a484 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_press_color_surface_radius_max.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_press_color_surface_radius_small.xml b/basic/res/src/main/res/drawable/bg_press_color_surface_radius_small.xml new file mode 100644 index 00000000..9bd021d3 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_press_color_surface_radius_small.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_press_color_surface_top_radius_max.xml b/basic/res/src/main/res/drawable/bg_press_color_surface_top_radius_max.xml new file mode 100644 index 00000000..e2076ecc --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_press_color_surface_top_radius_max.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_press_color_transparent.xml b/basic/res/src/main/res/drawable/bg_press_color_transparent.xml new file mode 100644 index 00000000..7410e9e1 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_press_color_transparent.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_press_color_transparent_radius_def.xml b/basic/res/src/main/res/drawable/bg_press_color_transparent_radius_def.xml new file mode 100644 index 00000000..f5d9f178 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_press_color_transparent_radius_def.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_accent_radius_def.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_accent_radius_def.xml new file mode 100644 index 00000000..627a54f5 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_accent_radius_def.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_accent_radius_max.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_accent_radius_max.xml new file mode 100644 index 00000000..7483a53c --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_accent_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_background_alpha_radius_max.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_background_alpha_radius_max.xml new file mode 100644 index 00000000..ea54e40a --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_background_alpha_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_background_alpha_radius_top_def.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_background_alpha_radius_top_def.xml new file mode 100644 index 00000000..12b190e9 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_background_alpha_radius_top_def.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_background_radius_def.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_background_radius_def.xml new file mode 100644 index 00000000..d2987af0 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_background_radius_def.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_background_radius_max.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_background_radius_max.xml new file mode 100644 index 00000000..2ebd7412 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_background_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_background_radius_small.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_background_radius_small.xml new file mode 100644 index 00000000..dd336564 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_background_radius_small.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_black_alpha_radius_max.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_black_alpha_radius_max.xml new file mode 100644 index 00000000..7e1e9076 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_black_alpha_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_foreground_top_radius_max.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_foreground_top_radius_max.xml new file mode 100644 index 00000000..48172880 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_foreground_top_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_line_radius_max.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_line_radius_max.xml new file mode 100644 index 00000000..03ffab09 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_line_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_main_radius_def.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_main_radius_def.xml new file mode 100644 index 00000000..62306dc3 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_main_radius_def.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_main_radius_max.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_main_radius_max.xml new file mode 100644 index 00000000..06ab090d --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_main_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_surface_mask_radius_def.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_mask_radius_def.xml new file mode 100644 index 00000000..eb5c1ed8 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_mask_radius_def.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_surface_radius_def.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_radius_def.xml similarity index 78% rename from basic_res/src/main/res/drawable/bg_solid_round_color_surface_radius_def.xml rename to basic/res/src/main/res/drawable/bg_solid_round_color_surface_radius_def.xml index c7974520..26ac1ebf 100644 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_surface_radius_def.xml +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_radius_def.xml @@ -1,5 +1,5 @@ - + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_surface_radius_max.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_radius_max.xml new file mode 100644 index 00000000..a5cce96d --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_surface_radius_small.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_radius_small.xml new file mode 100644 index 00000000..68783847 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_radius_small.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_surface_radius_top_def.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_radius_top_def.xml new file mode 100644 index 00000000..66d0f5b8 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_radius_top_def.xml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_alpha_radius_def.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_alpha_radius_def.xml new file mode 100644 index 00000000..1998c2ce --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_alpha_radius_def.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_alpha_radius_max.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_alpha_radius_max.xml new file mode 100644 index 00000000..0ad352f4 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_alpha_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_radius_def.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_radius_def.xml new file mode 100644 index 00000000..1a40f8f9 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_radius_def.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_radius_max.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_radius_max.xml new file mode 100644 index 00000000..ca3a4d43 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_radius_small.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_radius_small.xml new file mode 100644 index 00000000..a6e275df --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_surface_top_radius_small.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_white_radius_def.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_white_radius_def.xml new file mode 100644 index 00000000..ff73303d --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_white_radius_def.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_solid_round_color_white_radius_max.xml b/basic/res/src/main/res/drawable/bg_solid_round_color_white_radius_max.xml new file mode 100644 index 00000000..880a0dae --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_solid_round_color_white_radius_max.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_stroke_3dp_round_color_main_radius_def.xml b/basic/res/src/main/res/drawable/bg_stroke_3dp_round_color_main_radius_def.xml new file mode 100644 index 00000000..80ae0d7e --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_stroke_3dp_round_color_main_radius_def.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_stroke_round_color_main_radius_def.xml b/basic/res/src/main/res/drawable/bg_stroke_round_color_main_radius_def.xml new file mode 100644 index 00000000..4edb951b --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_stroke_round_color_main_radius_def.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_stroke_round_color_main_radius_max.xml b/basic/res/src/main/res/drawable/bg_stroke_round_color_main_radius_max.xml new file mode 100644 index 00000000..351ad9c1 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_stroke_round_color_main_radius_max.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_stroke_round_color_surface_radius_def.xml b/basic/res/src/main/res/drawable/bg_stroke_round_color_surface_radius_def.xml new file mode 100644 index 00000000..cc254207 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_stroke_round_color_surface_radius_def.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_stroke_round_color_surface_radius_max.xml b/basic/res/src/main/res/drawable/bg_stroke_round_color_surface_radius_max.xml new file mode 100644 index 00000000..d5a0aa28 --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_stroke_round_color_surface_radius_max.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/drawable/bg_stroke_round_color_white_radius_max.xml b/basic/res/src/main/res/drawable/bg_stroke_round_color_white_radius_max.xml new file mode 100644 index 00000000..f9962d8e --- /dev/null +++ b/basic/res/src/main/res/drawable/bg_stroke_round_color_white_radius_max.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/values-night/themes.xml b/basic/res/src/main/res/values-night/themes.xml new file mode 100644 index 00000000..1d530357 --- /dev/null +++ b/basic/res/src/main/res/values-night/themes.xml @@ -0,0 +1,89 @@ + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/values/attrs_theme.xml b/basic/res/src/main/res/values/attrs_theme.xml new file mode 100644 index 00000000..c418452d --- /dev/null +++ b/basic/res/src/main/res/values/attrs_theme.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/values/colors.xml b/basic/res/src/main/res/values/colors.xml new file mode 100644 index 00000000..37bb2f35 --- /dev/null +++ b/basic/res/src/main/res/values/colors.xml @@ -0,0 +1,58 @@ + + + #00000000 + + #4282f4 + #994282f4 + #3365BD + #993365BD + #73a3f5 + #1173a3f5 + #9973a3f5 + + #3cdc86 + #67db9d + + #f86734 + #f78c65 + + #000000 + #11000000 + #14000000 + #1A000000 + #22000000 + #4D000000 + #66000000 + #77000000 + #aa000000 + #ee000000 + + #111111 + #dd111111 + #131313 + #181818 + #cc181818 + #222425 + #EE222425 + #363839 + #EE363839 + + #eaeaea + #f5f5f5 + #ccf5f5f5 + #333333 + #88333333 + #666666 + #999999 + #cccccc + + #ffffff + #11ffffff + #33ffffff + #4DFFFFFF + #54ffffff + #88ffffff + #B3FFFFFF + #ccffffff + + \ No newline at end of file diff --git a/basic_res/src/main/res/values/dimens.xml b/basic/res/src/main/res/values/dimens.xml similarity index 65% rename from basic_res/src/main/res/values/dimens.xml rename to basic/res/src/main/res/values/dimens.xml index d7cd8304..f8a4e119 100644 --- a/basic_res/src/main/res/values/dimens.xml +++ b/basic/res/src/main/res/values/dimens.xml @@ -5,15 +5,29 @@ 0dp 49dp 44dp + 24dp + 22dp + 36dp + + 1px + 2px + 1dp + + 44dp + 4dp 12dp 3dp + 4dp 6dp 8dp + 10dp 16dp 32dp 48dp + 60dp + 80dp 22sp diff --git a/basic_res/src/main/res/values/integers.xml b/basic/res/src/main/res/values/integers.xml similarity index 100% rename from basic_res/src/main/res/values/integers.xml rename to basic/res/src/main/res/values/integers.xml diff --git a/basic/res/src/main/res/values/strings.xml b/basic/res/src/main/res/values/strings.xml new file mode 100644 index 00000000..85420055 --- /dev/null +++ b/basic/res/src/main/res/values/strings.xml @@ -0,0 +1,2 @@ + + diff --git a/basic/res/src/main/res/values/styles.xml b/basic/res/src/main/res/values/styles.xml new file mode 100644 index 00000000..99395f6a --- /dev/null +++ b/basic/res/src/main/res/values/styles.xml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/basic/res/src/main/res/values/themes.xml b/basic/res/src/main/res/values/themes.xml new file mode 100644 index 00000000..80ca29a9 --- /dev/null +++ b/basic/res/src/main/res/values/themes.xml @@ -0,0 +1,88 @@ + + + + + + \ No newline at end of file diff --git a/basic/ui/build.gradle b/basic/ui/build.gradle new file mode 100644 index 00000000..b8b05115 --- /dev/null +++ b/basic/ui/build.gradle @@ -0,0 +1,37 @@ +apply plugin: 'com.android.library' +apply from: "${rootDir.path}/gradle/basic.gradle" + +android { + namespace "per.goweii.basic.ui" +} + +dependencies { + api project(":basic:utils") + api project(":basic:res") + api project(":basic:l10n") + + api rootProject.ext.dependencies.design + api rootProject.ext.dependencies.smartRefreshLayoutKernel + api rootProject.ext.dependencies.smartRefreshHeaderClassics + api rootProject.ext.dependencies.smartRefreshHeaderTwoLevel + api rootProject.ext.dependencies.smartRefreshFooterClassics + api rootProject.ext.dependencies.bravh + api rootProject.ext.dependencies.recyclerview + api rootProject.ext.dependencies.anyLayer + api rootProject.ext.dependencies.actionBarEx + api rootProject.ext.dependencies.percentImageView + api rootProject.ext.dependencies.roundedimageview + api rootProject.ext.dependencies.circleimageview + api rootProject.ext.dependencies.magicIndicator + api rootProject.ext.dependencies.materialProgressBar + api rootProject.ext.dependencies.shadowLayout +// api rootProject.ext.dependencies.blurred +// api rootProject.ext.dependencies.visualeffectCore +// api rootProject.ext.dependencies.visualeffectView +// api rootProject.ext.dependencies.visualeffectBlur + + implementation 'com.github.woxingxiao:BounceScrollView:1.5-androidx' +} +repositories { + mavenCentral() +} diff --git a/basic_res/proguard-rules.pro b/basic/ui/proguard-rules.pro similarity index 100% rename from basic_res/proguard-rules.pro rename to basic/ui/proguard-rules.pro diff --git a/basic/ui/src/main/AndroidManifest.xml b/basic/ui/src/main/AndroidManifest.xml new file mode 100644 index 00000000..9b65eb06 --- /dev/null +++ b/basic/ui/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/actionbar/ActionBarSimple.java b/basic/ui/src/main/java/per/goweii/basic/ui/actionbar/ActionBarSimple.java similarity index 87% rename from basic_ui/src/main/java/per/goweii/basic/ui/actionbar/ActionBarSimple.java rename to basic/ui/src/main/java/per/goweii/basic/ui/actionbar/ActionBarSimple.java index 0965fac5..5c6381b1 100644 --- a/basic_ui/src/main/java/per/goweii/basic/ui/actionbar/ActionBarSimple.java +++ b/basic/ui/src/main/java/per/goweii/basic/ui/actionbar/ActionBarSimple.java @@ -1,16 +1,15 @@ package per.goweii.basic.ui.actionbar; import android.content.Context; -import android.support.annotation.Nullable; import android.util.AttributeSet; +import androidx.annotation.Nullable; + import per.goweii.actionbarex.ActionBarEx; /** * @author CuiZhen * @date 2019/5/15 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ActionBarSimple extends ActionBarEx { diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/dialog/DownloadDialog.java b/basic/ui/src/main/java/per/goweii/basic/ui/dialog/DownloadDialog.java similarity index 71% rename from basic_ui/src/main/java/per/goweii/basic/ui/dialog/DownloadDialog.java rename to basic/ui/src/main/java/per/goweii/basic/ui/dialog/DownloadDialog.java index 45c626ff..c56cdf04 100644 --- a/basic_ui/src/main/java/per/goweii/basic/ui/dialog/DownloadDialog.java +++ b/basic/ui/src/main/java/per/goweii/basic/ui/dialog/DownloadDialog.java @@ -6,6 +6,8 @@ import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.NonNull; + import java.io.File; import per.goweii.anylayer.AnyLayer; @@ -87,23 +89,23 @@ public void onPostExecute(File apk) { private void showDialog() { mAnyLayer = AnyLayer.dialog(mActivity) - .contentView(R.layout.basic_ui_dialog_download) - .gravity(Gravity.CENTER) - .backgroundColorRes(R.color.dialog_bg) - .cancelableOnTouchOutside(false) - .cancelableOnClickKeyBack(false) - .bindData(new Layer.DataBinder() { + .setContentView(R.layout.basic_ui_dialog_download) + .setGravity(Gravity.CENTER) + .setBackgroundDimDefault() + .setCancelableOnTouchOutside(false) + .setCancelableOnClickKeyBack(false) + .addOnBindDataListener(new Layer.OnBindDataListener() { @Override - public void bindData(Layer layer) { - progressBar = layer.getView(R.id.basic_ui_pb_dialog_download); - tvProgress = layer.getView(R.id.basic_ui_tv_dialog_download_progress); - tvApkSize = layer.getView(R.id.basic_ui_tv_dialog_download_apk_size); - tvState = layer.getView(R.id.basic_ui_tv_dialog_download_state); + public void onBindData(@NonNull Layer layer) { + progressBar = layer.requireView(R.id.basic_ui_pb_dialog_download); + tvProgress = layer.requireView(R.id.basic_ui_tv_dialog_download_progress); + tvApkSize = layer.requireView(R.id.basic_ui_tv_dialog_download_apk_size); + tvState = layer.requireView(R.id.basic_ui_tv_dialog_download_state); } }) - .onClick(new Layer.OnClickListener() { + .addOnClickListener(new Layer.OnClickListener() { @Override - public void onClick(Layer layer, View v) { + public void onClick(@NonNull Layer layer, @NonNull View v) { if (mApk == null) { return; } @@ -122,13 +124,13 @@ private void preDownload() { progressBar.setProgress(0); } if (tvApkSize != null) { - tvApkSize.setText("0B"); + tvApkSize.setText(mActivity.getString(R.string.n_bytes, 0)); } if (tvProgress != null) { - tvProgress.setText("0"); + tvProgress.setText(mActivity.getString(R.string.n_percent, 0)); } if (tvState != null) { - tvState.setText(R.string.basic_ui_dialog_download_state_downloading); + tvState.setText(R.string.dialog_download_state_downloading); } } @@ -137,12 +139,12 @@ private void setProgress(int progress) { progressBar.setProgress(progress); } if (tvProgress != null) { - tvProgress.setText("" + progress); + tvProgress.setText(mActivity.getString(R.string.n_percent, progress)); } if (progress >= 100) { if (tvState != null) { - tvState.setText(R.string.basic_ui_dialog_download_state_install); - tvState.setTextColor(ResUtils.getColor(mActivity, R.color.text_main)); + tvState.setText(R.string.dialog_download_state_install); + tvState.setTextColor(ResUtils.getThemeColor(mActivity, R.attr.colorTextMain)); } } } diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/dialog/ListDialog.java b/basic/ui/src/main/java/per/goweii/basic/ui/dialog/ListDialog.java similarity index 62% rename from basic_ui/src/main/java/per/goweii/basic/ui/dialog/ListDialog.java rename to basic/ui/src/main/java/per/goweii/basic/ui/dialog/ListDialog.java index 9241d138..3cedaf6b 100644 --- a/basic_ui/src/main/java/per/goweii/basic/ui/dialog/ListDialog.java +++ b/basic/ui/src/main/java/per/goweii/basic/ui/dialog/ListDialog.java @@ -1,15 +1,16 @@ package per.goweii.basic.ui.dialog; -import android.animation.Animator; import android.content.Context; -import android.support.annotation.StringRes; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; import android.view.Gravity; import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; @@ -17,17 +18,15 @@ import java.util.Arrays; import java.util.List; -import per.goweii.anylayer.AnimatorHelper; import per.goweii.anylayer.AnyLayer; import per.goweii.anylayer.Layer; +import per.goweii.anylayer.widget.SwipeLayout; import per.goweii.basic.ui.R; import per.goweii.basic.utils.ResUtils; /** * @author CuiZhen * @date 2019/5/20 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ListDialog { @@ -39,7 +38,8 @@ public class ListDialog { private boolean noBtn = false; private boolean singleBtnYes = false; private boolean cancelable = true; - private OnItemSelectedListener listener = null; + private OnItemSelectedListener onItemSelectedListener = null; + private OnDismissListener onDismissListener = null; private BaseQuickAdapter mAdapter = null; private List datas = new ArrayList<>(); private int currSelectPos = -1; @@ -111,34 +111,29 @@ public ListDialog currSelectPos(int currSelectPos) { return this; } - public ListDialog listener(OnItemSelectedListener listener) { - this.listener = listener; + public ListDialog onItemSelectedListener(OnItemSelectedListener listener) { + this.onItemSelectedListener = listener; + return this; + } + + public ListDialog onDismissListener(OnDismissListener listener) { + this.onDismissListener = listener; return this; } public void show() { AnyLayer.dialog(context) - .contentView(R.layout.basic_ui_dialog_list) - .backgroundColorRes(R.color.dialog_bg) - .gravity(Gravity.BOTTOM) - .contentAnimator(new Layer.AnimatorCreator() { - @Override - public Animator createInAnimator(View target) { - return AnimatorHelper.createBottomInAnim(target); - } - - @Override - public Animator createOutAnimator(View target) { - return AnimatorHelper.createBottomOutAnim(target); - } - }) - .cancelableOnTouchOutside(cancelable) - .cancelableOnClickKeyBack(cancelable) - .bindData(new Layer.DataBinder() { + .setContentView(R.layout.basic_ui_dialog_list) + .setGravity(Gravity.BOTTOM) + .setBackgroundDimDefault() + .setSwipeDismiss(SwipeLayout.Direction.BOTTOM) + .setCancelableOnTouchOutside(cancelable) + .setCancelableOnClickKeyBack(cancelable) + .addOnBindDataListener(new Layer.OnBindDataListener() { @Override - public void bindData(Layer layer) { - LinearLayout llYesNo = layer.getView(R.id.basic_ui_ll_dialog_list_yes_no); - View vLineH = layer.getView(R.id.basic_ui_v_dialog_list_line_h); + public void onBindData(@NonNull Layer layer) { + LinearLayout llYesNo = layer.requireView(R.id.basic_ui_ll_dialog_list_yes_no); + View vLineH = layer.requireView(R.id.basic_ui_v_dialog_list_line_h); if (noBtn) { vLineH.setVisibility(View.GONE); @@ -146,13 +141,13 @@ public void bindData(Layer layer) { } else { vLineH.setVisibility(View.VISIBLE); llYesNo.setVisibility(View.VISIBLE); - TextView tvYes = layer.getView(R.id.basic_ui_tv_dialog_list_yes); - TextView tvNo = layer.getView(R.id.basic_ui_tv_dialog_list_no); - View vLine = layer.getView(R.id.basic_ui_v_dialog_list_line); + TextView tvYes = layer.requireView(R.id.basic_ui_tv_dialog_list_yes); + TextView tvNo = layer.requireView(R.id.basic_ui_tv_dialog_list_no); + View vLine = layer.requireView(R.id.basic_ui_v_dialog_list_line); if (yesText != null) { tvYes.setText(yesText); } else { - tvYes.setText(R.string.basic_ui_dialog_btn_yes); + tvYes.setText(R.string.dialog_btn_yes); } if (singleBtnYes) { tvNo.setVisibility(View.GONE); @@ -163,28 +158,31 @@ public void bindData(Layer layer) { if (noText != null) { tvNo.setText(noText); } else { - tvNo.setText(R.string.basic_ui_dialog_btn_no); + tvNo.setText(R.string.dialog_btn_no); } } } - TextView tvTitle = layer.getView(R.id.basic_ui_tv_dialog_list_title); + TextView tvTitle = layer.requireView(R.id.basic_ui_tv_dialog_list_title); + View vTitleLineH = layer.requireView(R.id.basic_ui_v_dialog_title_line_h); if (title == null) { tvTitle.setVisibility(View.GONE); + vTitleLineH.setVisibility(View.GONE); } else { tvTitle.setText(title); + vTitleLineH.setVisibility(View.VISIBLE); } - RecyclerView rv = layer.getView(R.id.basic_ui_rv_dialog_list); + RecyclerView rv = layer.requireView(R.id.basic_ui_rv_dialog_list); rv.setLayoutManager(new LinearLayoutManager(rv.getContext())); mAdapter = new BaseQuickAdapter(R.layout.basic_ui_rv_item_dialog_list) { @Override protected void convert(BaseViewHolder helper, String item) { TextView tvName = helper.getView(R.id.basic_ui_tv_dialog_list_name); if (helper.getAdapterPosition() == currSelectPos) { - tvName.setTextColor(ResUtils.getColor(tvName.getContext(), R.color.text_main)); + tvName.setTextColor(ResUtils.getThemeColor(tvName.getContext(), R.attr.colorTextMain)); } else { - tvName.setTextColor(ResUtils.getColor(tvName.getContext(), R.color.text_surface)); + tvName.setTextColor(ResUtils.getThemeColor(tvName.getContext(), R.attr.colorTextSurface)); } tvName.setText(item); } @@ -195,8 +193,8 @@ public void onItemClick(BaseQuickAdapter adapter, View view, int position) { currSelectPos = position; adapter.notifyDataSetChanged(); if (noBtn) { - if (listener != null) { - listener.onSelect(datas.get(currSelectPos), currSelectPos); + if (onItemSelectedListener != null) { + onItemSelectedListener.onSelect(datas.get(currSelectPos), currSelectPos); } layer.dismiss(); } @@ -206,18 +204,38 @@ public void onItemClick(BaseQuickAdapter adapter, View view, int position) { mAdapter.setNewData(datas); } }) - .onClickToDismiss(new Layer.OnClickListener() { + .addOnClickToDismissListener(new Layer.OnClickListener() { @Override - public void onClick(Layer layer, View v) { - if (listener != null) { - listener.onSelect(datas.get(currSelectPos), currSelectPos); + public void onClick(@NonNull Layer layer, @NonNull View v) { + if (onItemSelectedListener != null) { + onItemSelectedListener.onSelect(datas.get(currSelectPos), currSelectPos); } } }, R.id.basic_ui_tv_dialog_list_yes) - .onClickToDismiss(R.id.basic_ui_tv_dialog_list_no) + .addOnClickToDismissListener(R.id.basic_ui_tv_dialog_list_no) + .addOnDismissListener(new Layer.OnDismissListener() { + @Override + public void onPreDismiss(@NonNull Layer layer) { + if (onDismissListener != null) { + onDismissListener.onPreDismiss(datas.get(currSelectPos), currSelectPos); + } + } + + @Override + public void onPostDismiss(@NonNull Layer layer) { + if (onDismissListener != null) { + onDismissListener.onPostDismiss(datas.get(currSelectPos), currSelectPos); + } + } + }) .show(); } + public interface OnDismissListener { + void onPreDismiss(String data, int pos); + void onPostDismiss(String data, int pos); + } + public interface OnItemSelectedListener { void onSelect(String data, int pos); } diff --git a/basic/ui/src/main/java/per/goweii/basic/ui/dialog/LoadingDialog.java b/basic/ui/src/main/java/per/goweii/basic/ui/dialog/LoadingDialog.java new file mode 100644 index 00000000..30ddaf3f --- /dev/null +++ b/basic/ui/src/main/java/per/goweii/basic/ui/dialog/LoadingDialog.java @@ -0,0 +1,80 @@ +package per.goweii.basic.ui.dialog; + +import android.animation.Animator; +import android.content.Context; +import android.graphics.Color; +import android.view.Gravity; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import per.goweii.anylayer.AnyLayer; +import per.goweii.anylayer.Layer; +import per.goweii.anylayer.dialog.DialogLayer; +import per.goweii.anylayer.utils.AnimatorHelper; +import per.goweii.basic.ui.R; + +/** + * loading弹窗 + * + * @author Cuizhen + * @date 2018/6/21-上午10:00 + */ +public class LoadingDialog { + + private static final long ANIM_DURATION = 200; + private final Context context; + private DialogLayer mAnyDialog; + private int count = 0; + + private LoadingDialog(Context context) { + this.context = context; + } + + public static LoadingDialog with(Context context) { + return new LoadingDialog(context); + } + + public void show() { + if (count <= 0) { + count = 0; + mAnyDialog = AnyLayer.dialog(context) + .setContentView(R.layout.basic_ui_dialog_loading) + .setBackgroundColorInt(Color.TRANSPARENT) + .setCancelableOnTouchOutside(false) + .setCancelableOnClickKeyBack(false) + .setGravity(Gravity.CENTER) + .setContentAnimator(new Layer.AnimatorCreator() { + @Nullable + @Override + public Animator createInAnimator(@NonNull View target) { + return AnimatorHelper.createZoomInAnim(target).setDuration(ANIM_DURATION); + } + + @Nullable + @Override + public Animator createOutAnimator(@NonNull View target) { + return AnimatorHelper.createZoomOutAnim(target).setDuration(ANIM_DURATION); + } + }); + mAnyDialog.show(); + } + count++; + } + + public void dismiss() { + count--; + if (count <= 0) { + clear(); + } + } + + public void clear() { + if (mAnyDialog != null) { + mAnyDialog.dismiss(false); + mAnyDialog = null; + } + count = 0; + } +} diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/dialog/PermissionDialog.java b/basic/ui/src/main/java/per/goweii/basic/ui/dialog/PermissionDialog.java similarity index 71% rename from basic_ui/src/main/java/per/goweii/basic/ui/dialog/PermissionDialog.java rename to basic/ui/src/main/java/per/goweii/basic/ui/dialog/PermissionDialog.java index feae6ebc..15a201e4 100644 --- a/basic_ui/src/main/java/per/goweii/basic/ui/dialog/PermissionDialog.java +++ b/basic/ui/src/main/java/per/goweii/basic/ui/dialog/PermissionDialog.java @@ -6,6 +6,8 @@ import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; + import per.goweii.anylayer.AnyLayer; import per.goweii.anylayer.Layer; import per.goweii.basic.ui.R; @@ -56,18 +58,18 @@ public PermissionDialog setOnCloseListener(SimpleCallback onCloseListener) public void show() { AnyLayer.dialog(mContext) - .contentView(R.layout.basic_ui_dialog_permission) - .backgroundColorRes(R.color.dialog_bg) - .gravity(Gravity.CENTER) - .cancelableOnTouchOutside(false) - .cancelableOnClickKeyBack(false) - .bindData(new Layer.DataBinder() { + .setContentView(R.layout.basic_ui_dialog_permission) + .setGravity(Gravity.CENTER) + .setBackgroundDimDefault() + .setCancelableOnTouchOutside(false) + .setCancelableOnClickKeyBack(false) + .addOnBindDataListener(new Layer.OnBindDataListener() { @Override - public void bindData(Layer layer) { - ImageView iv = layer.getView(R.id.basic_ui_iv_dialog_permission); - TextView tvTitle = layer.getView(R.id.basic_ui_tv_dialog_permission_title); - TextView tvDescription = layer.getView(R.id.basic_ui_tv_dialog_permission_description); - TextView tvNext = layer.getView(R.id.basic_ui_tv_dialog_permission_next); + public void onBindData(@NonNull Layer layer) { + ImageView iv = layer.requireView(R.id.basic_ui_iv_dialog_permission); + TextView tvTitle = layer.requireView(R.id.basic_ui_tv_dialog_permission_title); + TextView tvDescription = layer.requireView(R.id.basic_ui_tv_dialog_permission_description); + TextView tvNext = layer.requireView(R.id.basic_ui_tv_dialog_permission_next); int iconResId = getIconResId(); if (iconResId > 0) { @@ -85,23 +87,23 @@ public void bindData(Layer layer) { } tvDescription.setText(getDescription()); if (isGoSetting) { - tvNext.setText(R.string.basic_ui_dialog_permission_next_go_setting); + tvNext.setText(R.string.dialog_permission_next_go_setting); } else { - tvNext.setText(R.string.basic_ui_dialog_permission_next); + tvNext.setText(R.string.dialog_permission_next); } } }) - .onClickToDismiss(new Layer.OnClickListener() { + .addOnClickToDismissListener(new Layer.OnClickListener() { @Override - public void onClick(Layer layer, View v) { + public void onClick(@NonNull Layer layer, @NonNull View v) { if (onCloseListener != null) { onCloseListener.onResult(null); } } }, R.id.basic_ui_tv_dialog_permission_close) - .onClickToDismiss(new Layer.OnClickListener() { + .addOnClickToDismissListener(new Layer.OnClickListener() { @Override - public void onClick(Layer layer, View v) { + public void onClick(@NonNull Layer layer, @NonNull View v) { if (onNextListener != null) { onNextListener.onResult(null); } @@ -148,9 +150,9 @@ private int getIconResId() { } return resId; } - - private int getTitleResId(){ - int resId = R.string.basic_ui_dialog_permission_title_unknow; + + private int getTitleResId() { + int resId = R.string.dialog_permission_title_unknown; if (mGroupType == null) { return resId; } @@ -158,31 +160,31 @@ private int getTitleResId(){ default: break; case CALENDAR: - resId = R.string.basic_ui_dialog_permission_title_calendar; + resId = R.string.dialog_permission_title_calendar; break; case CAMERA: - resId = R.string.basic_ui_dialog_permission_title_camera; + resId = R.string.dialog_permission_title_camera; break; case CONTACTS: - resId = R.string.basic_ui_dialog_permission_title_contacts; + resId = R.string.dialog_permission_title_contacts; break; case LOCATION: - resId = R.string.basic_ui_dialog_permission_title_location; + resId = R.string.dialog_permission_title_location; break; case MICROPHONE: - resId = R.string.basic_ui_dialog_permission_title_microphone; + resId = R.string.dialog_permission_title_microphone; break; case PHONE: - resId = R.string.basic_ui_dialog_permission_title_phone; + resId = R.string.dialog_permission_title_phone; break; case SMS: - resId = R.string.basic_ui_dialog_permission_title_sms; + resId = R.string.dialog_permission_title_sms; break; case SENSORS: - resId = R.string.basic_ui_dialog_permission_title_sensors; + resId = R.string.dialog_permission_title_sensors; break; case STORAGE: - resId = R.string.basic_ui_dialog_permission_title_storage; + resId = R.string.dialog_permission_title_storage; break; } return resId; @@ -192,13 +194,13 @@ private String getDescription() { String description; String descriptionRes; if (isGoSetting) { - descriptionRes = ResUtils.getString(R.string.basic_ui_dialog_permission_description_go_setting); + descriptionRes = ResUtils.getString(R.string.dialog_permission_description_go_setting); } else { - descriptionRes = ResUtils.getString(R.string.basic_ui_dialog_permission_description); + descriptionRes = ResUtils.getString(R.string.dialog_permission_description); } int titleResId = getTitleResId(); if (titleResId <= 0) { - description = String.format(descriptionRes, ResUtils.getString(R.string.basic_ui_dialog_permission_description_title_holder)); + description = String.format(descriptionRes, ResUtils.getString(R.string.dialog_permission_description_title_holder)); } else { description = String.format(descriptionRes, ResUtils.getString(titleResId)); } diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/dialog/TipDialog.java b/basic/ui/src/main/java/per/goweii/basic/ui/dialog/TipDialog.java similarity index 69% rename from basic_ui/src/main/java/per/goweii/basic/ui/dialog/TipDialog.java rename to basic/ui/src/main/java/per/goweii/basic/ui/dialog/TipDialog.java index f8f155e1..5a7c67b6 100644 --- a/basic_ui/src/main/java/per/goweii/basic/ui/dialog/TipDialog.java +++ b/basic/ui/src/main/java/per/goweii/basic/ui/dialog/TipDialog.java @@ -1,14 +1,16 @@ package per.goweii.basic.ui.dialog; import android.content.Context; -import android.support.annotation.StringRes; import android.view.Gravity; import android.view.View; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + import per.goweii.anylayer.AnyLayer; -import per.goweii.anylayer.DialogLayer; import per.goweii.anylayer.Layer; +import per.goweii.anylayer.dialog.DialogLayer; import per.goweii.basic.ui.R; import per.goweii.basic.utils.listener.SimpleCallback; @@ -23,6 +25,7 @@ public class TipDialog { private CharSequence msg; private CharSequence yesText; private CharSequence noText; + private boolean msgCenter = true; private boolean singleBtnYes = false; private boolean cancelable = true; private SimpleCallback callbackYes = null; @@ -37,29 +40,29 @@ public static TipDialog with(Context context) { private TipDialog(Context context) { this.context = context; mDialogLayer = AnyLayer.dialog(context); - mDialogLayer.contentView(R.layout.basic_ui_dialog_tip) - .gravity(Gravity.CENTER) - .backgroundColorRes(R.color.dialog_bg) - .cancelableOnTouchOutside(cancelable) - .cancelableOnClickKeyBack(cancelable) - .onVisibleChangeListener(new Layer.OnVisibleChangeListener() { + mDialogLayer.setContentView(R.layout.basic_ui_dialog_tip) + .setGravity(Gravity.CENTER) + .setBackgroundDimDefault() + .setCancelableOnTouchOutside(cancelable) + .setCancelableOnClickKeyBack(cancelable) + .addOnVisibleChangeListener(new Layer.OnVisibleChangedListener() { @Override - public void onShow(Layer layer) { + public void onShow(@NonNull Layer layer) { } @Override - public void onDismiss(Layer layer) { + public void onDismiss(@NonNull Layer layer) { if (onDismissListener != null) { onDismissListener.onResult(null); } } }) - .bindData(new Layer.DataBinder() { + .addOnBindDataListener(new Layer.OnBindDataListener() { @Override - public void bindData(Layer layer) { - TextView tvYes = layer.getView(R.id.basic_ui_tv_dialog_tip_yes); - TextView tvNo = layer.getView(R.id.basic_ui_tv_dialog_tip_no); - View vLine = layer.getView(R.id.basic_ui_v_dialog_tip_line); + public void onBindData(@NonNull Layer layer) { + TextView tvYes = layer.requireView(R.id.basic_ui_tv_dialog_tip_yes); + TextView tvNo = layer.requireView(R.id.basic_ui_tv_dialog_tip_no); + View vLine = layer.requireView(R.id.basic_ui_v_dialog_tip_line); if (singleBtnYes) { tvNo.setVisibility(View.GONE); @@ -70,17 +73,17 @@ public void bindData(Layer layer) { if (noText != null) { tvNo.setText(noText); } else { - tvNo.setText(R.string.basic_ui_dialog_btn_no); + tvNo.setText(R.string.dialog_btn_no); } } if (yesText != null) { tvYes.setText(yesText); } else { - tvYes.setText(R.string.basic_ui_dialog_btn_yes); + tvYes.setText(R.string.dialog_btn_yes); } - TextView tvTitle = layer.getView(R.id.basic_ui_tv_dialog_tip_title); + TextView tvTitle = layer.requireView(R.id.basic_ui_tv_dialog_tip_title); if (title == null) { tvTitle.setVisibility(View.GONE); } else { @@ -88,21 +91,26 @@ public void bindData(Layer layer) { tvTitle.setText(title); } - TextView tvContent = layer.getView(R.id.basic_ui_tv_dialog_tip_content); + TextView tvContent = layer.requireView(R.id.basic_ui_tv_dialog_tip_content); + if (msgCenter) { + tvContent.setGravity(Gravity.CENTER); + } else { + tvContent.setGravity(Gravity.START); + } tvContent.setText(msg); } }) - .onClickToDismiss(new Layer.OnClickListener() { + .addOnClickToDismissListener(new Layer.OnClickListener() { @Override - public void onClick(Layer layer, View v) { + public void onClick(@NonNull Layer layer, @NonNull View v) { if (callbackYes != null) { callbackYes.onResult(null); } } }, R.id.basic_ui_tv_dialog_tip_yes) - .onClickToDismiss(new Layer.OnClickListener() { + .addOnClickToDismissListener(new Layer.OnClickListener() { @Override - public void onClick(Layer layer, View v) { + public void onClick(@NonNull Layer layer, @NonNull View v) { if (callbackNo != null) { callbackNo.onResult(null); } @@ -160,6 +168,11 @@ public TipDialog cancelable(boolean cancelable) { return this; } + public TipDialog msgCenter(boolean msgCenter) { + this.msgCenter = msgCenter; + return this; + } + public TipDialog onYes(SimpleCallback callback) { callbackYes = callback; return this; diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/dialog/UpdateDialog.java b/basic/ui/src/main/java/per/goweii/basic/ui/dialog/UpdateDialog.java similarity index 62% rename from basic_ui/src/main/java/per/goweii/basic/ui/dialog/UpdateDialog.java rename to basic/ui/src/main/java/per/goweii/basic/ui/dialog/UpdateDialog.java index e978c35f..20c72f10 100644 --- a/basic_ui/src/main/java/per/goweii/basic/ui/dialog/UpdateDialog.java +++ b/basic/ui/src/main/java/per/goweii/basic/ui/dialog/UpdateDialog.java @@ -2,14 +2,16 @@ import android.content.Context; import android.text.TextUtils; -import android.text.method.ScrollingMovementMethod; import android.view.Gravity; import android.view.View; import android.widget.TextView; +import androidx.annotation.NonNull; + import per.goweii.anylayer.AnyLayer; import per.goweii.anylayer.Layer; import per.goweii.basic.ui.R; +import per.goweii.basic.utils.ResUtils; /** * 版本更新弹窗 @@ -21,6 +23,7 @@ public class UpdateDialog { private final Context mContext; + private boolean mTest = false; private String mUrl = null; private String mUrlBackup = null; private int mVersionCode = 0; @@ -40,6 +43,11 @@ private UpdateDialog(Context context) { this.mContext = context; } + public UpdateDialog setTest(boolean test) { + mTest = test; + return this; + } + public UpdateDialog setUrl(String url) { mUrl = url; return this; @@ -87,20 +95,27 @@ public UpdateDialog setOnDismissListener(OnDismissListener onDismissListener) { public void show() { AnyLayer.dialog(mContext) - .contentView(R.layout.basic_ui_dialog_update) - .backgroundColorRes(R.color.dialog_bg) - .gravity(Gravity.CENTER) - .cancelableOnTouchOutside(!mForce) - .cancelableOnClickKeyBack(!mForce) - .bindData(new Layer.DataBinder() { + .setContentView(R.layout.basic_ui_dialog_update) + .setBackgroundDimDefault() + .setGravity(Gravity.CENTER) + .setCancelableOnTouchOutside(!mForce) + .setCancelableOnClickKeyBack(!mForce) + .addOnBindDataListener(new Layer.OnBindDataListener() { @Override - public void bindData(Layer layer) { - final View vLine = layer.getView(R.id.basic_ui_v_dialog_update_line); - final TextView tvNo = layer.getView(R.id.basic_ui_tv_dialog_update_no); - final TextView tvVersionName = layer.getView(R.id.basic_ui_tv_dialog_update_version_name); - final TextView tvTime = layer.getView(R.id.basic_ui_tv_dialog_update_time); - final TextView tvDescription = layer.getView(R.id.basic_ui_tv_dialog_update_description); - + public void onBindData(@NonNull Layer layer) { + final TextView tvTitle = layer.requireView(R.id.basic_ui_tv_dialog_update_title); + final View vLine = layer.requireView(R.id.basic_ui_v_dialog_update_line); + final TextView tvNo = layer.requireView(R.id.basic_ui_tv_dialog_update_no); + final TextView tvVersionName = layer.requireView(R.id.basic_ui_tv_dialog_update_version_name); + final TextView tvTime = layer.requireView(R.id.basic_ui_tv_dialog_update_time); + final TextView tvDescription = layer.requireView(R.id.basic_ui_tv_dialog_update_description); + if (mTest) { + tvTitle.setTextColor(ResUtils.getThemeColor(tvTitle.getContext(), R.attr.colorTextAccent)); + tvTitle.setText(R.string.dialog_update_test_title); + } else { + tvTitle.setTextColor(ResUtils.getThemeColor(tvTitle.getContext(), R.attr.colorTextMain)); + tvTitle.setText(R.string.dialog_update_title); + } if (TextUtils.isEmpty(mVersionName)) { tvVersionName.setVisibility(View.GONE); } else { @@ -114,8 +129,7 @@ public void bindData(Layer layer) { tvTime.setText(mTime); } tvDescription.setText(mDescription); - tvDescription.setMovementMethod(ScrollingMovementMethod.getInstance()); - + //tvDescription.setMovementMethod(ScrollingMovementMethod.getInstance()); if (mForce) { tvNo.setVisibility(View.GONE); vLine.setVisibility(View.GONE); @@ -125,30 +139,30 @@ public void bindData(Layer layer) { } } }) - .onClickToDismiss(new Layer.OnClickListener() { + .addOnClickToDismissListener(new Layer.OnClickListener() { @Override - public void onClick(Layer layer, View v) { + public void onClick(@NonNull Layer layer, @NonNull View v) { if (mOnUpdateListener != null) { mOnUpdateListener.onDownload(mUrl, mUrlBackup, mForce); } } }, R.id.basic_ui_tv_dialog_update_yes) - .onClickToDismiss(new Layer.OnClickListener() { + .addOnClickToDismissListener(new Layer.OnClickListener() { @Override - public void onClick(Layer layer, View v) { + public void onClick(@NonNull Layer layer, @NonNull View v) { if (mOnUpdateListener != null) { - mOnUpdateListener.onIgnore(mVersionCode); + mOnUpdateListener.onIgnore(mVersionName, mVersionCode); } } }, R.id.basic_ui_tv_dialog_update_no) - .onDismissListener(new Layer.OnDismissListener() { + .addOnDismissListener(new Layer.OnDismissListener() { @Override - public void onDismissing(Layer layer) { + public void onPreDismiss(@NonNull Layer layer) { } @Override - public void onDismissed(Layer layer) { + public void onPostDismiss(@NonNull Layer layer) { if (mOnDismissListener != null) { mOnDismissListener.onDismiss(); } @@ -159,7 +173,8 @@ public void onDismissed(Layer layer) { public interface OnUpdateListener { void onDownload(String url, String urlBackup, boolean isForce); - void onIgnore(int versionCode); + + void onIgnore(String versionName, int versionCode); } public interface OnDismissListener { diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/indicator/MoveCircleNavigator.java b/basic/ui/src/main/java/per/goweii/basic/ui/indicator/MoveCircleNavigator.java similarity index 98% rename from basic_ui/src/main/java/per/goweii/basic/ui/indicator/MoveCircleNavigator.java rename to basic/ui/src/main/java/per/goweii/basic/ui/indicator/MoveCircleNavigator.java index 0173b6d2..e5b8acc4 100644 --- a/basic_ui/src/main/java/per/goweii/basic/ui/indicator/MoveCircleNavigator.java +++ b/basic/ui/src/main/java/per/goweii/basic/ui/indicator/MoveCircleNavigator.java @@ -57,8 +57,8 @@ private void init(Context context) { mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mRadius = UIUtil.dip2px(context, 3); mCircleSpacing = UIUtil.dip2px(context, 12); - mColorNormal = ResUtils.getColor(context, R.color.invert); - mColorSelected = ResUtils.getColor(context, R.color.main); + mColorNormal = ResUtils.getThemeColor(context, R.attr.colorInvert); + mColorSelected = ResUtils.getThemeColor(context, R.attr.colorMain); } @Override diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/indicator/ScaleCircleNavigator.java b/basic/ui/src/main/java/per/goweii/basic/ui/indicator/ScaleCircleNavigator.java similarity index 100% rename from basic_ui/src/main/java/per/goweii/basic/ui/indicator/ScaleCircleNavigator.java rename to basic/ui/src/main/java/per/goweii/basic/ui/indicator/ScaleCircleNavigator.java diff --git a/basic/ui/src/main/java/per/goweii/basic/ui/toast/ToastMaker.java b/basic/ui/src/main/java/per/goweii/basic/ui/toast/ToastMaker.java new file mode 100644 index 00000000..1f968a5d --- /dev/null +++ b/basic/ui/src/main/java/per/goweii/basic/ui/toast/ToastMaker.java @@ -0,0 +1,143 @@ +package per.goweii.basic.ui.toast; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.text.TextUtils; +import android.view.Gravity; +import android.view.View; +import android.view.WindowManager; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + +import java.lang.reflect.Field; + +import per.goweii.anylayer.AnyLayer; +import per.goweii.anylayer.Layer; +import per.goweii.basic.ui.R; + +/** + * Toast的工具类 + * 防止重复点击队列形式出现长时间不消失 + * 链式调用,方便定制 + * + * @author Cuizhen + * @version v1.0.0 + * @date 2018/3/10-下午2:09 + */ +@SuppressLint("StaticFieldLeak") +public class ToastMaker { + private static Context sContext = null; + private static Handler sHandler = null; + private static Toast sToast = null; + + @SuppressLint("ShowToast") + public static void init(Context appContext) { + sContext = appContext.getApplicationContext(); + sHandler = new Handler(appContext.getMainLooper()) { + @Override + public void handleMessage(@NonNull Message msg) { + sToast.show(); + } + }; + } + + @Deprecated + public static void showShort(@StringRes int message) { + showShort(sContext.getString(message)); + } + + public static void showShort(CharSequence message) { + if (TextUtils.isEmpty(message)) return; + AnyLayer.toast() + .setMessage(message) + .setRemoveOthers(true) + .setDuration(2000) + .setGravity(Gravity.CENTER) + .setContentView(R.layout.basic_ui_toast) + .addOnBindDataListener(new Layer.OnBindDataListener() { + @Override + public void onBindData(@NonNull Layer layer) { + TextView textView = layer.requireView(R.id.basic_ui_toast_msg); + textView.setText(message); + } + }) + .show(); + } + + @SuppressLint("ShowToast") + public static ToastMaker make(CharSequence message) { + if (sToast == null) { + sToast = Toast.makeText(sContext, "", Toast.LENGTH_SHORT); + } + sToast.setText(message); + return Holder.INSTANCE; + } + + public ToastMaker duration(int duration) { + sToast.setDuration(duration); + return this; + } + + @Deprecated + public ToastMaker view(View view) { + sToast.setView(view); + return this; + } + + public ToastMaker text(String text) { + sToast.setText(text); + return this; + } + + public ToastMaker gravity(int gravity, int xOffset, int yOffset) { + sToast.setGravity(gravity, xOffset, yOffset); + return this; + } + + public ToastMaker margin(float horizontalMargin, float verticalMargin) { + sToast.setMargin(horizontalMargin, verticalMargin); + return this; + } + + public ToastMaker animation(int animResId) { + if (animResId != 0) { + initAnim(animResId); + } + return this; + } + + public void show() { + if (Thread.currentThread() != Looper.getMainLooper().getThread()) { + sHandler.sendEmptyMessage(0); + } else { + sToast.show(); + } + } + + @SuppressWarnings("ConstantConditions") + private static void initAnim(int animResId) { + try { + Field mTNField = sToast.getClass().getDeclaredField("mTN"); + mTNField.setAccessible(true); + Object mTN = mTNField.get(sToast); + Field tnParamsField = mTN.getClass().getDeclaredField("mParams"); + tnParamsField.setAccessible(true); + WindowManager.LayoutParams params = (WindowManager.LayoutParams) tnParamsField.get(mTN); + params.windowAnimations = animResId; + Field tnNextViewField = mTN.getClass().getDeclaredField("mNextView"); + tnNextViewField.setAccessible(true); + tnNextViewField.set(mTN, sToast.getView()); + } catch (Exception ignore) { + } + } + + private static class Holder { + private static final ToastMaker INSTANCE = new ToastMaker(); + } +} diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/widget/MultiTouchViewPager.java b/basic/ui/src/main/java/per/goweii/basic/ui/widget/MultiTouchViewPager.java similarity index 90% rename from basic_ui/src/main/java/per/goweii/basic/ui/widget/MultiTouchViewPager.java rename to basic/ui/src/main/java/per/goweii/basic/ui/widget/MultiTouchViewPager.java index 31801582..d71f3b09 100644 --- a/basic_ui/src/main/java/per/goweii/basic/ui/widget/MultiTouchViewPager.java +++ b/basic/ui/src/main/java/per/goweii/basic/ui/widget/MultiTouchViewPager.java @@ -2,12 +2,13 @@ import android.annotation.SuppressLint; import android.content.Context; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager.widget.ViewPager; + /** * 修复多点触控时抛出异常的问题 * java.lang.IllegalArgumentException: pointerIndex out of range diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/widget/NoScrollViewPager.java b/basic/ui/src/main/java/per/goweii/basic/ui/widget/NoScrollViewPager.java similarity index 96% rename from basic_ui/src/main/java/per/goweii/basic/ui/widget/NoScrollViewPager.java rename to basic/ui/src/main/java/per/goweii/basic/ui/widget/NoScrollViewPager.java index 8a008d39..b373e159 100644 --- a/basic_ui/src/main/java/per/goweii/basic/ui/widget/NoScrollViewPager.java +++ b/basic/ui/src/main/java/per/goweii/basic/ui/widget/NoScrollViewPager.java @@ -2,10 +2,11 @@ import android.annotation.SuppressLint; import android.content.Context; -import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; +import androidx.viewpager.widget.ViewPager; + /** * 禁止ViewPager的滑动 */ diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/widget/SignatureEditText.java b/basic/ui/src/main/java/per/goweii/basic/ui/widget/SignatureEditText.java similarity index 97% rename from basic_ui/src/main/java/per/goweii/basic/ui/widget/SignatureEditText.java rename to basic/ui/src/main/java/per/goweii/basic/ui/widget/SignatureEditText.java index d40188c7..d72d5739 100644 --- a/basic_ui/src/main/java/per/goweii/basic/ui/widget/SignatureEditText.java +++ b/basic/ui/src/main/java/per/goweii/basic/ui/widget/SignatureEditText.java @@ -1,14 +1,15 @@ package per.goweii.basic.ui.widget; import android.content.Context; -import android.support.annotation.IntRange; -import android.support.v7.widget.AppCompatEditText; import android.text.Editable; import android.text.InputFilter; import android.text.Spanned; import android.text.TextWatcher; import android.util.AttributeSet; +import androidx.annotation.IntRange; +import androidx.appcompat.widget.AppCompatEditText; + /** * 自定义EditText * 最大字符个数限制 中文一个算2个 diff --git a/basic/ui/src/main/java/per/goweii/basic/ui/widget/StatusBarView.java b/basic/ui/src/main/java/per/goweii/basic/ui/widget/StatusBarView.java new file mode 100644 index 00000000..9dd63290 --- /dev/null +++ b/basic/ui/src/main/java/per/goweii/basic/ui/widget/StatusBarView.java @@ -0,0 +1,108 @@ +package per.goweii.basic.ui.widget; + +import android.content.Context; +import android.content.res.TypedArray; +import android.util.AttributeSet; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import per.goweii.basic.ui.R; +import per.goweii.statusbarcompat.StatusBarCompat; + +/** + * @author CuiZhen + * @date 2019/8/25 + * GitHub: https://github.com/goweii + */ +public class StatusBarView extends View { + private IconMode iconMode; + private Transparent transparent; + + public StatusBarView(Context context) { + this(context, null); + } + + public StatusBarView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public StatusBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.StatusBarView); + int iconModeInt = typedArray.getInt(R.styleable.StatusBarView_sbv_iconMode, 0); + int transparentInt = typedArray.getInt(R.styleable.StatusBarView_sbv_transparent, 0); + iconMode = IconMode.values()[iconModeInt]; + transparent = Transparent.values()[transparentInt]; + typedArray.recycle(); + refreshIconMode(); + refreshTransparent(); + } + + public void setIconMode(@NonNull IconMode iconMode) { + this.iconMode = iconMode; + refreshIconMode(); + } + + public void setTransparent(@NonNull Transparent transparent) { + this.transparent = transparent; + refreshTransparent(); + } + + public void refreshIconMode() { + switch (iconMode) { + case UNCHANGED: + break; + case DARK_ICON: + StatusBarCompat.setIconDark(getContext()); + break; + case LIGHT_ICON: + StatusBarCompat.setIconLight(getContext()); + break; + } + } + + public void refreshTransparent() { + switch (transparent) { + case UNCHANGED: + break; + case TRANSPARENT: + StatusBarCompat.transparent(getContext()); + break; + case UNTRANSPARENT: + StatusBarCompat.unTransparent(getContext()); + break; + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + setMeasuredDimension( + MeasureSpec.getSize(widthMeasureSpec), + getStatusBarHeight() + getPaddingTop() + getPaddingBottom()); + } + + public int getStatusBarHeight() { + int resourceId = getContext().getResources().getIdentifier("status_bar_height", "dimen", "android"); + if (resourceId > 0) { + return getContext().getResources().getDimensionPixelSize(resourceId); + } + return 0; + } + + public enum Transparent { + UNCHANGED, + TRANSPARENT, + UNTRANSPARENT, + ; + } + + public enum IconMode { + UNCHANGED, + DARK_ICON, + LIGHT_ICON, + ; + } +} diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/widget/UserNameEditText.java b/basic/ui/src/main/java/per/goweii/basic/ui/widget/UserNameEditText.java similarity index 98% rename from basic_ui/src/main/java/per/goweii/basic/ui/widget/UserNameEditText.java rename to basic/ui/src/main/java/per/goweii/basic/ui/widget/UserNameEditText.java index 231af1fb..44059802 100644 --- a/basic_ui/src/main/java/per/goweii/basic/ui/widget/UserNameEditText.java +++ b/basic/ui/src/main/java/per/goweii/basic/ui/widget/UserNameEditText.java @@ -1,8 +1,6 @@ package per.goweii.basic.ui.widget; import android.content.Context; -import android.support.annotation.IntRange; -import android.support.v7.widget.AppCompatEditText; import android.text.Editable; import android.text.InputFilter; import android.text.Spanned; @@ -10,6 +8,9 @@ import android.text.TextWatcher; import android.util.AttributeSet; +import androidx.annotation.IntRange; +import androidx.appcompat.widget.AppCompatEditText; + import java.util.regex.Matcher; import java.util.regex.Pattern; diff --git a/basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_calendar.png b/basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_calendar.png similarity index 100% rename from basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_calendar.png rename to basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_calendar.png diff --git a/basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_camera.png b/basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_camera.png similarity index 100% rename from basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_camera.png rename to basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_camera.png diff --git a/basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_contacts.png b/basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_contacts.png similarity index 100% rename from basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_contacts.png rename to basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_contacts.png diff --git a/basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_location.png b/basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_location.png similarity index 100% rename from basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_location.png rename to basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_location.png diff --git a/basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_microphone.png b/basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_microphone.png similarity index 100% rename from basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_microphone.png rename to basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_microphone.png diff --git a/basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_phone.png b/basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_phone.png similarity index 100% rename from basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_phone.png rename to basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_phone.png diff --git a/basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_sensors.png b/basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_sensors.png similarity index 100% rename from basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_sensors.png rename to basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_sensors.png diff --git a/basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_sms.png b/basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_sms.png similarity index 100% rename from basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_sms.png rename to basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_sms.png diff --git a/basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_storage.png b/basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_storage.png similarity index 100% rename from basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_storage.png rename to basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_storage.png diff --git a/basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_unknow.png b/basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_unknow.png similarity index 100% rename from basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_unknow.png rename to basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_permission_unknow.png diff --git a/basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_update.png b/basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_update.png similarity index 100% rename from basic_ui/src/main/res/drawable-xhdpi/basic_ui_dialog_update.png rename to basic/ui/src/main/res/drawable-xhdpi/basic_ui_dialog_update.png diff --git a/basic_ui/src/main/res/drawable/basic_ui_dialog_download_progress_bg.xml b/basic/ui/src/main/res/drawable/basic_ui_dialog_download_progress_bg.xml similarity index 56% rename from basic_ui/src/main/res/drawable/basic_ui_dialog_download_progress_bg.xml rename to basic/ui/src/main/res/drawable/basic_ui_dialog_download_progress_bg.xml index 87d958f4..54510592 100644 --- a/basic_ui/src/main/res/drawable/basic_ui_dialog_download_progress_bg.xml +++ b/basic/ui/src/main/res/drawable/basic_ui_dialog_download_progress_bg.xml @@ -5,12 +5,11 @@ + android:endColor="?colorLine" + android:angle="270" /> @@ -19,12 +18,11 @@ + android:endColor="?colorLine" + android:angle="270" /> @@ -34,12 +32,11 @@ + android:endColor="?colorMain" + android:angle="270" /> diff --git a/basic/ui/src/main/res/drawable/basic_ui_dialog_loading_bg.xml b/basic/ui/src/main/res/drawable/basic_ui_dialog_loading_bg.xml new file mode 100644 index 00000000..8f1db57c --- /dev/null +++ b/basic/ui/src/main/res/drawable/basic_ui_dialog_loading_bg.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/basic_ui/src/main/res/drawable/basic_ui_progressbar.xml b/basic/ui/src/main/res/drawable/basic_ui_progressbar.xml similarity index 77% rename from basic_ui/src/main/res/drawable/basic_ui_progressbar.xml rename to basic/ui/src/main/res/drawable/basic_ui_progressbar.xml index 9e3022e1..7a8116da 100644 --- a/basic_ui/src/main/res/drawable/basic_ui_progressbar.xml +++ b/basic/ui/src/main/res/drawable/basic_ui_progressbar.xml @@ -7,8 +7,8 @@ + android:endColor="@android:color/transparent" + android:startColor="@android:color/transparent" /> @@ -19,8 +19,8 @@ + android:endColor="?colorThird" + android:startColor="?colorThird" /> @@ -33,8 +33,8 @@ + android:endColor="?colorMain" + android:startColor="?colorMain" /> diff --git a/basic/ui/src/main/res/drawable/basic_ui_progressbar_circle.xml b/basic/ui/src/main/res/drawable/basic_ui_progressbar_circle.xml new file mode 100644 index 00000000..04ddcb00 --- /dev/null +++ b/basic/ui/src/main/res/drawable/basic_ui_progressbar_circle.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic_ui/src/main/res/layout/basic_ui_dialog_download.xml b/basic/ui/src/main/res/layout/basic_ui_dialog_download.xml similarity index 83% rename from basic_ui/src/main/res/layout/basic_ui_dialog_download.xml rename to basic/ui/src/main/res/layout/basic_ui_dialog_download.xml index 40149e85..d044cc0f 100644 --- a/basic_ui/src/main/res/layout/basic_ui_dialog_download.xml +++ b/basic/ui/src/main/res/layout/basic_ui_dialog_download.xml @@ -6,17 +6,17 @@ android:layout_margin="@dimen/margin_big" android:background="@drawable/bg_solid_round_color_surface_radius_def" android:orientation="vertical" - android:paddingLeft="@dimen/margin_def" + android:paddingStart="@dimen/margin_def" android:paddingTop="@dimen/margin_def" - android:paddingRight="@dimen/margin_def" + android:paddingEnd="@dimen/margin_def" android:paddingBottom="@dimen/margin_def"> @@ -71,15 +71,15 @@ diff --git a/basic_ui/src/main/res/layout/basic_ui_dialog_list.xml b/basic/ui/src/main/res/layout/basic_ui_dialog_list.xml similarity index 75% rename from basic_ui/src/main/res/layout/basic_ui_dialog_list.xml rename to basic/ui/src/main/res/layout/basic_ui_dialog_list.xml index 137f2488..ae4f6cb2 100644 --- a/basic_ui/src/main/res/layout/basic_ui_dialog_list.xml +++ b/basic/ui/src/main/res/layout/basic_ui_dialog_list.xml @@ -12,11 +12,17 @@ android:layout_marginTop="@dimen/round_radius" android:layout_height="@dimen/button_height" android:gravity="center" - android:textColor="@color/text_surface" + android:textColor="?colorTextSurface" android:textSize="@dimen/text_title" - tools:text="提示" /> + tools:text="@string/tips" /> - + + + android:layout_height="@dimen/line_thin" + android:background="?colorLine" /> + android:background="?colorLine" /> diff --git a/basic_ui/src/main/res/layout/basic_ui_dialog_loading.xml b/basic/ui/src/main/res/layout/basic_ui_dialog_loading.xml similarity index 89% rename from basic_ui/src/main/res/layout/basic_ui_dialog_loading.xml rename to basic/ui/src/main/res/layout/basic_ui_dialog_loading.xml index 156b542b..27f8992e 100644 --- a/basic_ui/src/main/res/layout/basic_ui_dialog_loading.xml +++ b/basic/ui/src/main/res/layout/basic_ui_dialog_loading.xml @@ -11,7 +11,7 @@ android:layout_width="@dimen/button_height" android:layout_height="@dimen/button_height" android:layout_gravity="center" - android:indeterminateTint="@color/basic_ui_dialog_loading_circle" + android:indeterminateTint="?colorMain" android:indeterminateTintMode="src_atop" /> \ No newline at end of file diff --git a/basic_ui/src/main/res/layout/basic_ui_dialog_permission.xml b/basic/ui/src/main/res/layout/basic_ui_dialog_permission.xml similarity index 71% rename from basic_ui/src/main/res/layout/basic_ui_dialog_permission.xml rename to basic/ui/src/main/res/layout/basic_ui_dialog_permission.xml index 57671b90..0e8c4bca 100644 --- a/basic_ui/src/main/res/layout/basic_ui_dialog_permission.xml +++ b/basic/ui/src/main/res/layout/basic_ui_dialog_permission.xml @@ -15,7 +15,7 @@ android:layout_marginTop="@dimen/margin_def" android:layout_marginBottom="@dimen/margin_def" android:scaleType="fitCenter" - android:tint="@color/text_surface" + android:tint="?colorIconSurface" tools:src="@drawable/basic_ui_dialog_permission_camera" /> + tools:text="@string/dialog_permission_title_camera" /> + tools:text="@string/dialog_permission_description" /> + android:text="@string/dialog_permission_next" + android:background="@drawable/bg_press_color_main_radius_max" /> + android:text="@string/dialog_permission_close" /> \ No newline at end of file diff --git a/basic_ui/src/main/res/layout/basic_ui_dialog_tip.xml b/basic/ui/src/main/res/layout/basic_ui_dialog_tip.xml similarity index 74% rename from basic_ui/src/main/res/layout/basic_ui_dialog_tip.xml rename to basic/ui/src/main/res/layout/basic_ui_dialog_tip.xml index 97a9bf25..91684423 100644 --- a/basic_ui/src/main/res/layout/basic_ui_dialog_tip.xml +++ b/basic/ui/src/main/res/layout/basic_ui_dialog_tip.xml @@ -16,27 +16,27 @@ android:gravity="center" android:visibility="gone" tools:visibility="visible" - android:textColor="@color/text_surface" + android:textColor="?colorTextSurface" android:textSize="@dimen/text_title" - tools:text="提示" /> + tools:text="@string/tips" /> + tools:text="@tools:sample/lorem/random" /> + android:layout_height="@dimen/line_thin" + android:background="?colorLine" /> + android:background="?colorLine" /> diff --git a/basic_ui/src/main/res/layout/basic_ui_dialog_update.xml b/basic/ui/src/main/res/layout/basic_ui_dialog_update.xml similarity index 50% rename from basic_ui/src/main/res/layout/basic_ui_dialog_update.xml rename to basic/ui/src/main/res/layout/basic_ui_dialog_update.xml index 8355219f..05155ac8 100644 --- a/basic_ui/src/main/res/layout/basic_ui_dialog_update.xml +++ b/basic/ui/src/main/res/layout/basic_ui_dialog_update.xml @@ -1,26 +1,26 @@ + android:layout_gravity="center"> + android:orientation="vertical" + android:paddingTop="80dp"> @@ -37,38 +37,65 @@ android:id="@+id/basic_ui_tv_dialog_update_time" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/margin_def" - android:layout_marginTop="10dp" + android:layout_marginStart="@dimen/margin_def" + android:layout_marginTop="@dimen/margin_thin" android:gravity="center" - android:textColor="@color/text_second" + android:textColor="?colorTextSecond" android:textSize="@dimen/text_auxiliary" tools:text="2019-03-15" /> - + android:layout_marginStart="@dimen/margin_def" + android:layout_marginEnd="@dimen/margin_def" + android:maxHeight="145dp"> + + + + + + + + + + + + + android:layout_height="wrap_content"> + android:layout_height="@dimen/line_thin" + android:background="?attr/colorLine" /> + android:background="?attr/colorLine" /> diff --git a/basic_ui/src/main/res/layout/basic_ui_loading_bar.xml b/basic/ui/src/main/res/layout/basic_ui_loading_bar.xml similarity index 85% rename from basic_ui/src/main/res/layout/basic_ui_loading_bar.xml rename to basic/ui/src/main/res/layout/basic_ui_loading_bar.xml index 20f4b2cc..6607e062 100644 --- a/basic_ui/src/main/res/layout/basic_ui_loading_bar.xml +++ b/basic/ui/src/main/res/layout/basic_ui_loading_bar.xml @@ -13,10 +13,10 @@ android:layout_height="@dimen/basic_ui_action_bar_loading_bar_height" android:layout_gravity="bottom" android:indeterminate="true" - android:background="@color/transparent" + android:background="?colorTransparent" app:mpb_showProgressBackground="false" - app:mpb_progressTint="@color/assist" - app:mpb_indeterminateTint="@color/assist" + app:mpb_progressTint="?colorAssist" + app:mpb_indeterminateTint="?colorAssist" app:mpb_progressStyle="horizontal" app:mpb_useIntrinsicPadding="false" /> diff --git a/basic_ui/src/main/res/layout/basic_ui_progress_bar.xml b/basic/ui/src/main/res/layout/basic_ui_progress_bar.xml similarity index 82% rename from basic_ui/src/main/res/layout/basic_ui_progress_bar.xml rename to basic/ui/src/main/res/layout/basic_ui_progress_bar.xml index 5b4c1bf1..4d1cd8e6 100644 --- a/basic_ui/src/main/res/layout/basic_ui_progress_bar.xml +++ b/basic/ui/src/main/res/layout/basic_ui_progress_bar.xml @@ -7,10 +7,10 @@ android:layout_height="@dimen/basic_ui_action_bar_loading_bar_height" android:layout_gravity="top" android:indeterminate="false" - android:background="@color/transparent" + android:background="?colorTransparent" app:mpb_showProgressBackground="false" tools:progress="50" - app:mpb_progressTint="@color/assist" - app:mpb_indeterminateTint="@color/assist" + app:mpb_progressTint="?colorAssist" + app:mpb_indeterminateTint="?colorAssist" app:mpb_progressStyle="horizontal" app:mpb_useIntrinsicPadding="false" /> \ No newline at end of file diff --git a/basic_ui/src/main/res/layout/basic_ui_rv_item_dialog_list.xml b/basic/ui/src/main/res/layout/basic_ui_rv_item_dialog_list.xml similarity index 87% rename from basic_ui/src/main/res/layout/basic_ui_rv_item_dialog_list.xml rename to basic/ui/src/main/res/layout/basic_ui_rv_item_dialog_list.xml index fd872479..824cd6d8 100644 --- a/basic_ui/src/main/res/layout/basic_ui_rv_item_dialog_list.xml +++ b/basic/ui/src/main/res/layout/basic_ui_rv_item_dialog_list.xml @@ -12,7 +12,7 @@ android:layout_height="@dimen/button_height" android:gravity="center" android:textSize="@dimen/text_content" - android:textColor="@color/text_surface" - tools:text="列表条目"/> + android:textColor="?colorTextSurface" + tools:text="List entries"/> \ No newline at end of file diff --git a/basic/ui/src/main/res/layout/basic_ui_toast.xml b/basic/ui/src/main/res/layout/basic_ui_toast.xml new file mode 100644 index 00000000..d5e66a22 --- /dev/null +++ b/basic/ui/src/main/res/layout/basic_ui_toast.xml @@ -0,0 +1,29 @@ + + + + + + \ No newline at end of file diff --git a/basic/ui/src/main/res/values/attrs.xml b/basic/ui/src/main/res/values/attrs.xml new file mode 100644 index 00000000..caeafa27 --- /dev/null +++ b/basic/ui/src/main/res/values/attrs.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/basic/ui/src/main/res/values/dimens.xml b/basic/ui/src/main/res/values/dimens.xml new file mode 100644 index 00000000..5354a43f --- /dev/null +++ b/basic/ui/src/main/res/values/dimens.xml @@ -0,0 +1,6 @@ + + + 2dp + 13dp + 3dp + \ No newline at end of file diff --git a/basic/ui/src/main/res/values/strings.xml b/basic/ui/src/main/res/values/strings.xml new file mode 100644 index 00000000..85420055 --- /dev/null +++ b/basic/ui/src/main/res/values/strings.xml @@ -0,0 +1,2 @@ + + diff --git a/basic_ui/src/main/res/values/styles.xml b/basic/ui/src/main/res/values/styles.xml similarity index 68% rename from basic_ui/src/main/res/values/styles.xml rename to basic/ui/src/main/res/values/styles.xml index 1fae0760..d2589355 100644 --- a/basic_ui/src/main/res/values/styles.xml +++ b/basic/ui/src/main/res/values/styles.xml @@ -4,11 +4,12 @@ 1dp 5dp false - @color/assist - @color/assist + ?colorAssist + ?colorAssist horizontal false - \ No newline at end of file diff --git a/basic/utils/build.gradle b/basic/utils/build.gradle new file mode 100644 index 00000000..dc0339e3 --- /dev/null +++ b/basic/utils/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.library' +apply from: "${rootDir.path}/gradle/basic.gradle" + +android { + namespace "per.goweii.basic.utils" +} + +dependencies { + //debugImplementation rootProject.ext.dependencies.ponyo + api rootProject.ext.dependencies.ponyoLog + api rootProject.ext.dependencies.ponyoCrash +} diff --git a/basic_ui/proguard-rules.pro b/basic/utils/proguard-rules.pro similarity index 100% rename from basic_ui/proguard-rules.pro rename to basic/utils/proguard-rules.pro diff --git a/basic/utils/src/main/AndroidManifest.xml b/basic/utils/src/main/AndroidManifest.xml new file mode 100644 index 00000000..0bfa73ba --- /dev/null +++ b/basic/utils/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/AnimatorUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/AnimatorUtils.java similarity index 100% rename from basic_utils/src/main/java/per/goweii/basic/utils/AnimatorUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/AnimatorUtils.java diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/AppInfoUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/AppInfoUtils.java similarity index 80% rename from basic_utils/src/main/java/per/goweii/basic/utils/AppInfoUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/AppInfoUtils.java index 7c4ecabf..d2c8d2ee 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/AppInfoUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/AppInfoUtils.java @@ -7,13 +7,22 @@ import android.text.TextUtils; /** - * 描述:敏感词过滤 - * * @author Cuizhen * @date 2018/8/28-上午10:04 */ public class AppInfoUtils { + public static String getAppName() { + String appName = ""; + try { + PackageInfo packageInfo = Utils.getAppContext().getApplicationContext().getPackageManager().getPackageInfo(Utils.getAppContext().getPackageName(), 0); + appName = Utils.getAppContext().getString(packageInfo.applicationInfo.labelRes); + } catch (PackageManager.NameNotFoundException e) { + e.printStackTrace(); + } + return appName; + } + /** * 获取本地软件版本号code */ diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/AppOpenUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/AppOpenUtils.java similarity index 50% rename from basic_utils/src/main/java/per/goweii/basic/utils/AppOpenUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/AppOpenUtils.java index 6b52424f..ed54d59e 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/AppOpenUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/AppOpenUtils.java @@ -1,6 +1,5 @@ package per.goweii.basic.utils; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -8,8 +7,6 @@ /** * @author CuiZhen * @date 2019/11/3 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class AppOpenUtils { @@ -22,6 +19,7 @@ public class AppOpenUtils { public static boolean openQQChat(Context context, String qq) { String url = "mqqwpa://im/chat?chat_type=wpa&uin=" + qq; Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); try { context.startActivity(intent); return true; @@ -68,14 +66,60 @@ public static boolean joinQQGroup(Context context, String key) { } public static boolean openWechat(Context context) { - Intent intent = new Intent(); - ComponentName cmp = new ComponentName(AppInfoUtils.PackageName.WECHAT, - AppInfoUtils.PackageName.WECHAT + ".ui.LauncherUI"); - intent.setAction(Intent.ACTION_MAIN); - intent.addCategory(Intent.CATEGORY_LAUNCHER); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.setComponent(cmp); try { + Intent intent = context.getPackageManager().getLaunchIntentForPackage(AppInfoUtils.PackageName.WECHAT); + if (intent == null) return false; + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setAction("android.intent.action.VIEW"); + context.startActivity(intent); + return true; + } catch (Exception e) { + return false; + } + } + + public static boolean openWechatQRCode(Context context) { + try { + Intent intent = context.getPackageManager().getLaunchIntentForPackage(AppInfoUtils.PackageName.WECHAT); + if (intent == null) return false; + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setAction("android.intent.action.VIEW"); + intent.putExtra("LauncherUI.From.Scaner.Shortcut", true); + context.startActivity(intent); + return true; + } catch (Exception e) { + return false; + } + } + + public static boolean openQQ(Context context) { + try { + Intent intent = context.getPackageManager().getLaunchIntentForPackage(AppInfoUtils.PackageName.QQ); + if (intent == null) return false; + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setAction("android.intent.action.VIEW"); + context.startActivity(intent); + return true; + } catch (Exception e) { + return false; + } + } + + public static boolean openZFBScan(Context context) { + try { + Uri uri = Uri.parse("alipayqr://platformapi/startapp?saId=10000007"); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + context.startActivity(intent); + return true; + } catch (Exception e) { + return false; + } + } + + public static boolean openZFB(Context context) { + try { + Uri uri = Uri.parse("alipayqr://platformapi/startapp"); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); context.startActivity(intent); return true; } catch (Exception e) { diff --git a/basic/utils/src/main/java/per/goweii/basic/utils/Base64Utils.kt b/basic/utils/src/main/java/per/goweii/basic/utils/Base64Utils.kt new file mode 100644 index 00000000..272f461c --- /dev/null +++ b/basic/utils/src/main/java/per/goweii/basic/utils/Base64Utils.kt @@ -0,0 +1,50 @@ +package per.goweii.basic.utils + +import android.util.Base64 +import java.nio.charset.StandardCharsets + +object Base64Utils { + fun encodeToBytes(src: ByteArray?): ByteArray { + if (src == null || src.isEmpty()) { + return ByteArray(0) + } + return Base64.encode(src, Base64.NO_WRAP) + } + + fun encodeToBytes(src: String?): ByteArray { + val bytes = src?.toByteArray(StandardCharsets.UTF_8) + return encodeToBytes(bytes) + } + + fun encodeToString(src: ByteArray?): String { + val bytes = encodeToBytes(src) + return String(bytes, StandardCharsets.UTF_8) + } + + fun encodeToString(src: String?): String { + val bytes = encodeToBytes(src) + return String(bytes, StandardCharsets.UTF_8) + } + + fun decodeToBytes(src: ByteArray?): ByteArray { + if (src == null || src.isEmpty()) { + return ByteArray(0) + } + return Base64.decode(src, Base64.NO_WRAP) + } + + fun decodeToBytes(src: String?): ByteArray { + val bytes = src?.toByteArray(StandardCharsets.UTF_8) + return decodeToBytes(bytes) + } + + fun decodeToString(src: ByteArray?): String { + val bytes = decodeToBytes(src) + return String(bytes, StandardCharsets.UTF_8) + } + + fun decodeToString(src: String?): String { + val bytes = decodeToBytes(src) + return String(bytes, StandardCharsets.UTF_8) + } +} \ No newline at end of file diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/CaptureUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/CaptureUtils.java similarity index 94% rename from basic_utils/src/main/java/per/goweii/basic/utils/CaptureUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/CaptureUtils.java index b29dbc01..488ae54b 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/CaptureUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/CaptureUtils.java @@ -4,14 +4,13 @@ import android.graphics.Canvas; import android.graphics.Picture; import android.os.Build; -import android.support.annotation.RequiresApi; import android.webkit.WebView; +import androidx.annotation.RequiresApi; + /** * @author CuiZhen * @date 2019/11/3 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class CaptureUtils { diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/ClickHelper.java b/basic/utils/src/main/java/per/goweii/basic/utils/ClickHelper.java similarity index 97% rename from basic_utils/src/main/java/per/goweii/basic/utils/ClickHelper.java rename to basic/utils/src/main/java/per/goweii/basic/utils/ClickHelper.java index 073bfe09..185ade46 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/ClickHelper.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/ClickHelper.java @@ -1,8 +1,9 @@ package per.goweii.basic.utils; -import android.support.annotation.NonNull; import android.view.View; +import androidx.annotation.NonNull; + import java.util.ArrayList; import java.util.List; diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/ColorUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/ColorUtils.java similarity index 94% rename from basic_utils/src/main/java/per/goweii/basic/utils/ColorUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/ColorUtils.java index 6467afed..077faa52 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/ColorUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/ColorUtils.java @@ -1,9 +1,10 @@ package per.goweii.basic.utils; import android.graphics.Color; -import android.support.annotation.ColorInt; -import android.support.annotation.FloatRange; -import android.support.annotation.IntRange; + +import androidx.annotation.ColorInt; +import androidx.annotation.FloatRange; +import androidx.annotation.IntRange; /** * 描述: diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/CopyUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/CopyUtils.java similarity index 93% rename from basic_utils/src/main/java/per/goweii/basic/utils/CopyUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/CopyUtils.java index 7e97f947..c79d9419 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/CopyUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/CopyUtils.java @@ -2,9 +2,10 @@ import android.content.ClipboardManager; import android.content.Context; -import android.support.annotation.NonNull; import android.widget.TextView; +import androidx.annotation.NonNull; + /** * 复制到剪贴板 * diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/DebugUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/DebugUtils.java similarity index 56% rename from basic_utils/src/main/java/per/goweii/basic/utils/DebugUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/DebugUtils.java index c9144549..a458025a 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/DebugUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/DebugUtils.java @@ -5,14 +5,18 @@ /** * @author CuiZhen * @date 2019/10/20 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class DebugUtils { + private static final boolean DEBUG; + + static { + DEBUG = BuildConfig.DEBUG && TextUtils.equals(BuildConfig.BUILD_TYPE, "debug"); + } + public static boolean isDebug() { - return BuildConfig.DEBUG && TextUtils.equals(BuildConfig.BUILD_TYPE, "debug"); + return DEBUG; } } diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/DeviceIdUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/DeviceIdUtils.java similarity index 89% rename from basic_utils/src/main/java/per/goweii/basic/utils/DeviceIdUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/DeviceIdUtils.java index b893b6c8..d5c4292e 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/DeviceIdUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/DeviceIdUtils.java @@ -3,12 +3,10 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; -import android.provider.Settings; -import android.support.annotation.NonNull; import android.text.TextUtils; -import java.math.BigInteger; -import java.security.SecureRandom; +import androidx.annotation.NonNull; + import java.util.UUID; import per.goweii.basic.utils.coder.MD5Coder; diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/DeviceUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/DeviceUtils.java similarity index 92% rename from basic_utils/src/main/java/per/goweii/basic/utils/DeviceUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/DeviceUtils.java index b56b19ca..87bdb5ef 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/DeviceUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/DeviceUtils.java @@ -16,7 +16,7 @@ public static String getAndroidId() { return Settings.Secure.getString(Utils.getAppContext().getContentResolver(), Settings.Secure.ANDROID_ID); } - public static String getSerial(){ + public static String getSerial() { return android.os.Build.SERIAL; } diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/DoubleBackUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/DoubleBackUtils.java similarity index 100% rename from basic_utils/src/main/java/per/goweii/basic/utils/DoubleBackUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/DoubleBackUtils.java diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/DownloadUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/DownloadUtils.java similarity index 99% rename from basic_utils/src/main/java/per/goweii/basic/utils/DownloadUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/DownloadUtils.java index b4178d7d..a13a353e 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/DownloadUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/DownloadUtils.java @@ -6,7 +6,8 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Environment; -import android.support.v4.content.FileProvider; + +import androidx.core.content.FileProvider; import java.io.File; import java.io.FileOutputStream; diff --git a/basic/utils/src/main/java/per/goweii/basic/utils/EdgeToEdgeUtils.kt b/basic/utils/src/main/java/per/goweii/basic/utils/EdgeToEdgeUtils.kt new file mode 100644 index 00000000..de4d3111 --- /dev/null +++ b/basic/utils/src/main/java/per/goweii/basic/utils/EdgeToEdgeUtils.kt @@ -0,0 +1,4 @@ +package per.goweii.basic.utils + +object EdgeToEdgeUtils { +} \ No newline at end of file diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/EditTextUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/EditTextUtils.java similarity index 98% rename from basic_utils/src/main/java/per/goweii/basic/utils/EditTextUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/EditTextUtils.java index 4b8f8c68..5acb5399 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/EditTextUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/EditTextUtils.java @@ -3,8 +3,6 @@ import android.annotation.SuppressLint; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; -import android.support.annotation.IntRange; -import android.support.annotation.NonNull; import android.text.Editable; import android.text.InputFilter; import android.text.Selection; @@ -15,6 +13,9 @@ import android.widget.EditText; import android.widget.TextView; +import androidx.annotation.IntRange; +import androidx.annotation.NonNull; + import java.lang.reflect.Field; /** @@ -25,7 +26,7 @@ */ public class EditTextUtils { - public static void changeCursorColor(TextView target, int colorInt){ + public static void changeCursorColor(TextView target, int colorInt) { try { Field fCursorDrawableRes = TextView.class.getDeclaredField("mCursorDrawableRes"); fCursorDrawableRes.setAccessible(true); @@ -44,11 +45,11 @@ public static void changeCursorColor(TextView target, int colorInt){ } } - public static void setEditTextMaxLength(@NonNull EditText editText, @IntRange(from = 1) int length){ + public static void setEditTextMaxLength(@NonNull EditText editText, @IntRange(from = 1) int length) { editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(length)}); } - public static void setEditTextDigitsKey(@NonNull EditText editText, @NonNull String keys){ + public static void setEditTextDigitsKey(@NonNull EditText editText, @NonNull String keys) { editText.setKeyListener(DigitsKeyListener.getInstance(keys)); } diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/IOUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/IOUtils.java similarity index 99% rename from basic_utils/src/main/java/per/goweii/basic/utils/IOUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/IOUtils.java index 8529d08d..26d9b851 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/IOUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/IOUtils.java @@ -19,8 +19,6 @@ /** * @author Cuizhen * @date 2018/2/10 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public final class IOUtils { diff --git a/basic/utils/src/main/java/per/goweii/basic/utils/InitTask.kt b/basic/utils/src/main/java/per/goweii/basic/utils/InitTask.kt new file mode 100644 index 00000000..d3f336d5 --- /dev/null +++ b/basic/utils/src/main/java/per/goweii/basic/utils/InitTask.kt @@ -0,0 +1,141 @@ +package per.goweii.basic.utils + +import android.app.Application +import android.os.AsyncTask +import android.os.Process +import java.io.BufferedReader +import java.io.File +import java.io.FileReader + +/** + * @author CuiZhen + */ +class InitTaskRunner(private val application: Application) { + + private val mTasks: ArrayList = arrayListOf() + + fun add(task: InitTask): InitTaskRunner { + mTasks.add(task) + return this + } + + fun run() { + val isMainProcess = isMainProcess() + val syncTasks: ArrayList = arrayListOf() + val asyncTasks: ArrayList = arrayListOf() + for (task in mTasks) { + if (!isMainProcess && task.onlyMainProcess()) { + continue + } + if (task.sync()) { + syncTasks.add(task) + } else { + asyncTasks.add(task) + } + } + runSync(syncTasks) + runAsync(asyncTasks) + } + + private fun runSync(tasks: ArrayList) { + tasks.sortBy { it.level() } + for (task in tasks) { + try { + task.init(application) + } catch (e: Throwable) { + e.printStackTrace() + } + } + } + + private fun runAsync(tasks: ArrayList) { + val tasksMap = hashMapOf>() + for (task in tasks) { + val name = task.asyncTaskName() + var list = tasksMap[name] + if (list == null) { + list = arrayListOf() + tasksMap[name] = list + } + list.add(task) + } + for (map in tasksMap) { + val task = map.value + AsyncRunner(application, task).execute() + } + } + + private fun isMainProcess(): Boolean { + return application.packageName == getCurrentProcessName() + } + + private fun getCurrentProcessName(): String? { + return try { + val file = File("/proc/" + Process.myPid() + "/" + "cmdline") + val mBufferedReader = BufferedReader(FileReader(file)) + val processName = mBufferedReader.readLine().trim { it <= ' ' } + mBufferedReader.close() + processName + } catch (e: Exception) { + e.printStackTrace() + null + } + } +} + +class AsyncRunner(private val application: Application, private val tasks: ArrayList) : AsyncTask() { + override fun doInBackground(vararg params: Unit?) { + tasks.sortBy { it.level() } + for (task in tasks) { + try { + task.init(application) + } catch (e: Throwable) { + e.printStackTrace() + } + } + } +} + +abstract class AsyncInitTask : InitTask { + override fun sync(): Boolean { + return false + } + + override fun level(): Int { + return 0 + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun asyncTaskName(): String { + return toString() + } +} + +abstract class SyncInitTask : InitTask { + override fun sync(): Boolean { + return true + } + + override fun level(): Int { + return 0 + } + + override fun onlyMainProcess(): Boolean { + return true + } + + override fun asyncTaskName(): String { + return "" + } +} + +interface InitTask { + fun sync(): Boolean + fun asyncTaskName(): String + fun level(): Int + fun onlyMainProcess(): Boolean + fun init(application: Application) +} \ No newline at end of file diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/InputMethodUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/InputMethodUtils.java similarity index 76% rename from basic_utils/src/main/java/per/goweii/basic/utils/InputMethodUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/InputMethodUtils.java index 11c79a04..8aa03fd8 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/InputMethodUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/InputMethodUtils.java @@ -41,8 +41,8 @@ public static void disableShowSoftInput(EditText editText) { */ public static void hide(View v) { InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - if (imm != null && imm.isActive()) { - imm.hideSoftInputFromWindow(v.getApplicationWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); + if (imm != null) { + imm.hideSoftInputFromWindow(v.getApplicationWindowToken(), 0); } } @@ -51,16 +51,8 @@ public static void hide(View v) { */ public static void show(View v) { InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - if (imm != null && !imm.isActive()) { - imm.showSoftInput(v, InputMethodManager.SHOW_FORCED); + if (imm != null) { + imm.showSoftInput(v, 0); } } - - /** - * 输入法是否显示着 - */ - public static boolean isShow(View v) { - InputMethodManager imm = (InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - return imm != null && imm.isActive(); - } } diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/IntentUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/IntentUtils.java similarity index 79% rename from basic_utils/src/main/java/per/goweii/basic/utils/IntentUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/IntentUtils.java index ad632d5e..dae58d06 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/IntentUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/IntentUtils.java @@ -1,10 +1,12 @@ package per.goweii.basic.utils; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.provider.Settings; -import android.support.annotation.RequiresPermission; + +import androidx.annotation.RequiresPermission; /** * 一些系统的Intent跳转 @@ -47,7 +49,11 @@ public static void goToSetting(Context context) { * 浏览器打开 */ public static void openBrowser(Context context, String url) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - context.startActivity(intent); + try { + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + e.printStackTrace(); + } } } diff --git a/basic/utils/src/main/java/per/goweii/basic/utils/LogUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/LogUtils.java new file mode 100644 index 00000000..9b5a790f --- /dev/null +++ b/basic/utils/src/main/java/per/goweii/basic/utils/LogUtils.java @@ -0,0 +1,95 @@ +package per.goweii.basic.utils; + +import android.util.Log; + +import per.goweii.ponyo.log.LogBody; +import per.goweii.ponyo.log.Ponlog; + +/** + * Log输出工具类 + * + * @author Cuizhen + * @version v1.0.0 + * @date 2018/3/18-上午10:21 + */ +public final class LogUtils { + private static boolean DEBUGGABLE = BuildConfig.DEBUG; + + private static Ponlog.Logger sLogger; + private static Ponlog.Logger sPonLogger; + + static { + sLogger = Ponlog.INSTANCE.create(); + sLogger.setInvokeClass(LogUtils.class); + sPonLogger = Ponlog.INSTANCE.create(); + sPonLogger.setAndroidLogPrinterEnable(false); + } + + public static void httpi(String msg) { + if (!DEBUGGABLE) return; + sPonLogger.println(Ponlog.Level.INFO, "RxHttp", new LogBody( + System.currentTimeMillis(), + Thread.currentThread().getName(), + "", "", "", -1 + ), msg); + } + + public static void httpe(String msg) { + if (!DEBUGGABLE) return; + sPonLogger.println(Ponlog.Level.ERROR, "RxHttp", new LogBody( + System.currentTimeMillis(), + Thread.currentThread().getName(), + "", "", "", -1 + ), msg); + } + + public static void v(String tag, Object msg) { + log(Log.VERBOSE, tag, msg); + } + + public static void d(String tag, Object msg) { + log(Log.DEBUG, tag, msg); + } + + public static void i(String tag, Object msg) { + log(Log.INFO, tag, msg); + } + + public static void w(String tag, Object msg) { + log(Log.WARN, tag, msg); + } + + public static void e(String tag, Object msg) { + log(Log.ERROR, tag, msg); + } + + public static void a(String tag, Object msg) { + log(Log.ASSERT, tag, msg); + } + + private static void log(int level, String tag, Object msg) { + if (!DEBUGGABLE) return; + switch (level) { + case Log.VERBOSE: + sLogger.log(Ponlog.Level.VERBOSE, tag, msg); + break; + case Log.DEBUG: + sLogger.log(Ponlog.Level.DEBUG, tag, msg); + break; + case Log.INFO: + sLogger.log(Ponlog.Level.INFO, tag, msg); + break; + case Log.WARN: + sLogger.log(Ponlog.Level.WARN, tag, msg); + break; + case Log.ERROR: + sLogger.log(Ponlog.Level.ERROR, tag, msg); + break; + case Log.ASSERT: + sLogger.log(Ponlog.Level.ASSERT, tag, msg); + break; + default: + break; + } + } +} diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/NetUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/NetUtils.java similarity index 97% rename from basic_utils/src/main/java/per/goweii/basic/utils/NetUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/NetUtils.java index 80326a8b..6147f3af 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/NetUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/NetUtils.java @@ -3,7 +3,8 @@ import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.support.annotation.RequiresPermission; + +import androidx.annotation.RequiresPermission; /** * 描述:判断网络的辅助类 diff --git a/basic/utils/src/main/java/per/goweii/basic/utils/ProcessUtils.kt b/basic/utils/src/main/java/per/goweii/basic/utils/ProcessUtils.kt new file mode 100644 index 00000000..d325dad5 --- /dev/null +++ b/basic/utils/src/main/java/per/goweii/basic/utils/ProcessUtils.kt @@ -0,0 +1,101 @@ +package per.goweii.basic.utils + +import android.annotation.SuppressLint +import android.app.ActivityManager +import android.app.Application +import android.content.Context +import android.os.Build +import java.io.BufferedReader +import java.io.FileReader + +@Suppress("ObjectPropertyName") +object ProcessUtils { + private lateinit var _currentProcessName: String + + val myPid: Int by lazy { + android.os.Process.myPid() + } + + val currentProcessName: String by lazy { + if (this::_currentProcessName.isInitialized) { + return@lazy _currentProcessName + } + var processName: String? = getCurrentProcessNameOnP() + if (processName.isNullOrBlank()) { + processName = getCurrentProcessNameByRe() + } + if (processName.isNullOrBlank()) { + processName = getProcessNameByCmdLine(myPid) + } + _currentProcessName = processName ?: "" + _currentProcessName + } + + fun getCurrentProcessName(context: Context? = null): String { + if (this::_currentProcessName.isInitialized) { + return _currentProcessName + } + var processName: String? = getCurrentProcessNameOnP() + if (processName.isNullOrBlank()) { + processName = getCurrentProcessNameByRe() + } + if (processName.isNullOrBlank() && context != null) { + processName = getProcessName(context, myPid) + } + _currentProcessName = processName ?: "" + return _currentProcessName + } + + fun getProcessName(context: Context? = null, pid: Int): String? { + return if (context != null) { + getProcessNameByAM(context, pid) + } else { + getProcessNameByCmdLine(pid) + } + } + + private fun getCurrentProcessNameOnP(): String? { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + return Application.getProcessName() + } + return null + } + + @SuppressLint("DiscouragedPrivateApi", "PrivateApi") + private fun getCurrentProcessNameByRe(): String? { + try { + val clazz = Class.forName("android.app.ActivityThread", + false, Application::class.java.classLoader) + val method = clazz.getDeclaredMethod("currentProcessName") + method.isAccessible = true + val obj = method.invoke(null) + if (obj is String) { + return obj + } + } catch (e: Throwable) { + } + return null + } + + private fun getProcessNameByAM(context: Context, pid: Int): String? { + val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager + return activityManager.runningAppProcesses + ?.find { it.pid == pid } + ?.processName + } + + private fun getProcessNameByCmdLine(pid: Int): String? { + return try { + BufferedReader(FileReader("/proc/$pid/cmdline")) + .use { reader -> + val line = reader.readLine() + if (!line.isNullOrBlank()) { + return@use line.trim { it <= ' ' } + } + return@use null + } + } catch (e: Throwable) { + null + } + } +} \ No newline at end of file diff --git a/basic/utils/src/main/java/per/goweii/basic/utils/RandomUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/RandomUtils.java new file mode 100644 index 00000000..85bf9725 --- /dev/null +++ b/basic/utils/src/main/java/per/goweii/basic/utils/RandomUtils.java @@ -0,0 +1,19 @@ +package per.goweii.basic.utils; + +/** + * @author CuiZhen + * @date 2019/12/28 + * GitHub: https://github.com/goweii + */ +public class RandomUtils { + + public static String randomLetter(int length) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < length; i++) { + int r = (int) (Math.random() * 26); + char c = Math.random() > 0.5 ? 'a' : 'A'; + s.append((char) (c + r)); + } + return s.toString(); + } +} diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/RegexUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/RegexUtils.java similarity index 100% rename from basic_utils/src/main/java/per/goweii/basic/utils/RegexUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/RegexUtils.java diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/ResUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/ResUtils.java similarity index 57% rename from basic_utils/src/main/java/per/goweii/basic/utils/ResUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/ResUtils.java index fbd8133b..e8e3be02 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/ResUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/ResUtils.java @@ -3,17 +3,22 @@ import android.content.Context; import android.content.res.AssetManager; import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; import android.graphics.drawable.Drawable; -import android.support.annotation.ArrayRes; -import android.support.annotation.BoolRes; -import android.support.annotation.ColorRes; -import android.support.annotation.DimenRes; -import android.support.annotation.DrawableRes; -import android.support.annotation.IntegerRes; -import android.support.annotation.StringRes; -import android.support.v4.content.ContextCompat; import android.view.View; +import androidx.annotation.ArrayRes; +import androidx.annotation.AttrRes; +import androidx.annotation.BoolRes; +import androidx.annotation.ColorRes; +import androidx.annotation.DimenRes; +import androidx.annotation.DrawableRes; +import androidx.annotation.IntegerRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; +import androidx.core.content.ContextCompat; + import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -25,32 +30,49 @@ * @date 2018/7/9-下午4:56 */ public class ResUtils { + public static Context getContext() { + Context context = Utils.getResumedActivity(); + if (context != null) return context; + return Utils.getAppContext(); + } public static Resources getResources() { - return Utils.getAppContext().getResources(); + return getContext().getResources(); } public static Drawable getDrawable(@DrawableRes int id) { - return ContextCompat.getDrawable(Utils.getAppContext(), id); + return ContextCompat.getDrawable(getContext(), id); } + @NonNull public static String getString(@StringRes int id) { return getResources().getString(id); } @Deprecated public static int getColor(@ColorRes int id) { - return ContextCompat.getColor(Utils.getAppContext(), id); + return ContextCompat.getColor(getContext(), id); } - public static int getColor(Context context, @ColorRes int id) { + public static int getColor(@NonNull Context context, @ColorRes int id) { return context.getResources().getColor(id); } - public static int getColor(View view, @ColorRes int id) { + public static int getColor(@NonNull View view, @ColorRes int id) { return view.getContext().getResources().getColor(id); } + public static int getThemeColor(@NonNull View view, @AttrRes int id) { + return getThemeColor(view.getContext(), id); + } + + public static int getThemeColor(Context context, @AttrRes int id) { + TypedArray typedArray = context.obtainStyledAttributes(new int[]{id}); + int color = typedArray.getColor(0, Color.TRANSPARENT); + typedArray.recycle(); + return color; + } + public static float getDimens(@DimenRes int id) { return getResources().getDimension(id); } diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/SPUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/SPUtils.java similarity index 93% rename from basic_utils/src/main/java/per/goweii/basic/utils/SPUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/SPUtils.java index c98b90e8..4b4a74fa 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/SPUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/SPUtils.java @@ -3,7 +3,8 @@ import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; -import android.support.annotation.NonNull; + +import androidx.annotation.NonNull; /** * 共享参数类 @@ -12,7 +13,7 @@ public class SPUtils { private final SharedPreferences sp; - private static class SPHolder{ + private static class SPHolder { private static final SharedPreferences INSTANCE = PreferenceManager.getDefaultSharedPreferences(Utils.getAppContext()); } @@ -49,7 +50,7 @@ public SPUtils clear() { return this; } - public SPUtils remove(String keyword){ + public SPUtils remove(String keyword) { getEditor().remove(keyword).apply(); return this; } @@ -95,5 +96,7 @@ public T get(String keyword, T defValue) { return value; } - + public boolean has(String keyword) { + return sp.contains(keyword); + } } diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/ShareUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/ShareUtils.java similarity index 51% rename from basic_utils/src/main/java/per/goweii/basic/utils/ShareUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/ShareUtils.java index 72e259e4..12a615aa 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/ShareUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/ShareUtils.java @@ -1,5 +1,6 @@ package per.goweii.basic.utils; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; @@ -11,8 +12,6 @@ /** * @author CuiZhen * @date 2019/11/10 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class ShareUtils { @@ -27,7 +26,24 @@ public static void shareBitmap(Context context, Bitmap bitmap) { intent.setAction(Intent.ACTION_SEND); intent.setType("image/*"); intent.putExtra(Intent.EXTRA_STREAM, UriUtils.getFileUri(file)); - intent = Intent.createChooser(intent, ""); - context.startActivity(intent); + intent = Intent.createChooser(intent, null); + try { + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + e.printStackTrace(); + } + } + + public static void shareLink(Context context, String url) { + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_SEND); + intent.putExtra(Intent.EXTRA_TEXT, url); + intent.setType("text/plain"); + intent = Intent.createChooser(intent, null); + try { + context.startActivity(intent); + } catch (ActivityNotFoundException e) { + e.printStackTrace(); + } } } diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/SoftInputHelper.java b/basic/utils/src/main/java/per/goweii/basic/utils/SoftInputHelper.java similarity index 66% rename from basic_utils/src/main/java/per/goweii/basic/utils/SoftInputHelper.java rename to basic/utils/src/main/java/per/goweii/basic/utils/SoftInputHelper.java index faede4d3..b86c9da7 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/SoftInputHelper.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/SoftInputHelper.java @@ -3,31 +3,27 @@ import android.animation.ObjectAnimator; import android.app.Activity; import android.graphics.Rect; -import android.support.annotation.NonNull; import android.view.View; import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; import android.view.animation.DecelerateInterpolator; -import android.widget.EditText; + +import java.util.HashMap; +import java.util.Map; /** * 监听软键盘的打开和隐藏 * 打开时滚动布局,可设置仅在某几个EditText获取焦点时开启 - * - * @author Cuizhen - * @version v1.0.0 - * @date 2018/3/30-上午9:06 */ -public class SoftInputHelper implements ViewTreeObserver.OnGlobalLayoutListener, ViewTreeObserver.OnGlobalFocusChangeListener { +public final class SoftInputHelper implements ViewTreeObserver.OnGlobalLayoutListener, ViewTreeObserver.OnGlobalFocusChangeListener { private final Window window; private final View rootView; - private long duration = 200; + private long duration = 300; private View moveView = null; - private View bottomView = null; - private EditText[] focusViews = null; + private Map focusBottomMap = new HashMap<>(1); private OnSoftInputListener onSoftInputListener = null; private boolean moveWithScroll = false; @@ -39,38 +35,39 @@ public class SoftInputHelper implements ViewTreeObserver.OnGlobalLayoutListener, private Runnable moveRunnable = new Runnable() { @Override public void run() { - if (isViewFocus()) { - Rect rect = getRootViewRect(); - int bottomViewY = getBottomViewY(); - if (bottomViewY > rect.bottom) { - int offHeight = bottomViewY - rect.bottom; - moveHeight += offHeight; - move(); - } - } else { - moveHeight = 0; - move(); - } + calcToMove(); } }; - private SoftInputHelper(@NonNull Activity activity) { + public static SoftInputHelper attach(Activity activity) { + return new SoftInputHelper(activity); + } + + private SoftInputHelper(Activity activity) { this.window = activity.getWindow(); this.rootView = window.getDecorView().getRootView(); ViewTreeObserver observer = rootView.getViewTreeObserver(); observer.addOnGlobalLayoutListener(this); observer.addOnGlobalFocusChangeListener(this); + window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); } - public static SoftInputHelper with(@NonNull Activity activity) { - return new SoftInputHelper(activity); + public void detach() { + if (rootView.getViewTreeObserver().isAlive()) { + rootView.getViewTreeObserver().removeOnGlobalLayoutListener(this); + rootView.getViewTreeObserver().removeOnGlobalFocusChangeListener(this); + } } - public SoftInputHelper init(@NonNull View moveView, @NonNull View bottomView, @NonNull EditText... focusViews) { + public SoftInputHelper moveBy(View moveView) { this.moveView = moveView; - this.bottomView = bottomView; - this.focusViews = focusViews; - window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING); + return this; + } + + public SoftInputHelper moveWith(View bottomView, View... focusViews) { + for (View focusView : focusViews) { + focusBottomMap.put(focusView, bottomView); + } return this; } @@ -106,8 +103,7 @@ public SoftInputHelper moveWithTranslation() { @Override public void onGlobalLayout() { - Rect rect = getRootViewRect(); - boolean isOpen = isSoftOpen(rect.bottom - rect.top, rootView.getHeight()); + boolean isOpen = isSoftOpen(); if (isOpen) { if (!isOpened) { isOpened = true; @@ -115,32 +111,12 @@ public void onGlobalLayout() { onSoftInputListener.onOpen(); } } - if (moveView != null && bottomView != null && focusViews != null) { + if (moveView != null) { if (isFocusChange) { isFocusChange = false; rootView.removeCallbacks(moveRunnable); } - if (isViewFocus()) { - int bottomViewY = getBottomViewY(); - if (bottomViewY > rect.bottom) { - int offHeight = bottomViewY - rect.bottom; - moveHeight += offHeight; - move(); - } else if (bottomViewY < rect.bottom) { - int offHeight = -(bottomViewY - rect.bottom); - if (moveHeight > 0) { - if (moveHeight >= offHeight) { - moveHeight -= offHeight; - } else { - moveHeight = 0; - } - move(); - } - } - } else { - moveHeight = 0; - move(); - } + calcToMove(); } } else { if (isOpened) { @@ -149,24 +125,53 @@ public void onGlobalLayout() { onSoftInputListener.onClose(); } } - if (moveView != null && bottomView != null && focusViews != null) { + if (moveView != null) { moveHeight = 0; move(); } } } + private void calcToMove() { + View focusView = isViewFocus(); + if (focusView != null) { + View bottomView = focusBottomMap.get(focusView); + if (bottomView != null) { + Rect rect = getRootViewRect(); + int bottomViewY = getBottomViewY(bottomView); + if (bottomViewY > rect.bottom) { + int offHeight = bottomViewY - rect.bottom; + moveHeight += offHeight; + move(); + } else if (bottomViewY < rect.bottom) { + int offHeight = -(bottomViewY - rect.bottom); + if (moveHeight > 0) { + if (moveHeight >= offHeight) { + moveHeight -= offHeight; + } else { + moveHeight = 0; + } + move(); + } + } + } + } else { + moveHeight = 0; + move(); + } + } + @Override public void onGlobalFocusChanged(View oldFocus, View newFocus) { if (isOpened) { - if (moveView != null && bottomView != null && focusViews != null) { + if (moveView != null) { isFocusChange = true; rootView.postDelayed(moveRunnable, 100); } } } - private int getBottomViewY() { + private int getBottomViewY(View bottomView) { int[] bottomLocation = new int[2]; bottomView.getLocationOnScreen(bottomLocation); return bottomLocation[1] + bottomView.getHeight(); @@ -212,11 +217,12 @@ private void scrollTo(int to) { * 判断软键盘打开状态的阈值 * 此处以用户可用高度变化值大于1/4总高度时作为判断依据。 * - * @param usableHeightNow 当前可被用户使用的高度 - * @param usableHeightSansKeyboard 总高度,及包含软键盘占位的高度 * @return boolean */ - private boolean isSoftOpen(int usableHeightNow, int usableHeightSansKeyboard) { + private boolean isSoftOpen() { + Rect rect = getRootViewRect(); + int usableHeightNow = rect.bottom - rect.top; + int usableHeightSansKeyboard = rootView.getHeight(); int heightDifference = usableHeightSansKeyboard - usableHeightNow; if (heightDifference > (usableHeightSansKeyboard / 4)) { return true; @@ -225,20 +231,14 @@ private boolean isSoftOpen(int usableHeightNow, int usableHeightSansKeyboard) { } } - private boolean isViewFocus() { - boolean focus = false; - if (focusViews == null || focusViews.length == 0) { - focus = true; - } else { - View focusView = window.getCurrentFocus(); - for (EditText editText : focusViews) { - if (focusView == editText) { - focus = true; - break; - } + private View isViewFocus() { + View focusView = window.getCurrentFocus(); + for (View view : focusBottomMap.keySet()) { + if (focusView == view) { + return view; } } - return focus; + return null; } public interface OnSoftInputListener { diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/StringUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/StringUtils.java similarity index 94% rename from basic_utils/src/main/java/per/goweii/basic/utils/StringUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/StringUtils.java index 6aa802e6..40c963c8 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/StringUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/StringUtils.java @@ -6,8 +6,6 @@ /** * @author CuiZhen * @date 2019/10/19 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class StringUtils { diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/UriUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/UriUtils.java similarity index 84% rename from basic_utils/src/main/java/per/goweii/basic/utils/UriUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/UriUtils.java index 934d34e5..e346e2e8 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/UriUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/UriUtils.java @@ -2,15 +2,14 @@ import android.net.Uri; import android.os.Build; -import android.support.v4.content.FileProvider; + +import androidx.core.content.FileProvider; import java.io.File; /** * @author CuiZhen * @date 2019/11/10 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ public class UriUtils { diff --git a/basic/utils/src/main/java/per/goweii/basic/utils/Utils.java b/basic/utils/src/main/java/per/goweii/basic/utils/Utils.java new file mode 100644 index 00000000..eba970cb --- /dev/null +++ b/basic/utils/src/main/java/per/goweii/basic/utils/Utils.java @@ -0,0 +1,100 @@ +package per.goweii.basic.utils; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.Application; +import android.content.Context; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import java.lang.ref.WeakReference; + +/** + * @author Cuizhen + */ +public class Utils { + @SuppressLint("StaticFieldLeak") + private static Context context = null; + private static final ActivityLifecycleCallbackImpl mActivityLifecycleCallback = new ActivityLifecycleCallbackImpl(); + + public static void init(Context context) { + if (Utils.context == null) { + Utils.context = context; + registerActivityLifecycleCallbacks(); + } + } + + @NonNull + public static Context getAppContext() { + if (context == null) { + throw new RuntimeException("Not initialized"); + } + return context; + } + + @Nullable + public static Activity getResumedActivity() { + if (context == null) { + throw new RuntimeException("Not initialized"); + } + return mActivityLifecycleCallback.getResumedActivity(); + } + + private static void registerActivityLifecycleCallbacks() { + if (context == null) { + return; + } + if (context instanceof Application) { + Application application = (Application) context; + application.registerActivityLifecycleCallbacks(mActivityLifecycleCallback); + } + } + + private static class ActivityLifecycleCallbackImpl implements Application.ActivityLifecycleCallbacks { + @Nullable + private WeakReference mResumedActivityRef = null; + + @Nullable + public Activity getResumedActivity() { + return mResumedActivityRef != null ? mResumedActivityRef.get() : null; + } + + @Override + public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { + } + + @Override + public void onActivityStarted(@NonNull Activity activity) { + } + + @Override + public void onActivityResumed(@NonNull Activity activity) { + if (mResumedActivityRef != null) { + mResumedActivityRef.clear(); + } + mResumedActivityRef = new WeakReference<>(activity); + } + + @Override + public void onActivityPaused(@NonNull Activity activity) { + } + + @Override + public void onActivityStopped(@NonNull Activity activity) { + } + + @Override + public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) { + } + + @Override + public void onActivityDestroyed(@NonNull Activity activity) { + if (mResumedActivityRef != null && mResumedActivityRef.get() == activity) { + mResumedActivityRef.clear(); + mResumedActivityRef = null; + } + } + } +} diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/ViewUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/ViewUtils.java similarity index 92% rename from basic_utils/src/main/java/per/goweii/basic/utils/ViewUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/ViewUtils.java index 959379c3..26473f2b 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/ViewUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/ViewUtils.java @@ -1,9 +1,10 @@ package per.goweii.basic.utils; -import android.support.annotation.Nullable; import android.widget.CompoundButton; import android.widget.RadioButton; +import androidx.annotation.Nullable; + /** * 描述: * @@ -34,7 +35,7 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { }); } for (RadioButton radioButton : radioButtons) { - if (defaultSelect == null){ + if (defaultSelect == null) { radioButton.setChecked(false); } else { if (radioButton.getId() == defaultSelect.getId()) { @@ -45,7 +46,7 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { } } - public interface SimpleListener{ + public interface SimpleListener { void onAction(E data); } } diff --git a/basic/utils/src/main/java/per/goweii/basic/utils/bitmap/BitmapUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/bitmap/BitmapUtils.java new file mode 100644 index 00000000..39b2fa79 --- /dev/null +++ b/basic/utils/src/main/java/per/goweii/basic/utils/bitmap/BitmapUtils.java @@ -0,0 +1,191 @@ +package per.goweii.basic.utils.bitmap; + +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Build; +import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.provider.MediaStore; +import android.text.TextUtils; + +import androidx.annotation.Nullable; + +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import per.goweii.basic.utils.UriUtils; +import per.goweii.basic.utils.Utils; +import per.goweii.basic.utils.file.CacheUtils; + +/** + * @author CuiZhen + * @date 2019/11/3 + * GitHub: https://github.com/goweii + */ +public class BitmapUtils { + + /** + * @param bmp 获取的bitmap数据 + * @param picName 自定义的图片名 + */ + @Deprecated + public static File saveGallery2(Bitmap bmp, String picName) { + FileOutputStream outStream = null; + try { + File gallery = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); + File file = new File(gallery, picName); + outStream = new FileOutputStream(file.getPath()); + bmp.compress(Bitmap.CompressFormat.JPEG, 100, outStream); + Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); + Uri uri = UriUtils.getFileUri(file); + intent.setData(uri); + Utils.getAppContext().sendBroadcast(intent); + return file; + } catch (Exception e) { + e.getStackTrace(); + return null; + } finally { + try { + if (outStream != null) { + outStream.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * @param bmp 获取的bitmap数据 + * @param picName 自定义的图片名 + */ + public static boolean saveGallery(Bitmap bmp, String picName) { + ContentValues contentValues = new ContentValues(); + contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, picName); + contentValues.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { + contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES); + } else { + File dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); + File file = new File(dir, picName); + contentValues.put(MediaStore.MediaColumns.DATA, file.getPath()); + } + Uri insertUri = Utils.getAppContext() + .getContentResolver() + .insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); + if (insertUri == null) { + return false; + } + OutputStream outputStream = null; + try { + outputStream = Utils.getAppContext() + .getContentResolver() + .openOutputStream(insertUri); + bmp.compress(Bitmap.CompressFormat.JPEG, 100, outputStream); + return true; + } catch (FileNotFoundException e) { + return false; + } finally { + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } + + public static File saveBitmapToCache(Bitmap bm) { + return saveBitmapToCache(bm, null); + } + + public static File saveBitmapToCache(Bitmap bm, String name) { + FileOutputStream outStream = null; + try { + String dir = CacheUtils.getCacheDir(); + String fileName; + if (TextUtils.isEmpty(name)) { + fileName = System.currentTimeMillis() + ".jpg"; + } else { + fileName = name; + } + File f = new File(dir, fileName); + outStream = new FileOutputStream(f); + bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream); + outStream.flush(); + outStream.close(); + return f; + } catch (IOException e) { + e.printStackTrace(); + return null; + } finally { + try { + if (outStream != null) { + outStream.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @Nullable + public static Uri getImageContentUri(Context context, String path) { + try (Cursor cursor = context.getContentResolver() + .query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + new String[]{MediaStore.Images.Media._ID}, + MediaStore.Images.Media.DATA + "=? ", + new String[]{path}, null)) { + if (cursor != null && cursor.moveToFirst()) { + int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID)); + Uri baseUri = Uri.parse("content://media/external/images/media"); + return Uri.withAppendedPath(baseUri, "" + id); + } else { + if (new File(path).exists()) { + ContentValues values = new ContentValues(); + values.put(MediaStore.Images.Media.DATA, path); + return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); + } else { + return null; + } + } + } + } + + // 通过uri加载图片 + @Nullable + public static Bitmap getBitmapFromUri(Context context, Uri uri) { + try { + ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "r"); + if (parcelFileDescriptor == null) { + return null; + } + FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); + Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); + parcelFileDescriptor.close(); + return image; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Nullable + public static Bitmap getBitmapFromPath(Context context, String path) { + Uri uri = getImageContentUri(context, path); + if (uri == null) { + return null; + } + return getBitmapFromUri(context, uri); + } +} diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/bitmap/MosaicUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/bitmap/MosaicUtils.java similarity index 100% rename from basic_utils/src/main/java/per/goweii/basic/utils/bitmap/MosaicUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/bitmap/MosaicUtils.java diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/bitmap/WatermarkUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/bitmap/WatermarkUtils.java similarity index 98% rename from basic_utils/src/main/java/per/goweii/basic/utils/bitmap/WatermarkUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/bitmap/WatermarkUtils.java index 4397618a..4a16d77e 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/bitmap/WatermarkUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/bitmap/WatermarkUtils.java @@ -4,7 +4,6 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; -import android.view.Gravity; /** * 水印 diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/coder/MD5Coder.java b/basic/utils/src/main/java/per/goweii/basic/utils/coder/MD5Coder.java similarity index 96% rename from basic_utils/src/main/java/per/goweii/basic/utils/coder/MD5Coder.java rename to basic/utils/src/main/java/per/goweii/basic/utils/coder/MD5Coder.java index f7387417..f95bd503 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/coder/MD5Coder.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/coder/MD5Coder.java @@ -1,12 +1,12 @@ package per.goweii.basic.utils.coder; -import android.support.annotation.NonNull; import android.text.TextUtils; +import androidx.annotation.NonNull; + import java.io.File; import java.io.FileInputStream; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/display/DisplayInfoUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/display/DisplayInfoUtils.java similarity index 99% rename from basic_utils/src/main/java/per/goweii/basic/utils/display/DisplayInfoUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/display/DisplayInfoUtils.java index 4b37706e..001228fc 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/display/DisplayInfoUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/display/DisplayInfoUtils.java @@ -22,8 +22,6 @@ * * @author Cuizhen * @date 2017/12/28 - * QQ: 302833254 - * E-mail: goweii@163.com * GitHub: https://github.com/goweii */ @SuppressLint("StaticFieldLeak") diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/display/NotchScreenUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/display/NotchScreenUtils.java similarity index 97% rename from basic_utils/src/main/java/per/goweii/basic/utils/display/NotchScreenUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/display/NotchScreenUtils.java index f372c8bb..2ed10868 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/display/NotchScreenUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/display/NotchScreenUtils.java @@ -6,11 +6,8 @@ import android.view.View; import android.view.WindowInsets; -import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import per.goweii.basic.utils.LogUtils; - /** * 描述:判断是否是刘海屏 */ diff --git a/basic/utils/src/main/java/per/goweii/basic/utils/ext/ViewExt.kt b/basic/utils/src/main/java/per/goweii/basic/utils/ext/ViewExt.kt new file mode 100644 index 00000000..b2a71ad6 --- /dev/null +++ b/basic/utils/src/main/java/per/goweii/basic/utils/ext/ViewExt.kt @@ -0,0 +1,56 @@ +package per.goweii.basic.utils.ext + +import android.view.View +import android.view.ViewGroup +import android.view.ViewTreeObserver + +fun View?.setPadding(px: Int) = this?.run { + setPadding(px, px, px, px) +} + +fun View?.visible() { + this?.visibility = View.VISIBLE +} + +fun View?.gone() { + this?.visibility = View.GONE +} + +fun View?.invisible() { + this?.visibility = View.INVISIBLE +} + +fun View?.onPreDraw(block: View.() -> Unit) = this?.run { + viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener { + override fun onPreDraw(): Boolean { + if (viewTreeObserver.isAlive) { + viewTreeObserver.removeOnPreDrawListener(this) + } + block.invoke(this@run) + return true + } + }) +} + +fun View?.measureSize(): IntArray? { + this?.apply { + var lp: ViewGroup.LayoutParams? = layoutParams + if (lp == null) { + lp = ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) + } + val widthSpec = ViewGroup.getChildMeasureSpec(0, 0, lp.width) + val lpHeight = lp.height + val heightSpec: Int + heightSpec = if (lpHeight > 0) { + View.MeasureSpec.makeMeasureSpec(lpHeight, View.MeasureSpec.EXACTLY) + } else { + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + } + measure(widthSpec, heightSpec) + return intArrayOf(measuredWidth, measuredHeight) + } + return null +} diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/file/CacheUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/file/CacheUtils.java similarity index 89% rename from basic_utils/src/main/java/per/goweii/basic/utils/file/CacheUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/file/CacheUtils.java index f5b0c2bb..1de35c1d 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/file/CacheUtils.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/file/CacheUtils.java @@ -6,6 +6,7 @@ /** * 缓存辅助类 + * * @author Cuizhen * @date 18/4/23 */ @@ -26,6 +27,11 @@ public static String getCacheDir() { return cacheFile.getAbsolutePath(); } + public static String getFilesDir() { + File cacheFile = Utils.getAppContext().getFilesDir(); + return cacheFile.getAbsolutePath(); + } + /** * 获取系统默认缓存文件夹内的缓存大小 */ diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/file/FileUtils.java b/basic/utils/src/main/java/per/goweii/basic/utils/file/FileUtils.java similarity index 100% rename from basic_utils/src/main/java/per/goweii/basic/utils/file/FileUtils.java rename to basic/utils/src/main/java/per/goweii/basic/utils/file/FileUtils.java diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/listener/AnimatorListenerWrapper.java b/basic/utils/src/main/java/per/goweii/basic/utils/listener/AnimatorListenerWrapper.java similarity index 100% rename from basic_utils/src/main/java/per/goweii/basic/utils/listener/AnimatorListenerWrapper.java rename to basic/utils/src/main/java/per/goweii/basic/utils/listener/AnimatorListenerWrapper.java diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/listener/OnClickListener2.java b/basic/utils/src/main/java/per/goweii/basic/utils/listener/OnClickListener2.java similarity index 100% rename from basic_utils/src/main/java/per/goweii/basic/utils/listener/OnClickListener2.java rename to basic/utils/src/main/java/per/goweii/basic/utils/listener/OnClickListener2.java diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/listener/SimpleCallback.java b/basic/utils/src/main/java/per/goweii/basic/utils/listener/SimpleCallback.java similarity index 100% rename from basic_utils/src/main/java/per/goweii/basic/utils/listener/SimpleCallback.java rename to basic/utils/src/main/java/per/goweii/basic/utils/listener/SimpleCallback.java diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/listener/SimpleListener.java b/basic/utils/src/main/java/per/goweii/basic/utils/listener/SimpleListener.java similarity index 100% rename from basic_utils/src/main/java/per/goweii/basic/utils/listener/SimpleListener.java rename to basic/utils/src/main/java/per/goweii/basic/utils/listener/SimpleListener.java diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/listener/SimpleTextWatcher.java b/basic/utils/src/main/java/per/goweii/basic/utils/listener/SimpleTextWatcher.java similarity index 100% rename from basic_utils/src/main/java/per/goweii/basic/utils/listener/SimpleTextWatcher.java rename to basic/utils/src/main/java/per/goweii/basic/utils/listener/SimpleTextWatcher.java diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/timer/BaseCountDownTimer.java b/basic/utils/src/main/java/per/goweii/basic/utils/timer/BaseCountDownTimer.java similarity index 97% rename from basic_utils/src/main/java/per/goweii/basic/utils/timer/BaseCountDownTimer.java rename to basic/utils/src/main/java/per/goweii/basic/utils/timer/BaseCountDownTimer.java index 47b10694..797fe4a1 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/timer/BaseCountDownTimer.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/timer/BaseCountDownTimer.java @@ -29,7 +29,7 @@ public boolean isStart() { return mStart; } - public void finish(){ + public void finish() { onFinish(); cancel(); } diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/timer/MillisCountDownTimer.java b/basic/utils/src/main/java/per/goweii/basic/utils/timer/MillisCountDownTimer.java similarity index 100% rename from basic_utils/src/main/java/per/goweii/basic/utils/timer/MillisCountDownTimer.java rename to basic/utils/src/main/java/per/goweii/basic/utils/timer/MillisCountDownTimer.java diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/timer/SecondCountDownTimer.java b/basic/utils/src/main/java/per/goweii/basic/utils/timer/SecondCountDownTimer.java similarity index 100% rename from basic_utils/src/main/java/per/goweii/basic/utils/timer/SecondCountDownTimer.java rename to basic/utils/src/main/java/per/goweii/basic/utils/timer/SecondCountDownTimer.java diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/timer/Timer.java b/basic/utils/src/main/java/per/goweii/basic/utils/timer/Timer.java similarity index 97% rename from basic_utils/src/main/java/per/goweii/basic/utils/timer/Timer.java rename to basic/utils/src/main/java/per/goweii/basic/utils/timer/Timer.java index 0393b941..2d7eb9bd 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/timer/Timer.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/timer/Timer.java @@ -4,7 +4,8 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.support.annotation.IntRange; + +import androidx.annotation.IntRange; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -158,13 +159,13 @@ public void run() { } } - private void increaseTime(){ + private void increaseTime() { if (mIncrease) { mTime += mDelay; } else { mTime -= mDelay; } - if (mTime < 0){ + if (mTime < 0) { mTime = 0; } } diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/timer/ViewCountDownTimer.java b/basic/utils/src/main/java/per/goweii/basic/utils/timer/ViewCountDownTimer.java similarity index 95% rename from basic_utils/src/main/java/per/goweii/basic/utils/timer/ViewCountDownTimer.java rename to basic/utils/src/main/java/per/goweii/basic/utils/timer/ViewCountDownTimer.java index bf5b3352..9763a6fe 100644 --- a/basic_utils/src/main/java/per/goweii/basic/utils/timer/ViewCountDownTimer.java +++ b/basic/utils/src/main/java/per/goweii/basic/utils/timer/ViewCountDownTimer.java @@ -1,8 +1,9 @@ package per.goweii.basic.utils.timer; -import android.support.annotation.NonNull; import android.view.View; +import androidx.annotation.NonNull; + /** * 描述:对一个View进行倒计时 * @@ -28,13 +29,13 @@ public ViewCountDownTimer setOnTimerListener(OnTimerListener onTimerListen return this; } - public void start(){ + public void start() { if (!mSecondTimer.isStart()) { mSecondTimer.start(); } } - public void cancel(){ + public void cancel() { mSecondTimer.cancel(); } @@ -61,7 +62,9 @@ public void onFinish() { public interface OnTimerListener { void onStart(V timerView, long secondUntilFinished); + void onTick(V timerView, long secondUntilFinished); + void onFinish(V timerView); } } diff --git a/basic_core/src/main/java/per/goweii/basic/core/base/App.java b/basic_core/src/main/java/per/goweii/basic/core/base/App.java deleted file mode 100644 index 3da6c3a8..00000000 --- a/basic_core/src/main/java/per/goweii/basic/core/base/App.java +++ /dev/null @@ -1,408 +0,0 @@ -package per.goweii.basic.core.base; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.ActivityManager; -import android.app.Application; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.Process; -import android.support.annotation.IntDef; -import android.support.annotation.Nullable; -import android.support.multidex.MultiDex; -import android.text.TextUtils; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; - -import per.goweii.basic.utils.LogUtils; - -/** - * @author Cuizhen - * @date 2018/6/25-上午10:39 - */ -class App extends Application implements Application.ActivityLifecycleCallbacks { - - private static final String TAG = App.class.getSimpleName(); - - protected static final int FLAG_CLEAR_TOP = 0; - protected static final int FLAG_CLEAR_OLD = 1; - - @SuppressLint("StaticFieldLeak") - private static Application application = null; - private static List activities = Collections.synchronizedList(new LinkedList()); - private static Map, Integer> singleInstanceActivities = Collections.synchronizedMap(new HashMap, Integer>()); - - public static Application getApp() { - if (application == null) { - throw new NullPointerException("App is not registered in the manifest"); - } else { - return application; - } - } - - public static Context getAppContext() { - return getApp().getApplicationContext(); - } - - public static void addSingleInstanceActivity(Class cls, @Flag int flag) { - singleInstanceActivities.put(cls, flag); - } - - public static List getActivities() { - return activities; - } - - public static boolean isAppAlive() { - if (application == null) { - return false; - } - if (activities == null || activities.size() == 0) { - return false; - } - return true; - } - - /** - * 判断Android程序是否在前台运行 - */ - public static boolean isAppOnForeground() { - ActivityManager activityManager = (ActivityManager) getAppContext().getSystemService(Context.ACTIVITY_SERVICE); - if (activityManager == null) { - return false; - } - List appProcesses = activityManager.getRunningAppProcesses(); - if (appProcesses == null) { - return false; - } - String packageName = getAppContext().getPackageName(); - for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) { - if (appProcess.processName.equals(packageName) && appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) { - return true; - } - } - return false; - } - - /** - * APP从后台切换回前台 - */ - public static void returnToForeground() { - if (!isAppOnForeground()) { - Activity currentActivity = currentActivity(); - if (currentActivity != null) { - Intent intent = new Intent(getAppContext(), currentActivity.getClass()); - intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - getAppContext().startActivity(intent); - } - } - } - - public static boolean isMainProcess() { - String mainProcessName = getAppContext().getPackageName(); - String processName = currentProcessName(); - return processName == null || TextUtils.equals(processName, mainProcessName); - } - - @Nullable - public static String currentProcessName() { - return getProcessName(Process.myPid()); - } - - /** - * 获取进程号对应的进程名 - * - * @param pid 进程号 - * @return 进程名 - */ - @Nullable - private static String getProcessName(int pid) { - BufferedReader reader = null; - try { - reader = new BufferedReader(new FileReader("/proc/" + pid + "/cmdline")); - String processName = reader.readLine(); - if (!TextUtils.isEmpty(processName)) { - processName = processName.trim(); - } - return processName; - } catch (Throwable throwable) { - throwable.printStackTrace(); - } finally { - try { - if (reader != null) { - reader.close(); - } - } catch (IOException exception) { - exception.printStackTrace(); - } - } - return null; - } - - /** - * 获取当前Activity - */ - @Nullable - public static Activity currentActivity() { - if (activities == null || activities.isEmpty()) { - return null; - } - return activities.get(activities.size() - 1); - } - - /** - * 按照指定类名找到activity - */ - @Nullable - public static Activity findActivity(Class cls) { - if (cls == null) { - return null; - } - if (activities == null || activities.isEmpty()) { - return null; - } - for (Activity activity : activities) { - if (activity.getClass().equals(cls)) { - return activity; - } - } - return null; - } - - /** - * 结束当前Activity - */ - public static void finishCurrentActivity() { - finishActivity(currentActivity()); - } - - /** - * 结束指定的Activity - */ - public static void finishActivity(Activity activity) { - if (activity == null) { - return; - } - if (activities == null || activities.isEmpty()) { - return; - } - activities.remove(activity); - activity.finish(); - activity = null; - } - - /** - * 结束指定类名的Activity - */ - public static void finishActivity(Class cls) { - if (cls == null) { - return; - } - if (activities == null || activities.isEmpty()) { - return; - } - for (int i = activities.size() - 1; i >= 0; i--) { - Activity activity = activities.get(i); - if (cls.equals(activity.getClass())) { - finishActivity(activity); - } - } - } - - /** - * 结束所有Activity - */ - public static void finishAllActivity() { - if (activities == null || activities.isEmpty()) { - return; - } - for (int i = activities.size() - 1; i >= 0; i--) { - Activity activity = activities.get(i); - if (!activity.isFinishing()) { - activity.finish(); - } - } - activities.clear(); - } - - /** - * 退出应用程序 - */ - public static void exitApp() { - finishAllActivity(); - } - - /** - * 退出应用程序 - */ - public static void killProcess() { - exitApp(); - Process.killProcess(Process.myPid()); - } - - public static void restart() { - finishActivityWithoutCount(1); - if (activities != null && !activities.isEmpty()) { - activities.get(0).recreate(); - } - } - - public static void recreate() { - if (activities != null && !activities.isEmpty()) { - for (Activity activity : activities) { - activity.recreate(); - } - } - } - - public static void finishActivityWithoutCount(int count) { - if (activities == null || activities.isEmpty()) { - return; - } - if (count <= 0) { - finishAllActivity(); - return; - } - for (int i = activities.size() - 1; i >= count; i--) { - finishActivity(activities.get(i)); - } - } - - public static void finishActivityWithout(Class cls) { - if (cls == null) { - finishAllActivity(); - return; - } - if (activities == null || activities.isEmpty()) { - return; - } - for (int i = activities.size() - 1; i >= 0; i--) { - Activity activity = activities.get(i); - if (!cls.equals(activity.getClass())) { - finishActivity(activity); - } - } - } - - public static void finishActivityWithout(Activity activity) { - if (activity == null) { - finishAllActivity(); - return; - } - finishActivityWithout(activity.getClass()); - } - - @Override - public void onCreate() { - super.onCreate(); - application = this; - registerActivityListener(); - } - - @Override - protected void attachBaseContext(Context context) { - super.attachBaseContext(context); - MultiDex.install(this); - } - - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - if (activity == null) { - return; - } - Class cls = activity.getClass(); - if (!singleInstanceActivities.containsKey(cls)) { - activities.add(activity); - return; - } - int flag = singleInstanceActivities.get(cls); - switch (flag) { - default: - throw new UnsupportedOperationException("Flag not find"); - case FLAG_CLEAR_TOP: - int oldPos = -1; - for (int i = 0; i < activities.size(); i++) { - if (cls.equals(activities.get(i).getClass())) { - oldPos = i; - } - } - if (oldPos >= 0) { - for (int i = activities.size() - 1; i >= oldPos; i--) { - Activity top = activities.get(i); - if (!top.isFinishing()) { - top.finish(); - } - } - } - break; - case FLAG_CLEAR_OLD: - for (int i = activities.size() - 1; i >= 0; i--) { - Activity old = activities.get(i); - if (cls.equals(old.getClass()) && !old.isFinishing()) { - old.finish(); - } - } - break; - } - activities.add(activity); - LogUtils.i(TAG, activities); - } - - @Override - public void onActivityStarted(Activity activity) { - - } - - @Override - public void onActivityResumed(Activity activity) { - - } - - @Override - public void onActivityPaused(Activity activity) { - - } - - @Override - public void onActivityStopped(Activity activity) { - - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) { - - } - - @Override - public void onActivityDestroyed(Activity activity) { - if (activity == null) { - return; - } - if (activities == null || activities.isEmpty()) { - return; - } - if (activities.contains(activity)) { - activities.remove(activity); - } - LogUtils.i(TAG, activities); - } - - private void registerActivityListener() { - registerActivityLifecycleCallbacks(this); - } - - @IntDef({FLAG_CLEAR_TOP, FLAG_CLEAR_OLD}) - @Retention(RetentionPolicy.SOURCE) - @interface Flag { - } - -} diff --git a/basic_core/src/main/java/per/goweii/basic/core/common/Config.java b/basic_core/src/main/java/per/goweii/basic/core/common/Config.java deleted file mode 100644 index 0b536fac..00000000 --- a/basic_core/src/main/java/per/goweii/basic/core/common/Config.java +++ /dev/null @@ -1,32 +0,0 @@ -package per.goweii.basic.core.common; - -import per.goweii.basic.core.BuildConfig; - -/** - * 可变的参数 - * - * @author Cuizhen - */ -public final class Config { - - public static final boolean DEBUG = true; - - public static final long HTTP_TIMEOUT = 5000; - - public static final String BASE_URL = "https://www.wanandroid.com/"; - - /** - * 强制退出登陆状态的本地广播的Action - */ - public static final String ACTION_FORCE_OFFLINE = BuildConfig.APPLICATION_ID + ".receiver.FORCE_OFFLINE"; - - /** - * 更新APP的本地广播的Action - */ - public static final String ACTION_APP_UPDATE = BuildConfig.APPLICATION_ID + ".receiver.APP_UPDATE"; - - /** - * 软件版本更新同一版本最大忽略时间 - */ - public static final long APP_UPDATE_IGNORE_TIME_MAX_MILL = 1 * 24 * 60 * 60 * 1000; -} \ No newline at end of file diff --git a/basic_core/src/main/java/per/goweii/basic/core/glide/GlideHelper.java b/basic_core/src/main/java/per/goweii/basic/core/glide/GlideHelper.java deleted file mode 100644 index 900bd454..00000000 --- a/basic_core/src/main/java/per/goweii/basic/core/glide/GlideHelper.java +++ /dev/null @@ -1,249 +0,0 @@ -package per.goweii.basic.core.glide; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Handler; -import android.os.Message; -import android.support.annotation.DrawableRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.widget.ImageView; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.RequestBuilder; -import com.bumptech.glide.RequestManager; -import com.bumptech.glide.load.DecodeFormat; -import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; -import com.bumptech.glide.request.RequestOptions; -import com.bumptech.glide.request.target.BitmapImageViewTarget; -import com.bumptech.glide.request.target.SimpleTarget; -import com.bumptech.glide.request.transition.Transition; - -import per.goweii.basic.core.glide.progress.OnProgressListener; -import per.goweii.basic.core.glide.progress.ProgressInterceptor; -import per.goweii.basic.utils.Utils; -import per.goweii.basic.utils.listener.SimpleCallback; -import per.goweii.basic.utils.listener.SimpleListener; - -/** - * Glide图片加载框架的帮助类 - * - * @author Cuizhen - * @date 2018/8/29-上午9:51 - */ -public class GlideHelper { - - private final RequestManager mManager; - private RequestBuilder mBuilder; - private boolean mCache = true; - private int mPlaceHolder = 0; - private int mErrorHolder = 0; - private OnGlideProgressListener mOnGlideProgressListener = null; - private OnProgressListener mProgressListener = null; - private Handler mProgressHandler = null; - private String mImageUrl; - private BitmapTransformation mBitmapTransformation = null; - - private GlideHelper(Context context) { - mManager = Glide.with(checkContext(context)); - } - - public static GlideHelper with(Context context) { - return new GlideHelper(context); - } - - public void pauseRequests() { - mManager.pauseRequests(); - } - - public void resumeRequests() { - mManager.resumeRequests(); - } - - public GlideHelper highQuality() { - mManager.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_ARGB_8888)); - return this; - } - - public GlideHelper cache(boolean cache) { - this.mCache = cache; - return this; - } - - public GlideHelper placeHolder(@DrawableRes int placeHolder) { - this.mPlaceHolder = placeHolder; - return this; - } - - public GlideHelper errorHolder(@DrawableRes int errorHolder) { - this.mErrorHolder = errorHolder; - return this; - } - - public GlideHelper transformation(BitmapTransformation transformation) { - this.mBitmapTransformation = transformation; - return this; - } - - @SuppressLint("HandlerLeak") - public GlideHelper onProgressListener(OnGlideProgressListener listener) { - mOnGlideProgressListener = listener; - mProgressHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - if (mOnGlideProgressListener != null) { - float progress = (float) msg.obj; - mOnGlideProgressListener.onProgress(progress); - } - } - }; - return this; - } - - public GlideHelper load(String url) { - this.mImageUrl = url; - mBuilder = getBuilder().load(url); - return this; - } - - public GlideHelper load(Uri uri) { - mBuilder = getBuilder().load(uri); - return this; - } - - public GlideHelper load(int resId) { - mBuilder = getBuilder().load(resId); - return this; - } - - public GlideHelper load(Bitmap bitmap) { - mBuilder = getBuilder().load(bitmap); - return this; - } - - public void into(ImageView imageView) { - if (mOnGlideProgressListener != null && mImageUrl != null) { - mProgressListener = new OnProgressListener() { - @Override - public void onProgress(float progress) { - if (mProgressHandler != null) { - Message msg = mProgressHandler.obtainMessage(); - msg.obj = progress; - mProgressHandler.sendMessage(msg); - } - } - }; - } - getBuilder().apply(getOptions()).into(new BitmapImageViewTarget(imageView) { - @Override - public void onLoadStarted(@Nullable Drawable placeholder) { - super.onLoadStarted(placeholder); - if (mOnGlideProgressListener != null) { - mOnGlideProgressListener.onProgress(0); - } - if (mProgressListener != null) { - ProgressInterceptor.addProgressListener(mImageUrl, mProgressListener); - } - } - - @Override - public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { - super.onResourceReady(resource, transition); - if (mOnGlideProgressListener != null) { - mOnGlideProgressListener.onProgress(1); - } - if (mProgressListener != null) { - ProgressInterceptor.removeProgressListener(mProgressListener); - } - } - - @Override - public void onLoadFailed(@Nullable Drawable errorDrawable) { - super.onLoadFailed(errorDrawable); - if (mOnGlideProgressListener != null) { - mOnGlideProgressListener.onProgress(-1); - } - if (mProgressListener != null) { - ProgressInterceptor.removeProgressListener(mProgressListener); - } - } - }); - } - - public void preload() { - getBuilder().apply(getOptions()).preload(); - } - - public void get(final SimpleCallback callback) { - getBuilder().apply(getOptions()).into(new SimpleTarget() { - @Override - public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { - if (callback != null) { - callback.onResult(resource); - } - } - }); - } - - public void get(final SimpleCallback onSuccess, final SimpleListener onFail) { - getBuilder().apply(getOptions()).into(new SimpleTarget() { - @Override - public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { - if (onSuccess != null) { - onSuccess.onResult(resource); - } - } - - @Override - public void onLoadFailed(@Nullable Drawable errorDrawable) { - super.onLoadFailed(errorDrawable); - if (onFail != null) { - onFail.onResult(); - } - } - }); - } - - @SuppressLint("CheckResult") - private RequestOptions getOptions() { - RequestOptions options = new RequestOptions(); - if (mPlaceHolder > 0) { - options.placeholder(mPlaceHolder); - } - if (mErrorHolder > 0) { - options.error(mErrorHolder); - } - if (mCache) { - options.diskCacheStrategy(DiskCacheStrategy.ALL); - } else { - options.skipMemoryCache(true) - .diskCacheStrategy(DiskCacheStrategy.NONE); - } - if (mBitmapTransformation != null) { - options.transform(mBitmapTransformation); - } - return options; - } - - private Context checkContext(Context context) { - if (context != null) { - return context; - } - return Utils.getAppContext(); - } - - private RequestBuilder getBuilder() { - if (mBuilder == null) { - mBuilder = mManager.asBitmap(); - } - return mBuilder; - } - - public interface OnGlideProgressListener { - void onProgress(float progress); - } -} diff --git a/basic_core/src/main/res/values/strings.xml b/basic_core/src/main/res/values/strings.xml deleted file mode 100644 index 60bb08fc..00000000 --- a/basic_core/src/main/res/values/strings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - BasicCore - - Fragment not found - diff --git a/basic_res/build.gradle b/basic_res/build.gradle deleted file mode 100644 index bc9c3627..00000000 --- a/basic_res/build.gradle +++ /dev/null @@ -1,9 +0,0 @@ -apply plugin: 'com.android.library' -apply from: "../basic.gradle" - -android { - resourcePrefix "" -} - -dependencies { -} diff --git a/basic_res/src/main/AndroidManifest.xml b/basic_res/src/main/AndroidManifest.xml deleted file mode 100644 index 01d81dd0..00000000 --- a/basic_res/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/basic_res/src/main/res/drawable-xxhdpi/ic_back.png b/basic_res/src/main/res/drawable-xxhdpi/ic_back.png deleted file mode 100644 index 3dded68f..00000000 Binary files a/basic_res/src/main/res/drawable-xxhdpi/ic_back.png and /dev/null differ diff --git a/basic_res/src/main/res/drawable-xxhdpi/ic_close.png b/basic_res/src/main/res/drawable-xxhdpi/ic_close.png deleted file mode 100644 index b6f237f9..00000000 Binary files a/basic_res/src/main/res/drawable-xxhdpi/ic_close.png and /dev/null differ diff --git a/basic_res/src/main/res/drawable/bg_press_color_foreground.xml b/basic_res/src/main/res/drawable/bg_press_color_foreground.xml deleted file mode 100644 index e6c2c2e7..00000000 --- a/basic_res/src/main/res/drawable/bg_press_color_foreground.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_press_color_surface.xml b/basic_res/src/main/res/drawable/bg_press_color_surface.xml deleted file mode 100644 index a42c5b43..00000000 --- a/basic_res/src/main/res/drawable/bg_press_color_surface.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_press_color_transparent.xml b/basic_res/src/main/res/drawable/bg_press_color_transparent.xml deleted file mode 100644 index 159f0f82..00000000 --- a/basic_res/src/main/res/drawable/bg_press_color_transparent.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_foreground_top_radius_max.xml b/basic_res/src/main/res/drawable/bg_solid_round_color_foreground_top_radius_max.xml deleted file mode 100644 index bf92fd79..00000000 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_foreground_top_radius_max.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/basic_res/src/main/res/drawable/bg_solid_round_color_surface_top_radius_max.xml b/basic_res/src/main/res/drawable/bg_solid_round_color_surface_top_radius_max.xml deleted file mode 100644 index 10bebb97..00000000 --- a/basic_res/src/main/res/drawable/bg_solid_round_color_surface_top_radius_max.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/basic_res/src/main/res/values-night/colors.xml b/basic_res/src/main/res/values-night/colors.xml deleted file mode 100644 index 57c88b1a..00000000 --- a/basic_res/src/main/res/values-night/colors.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - #aa000000 - #000000 - #0773a3f5 - #0773a3f5 - - - #73a3f5 - #67db9d - #f78c65 - #00000000 - #111111 - #111111 - #dd111111 - #11ffffff - #111111 - #dd111111 - #11ffffff - #141516 - #141516 - #cc141516 - #11ffffff - #222425 - #cc222425 - #11ffffff - #bbbbbb - #11bbbbbb - #67676a - #616161 - #393939 - - @color/main - @color/accent - #bbbbbb - #88bbbbbb - #67676a - #434346 - #111111 - #88111111 - #ffffff - \ No newline at end of file diff --git a/basic_res/src/main/res/values/colors.xml b/basic_res/src/main/res/values/colors.xml deleted file mode 100644 index 1f30328a..00000000 --- a/basic_res/src/main/res/values/colors.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - #aa000000 - #000000 - #11000000 - #11ffffff - - - #4282f4 - #3cdc86 - #f86734 - #00000000 - #f5f5f5 - #f5f5f5 - #ccf5f5f5 - #11000000 - #ffffff - #ccffffff - #ccffffff - #f5f5f5 - #ffffff - #ccffffff - #ccffffff - #f5f5f5 - #ccf5f5f5 - #11000000 - #333333 - #11000000 - #666666 - #999999 - #cccccc - - @color/main - @color/accent - #333333 - #88333333 - #666666 - #999999 - #ffffff - #88ffffff - #ffffff - \ No newline at end of file diff --git a/basic_res/src/main/res/values/strings.xml b/basic_res/src/main/res/values/strings.xml deleted file mode 100644 index af639734..00000000 --- a/basic_res/src/main/res/values/strings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - BasicRes - - 网络连接失败,请检查网络设置 - 访问出错,请稍后重试 - diff --git a/basic_res/src/main/res/values/styles.xml b/basic_res/src/main/res/values/styles.xml deleted file mode 100644 index 0d2c4cc4..00000000 --- a/basic_res/src/main/res/values/styles.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/basic_ui/build.gradle b/basic_ui/build.gradle deleted file mode 100644 index 806148f3..00000000 --- a/basic_ui/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -apply plugin: 'com.android.library' -apply from: "../basic.gradle" - -android { -} - -dependencies { - api project(":basic_utils") - api project(":basic_res") - - api rootProject.ext.dependencies.design - api rootProject.ext.dependencies.smartRefreshLayout - api rootProject.ext.dependencies.bravh - api rootProject.ext.dependencies.recyclerview - api rootProject.ext.dependencies.anyDialog - api rootProject.ext.dependencies.anyLayer - api rootProject.ext.dependencies.actionBarEx - api rootProject.ext.dependencies.percentImageView - api rootProject.ext.dependencies.roundedimageview - api rootProject.ext.dependencies.circleimageview - api rootProject.ext.dependencies.magicIndicator - api rootProject.ext.dependencies.materialProgressBar - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} -repositories { - mavenCentral() -} diff --git a/basic_ui/src/main/AndroidManifest.xml b/basic_ui/src/main/AndroidManifest.xml deleted file mode 100644 index a0418c3f..00000000 --- a/basic_ui/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/dialog/LoadingDialog.java b/basic_ui/src/main/java/per/goweii/basic/ui/dialog/LoadingDialog.java deleted file mode 100644 index 32b0359a..00000000 --- a/basic_ui/src/main/java/per/goweii/basic/ui/dialog/LoadingDialog.java +++ /dev/null @@ -1,74 +0,0 @@ -package per.goweii.basic.ui.dialog; - -import android.animation.Animator; -import android.content.Context; -import android.graphics.Color; -import android.view.Gravity; -import android.view.View; - -import per.goweii.anydialog.AnimHelper; -import per.goweii.anydialog.AnyDialog; -import per.goweii.anydialog.IAnim; -import per.goweii.basic.ui.R; - -/** - * loading弹窗 - * - * @author Cuizhen - * @date 2018/6/21-上午10:00 - */ -public class LoadingDialog { - - private static final long ANIM_DURATION = 200; - private final Context context; - private AnyDialog mAnyDialog; - private int count = 0; - - private LoadingDialog(Context context) { - this.context = context; - } - - public static LoadingDialog with(Context context) { - return new LoadingDialog(context); - } - - public void show() { - if (count <= 0) { - count = 0; - mAnyDialog = AnyDialog.with(context) - .contentView(R.layout.basic_ui_dialog_loading) - .backgroundColorInt(Color.TRANSPARENT) - .cancelableOnClickKeyBack(false) - .cancelableOnTouchOutside(false) - .gravity(Gravity.CENTER) - .contentAnim(new IAnim() { - @Override - public Animator inAnim(View target) { - return AnimHelper.createZoomInAnim(target).setDuration(ANIM_DURATION); - } - - @Override - public Animator outAnim(View target) { - return AnimHelper.createZoomOutAnim(target).setDuration(ANIM_DURATION); - } - }); - mAnyDialog.show(); - } - count++; - } - - public void dismiss() { - count--; - if (count <= 0) { - clear(); - } - } - - public void clear() { - if (mAnyDialog != null) { - mAnyDialog.dismiss(); - mAnyDialog = null; - } - count = 0; - } -} diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/toast/ToastMaker.java b/basic_ui/src/main/java/per/goweii/basic/ui/toast/ToastMaker.java deleted file mode 100644 index d3681e01..00000000 --- a/basic_ui/src/main/java/per/goweii/basic/ui/toast/ToastMaker.java +++ /dev/null @@ -1,243 +0,0 @@ -package per.goweii.basic.ui.toast; - -import android.animation.Animator; -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.support.annotation.StringRes; -import android.text.TextUtils; -import android.util.TypedValue; -import android.view.Gravity; -import android.view.View; -import android.view.WindowManager; -import android.widget.Toast; - -import java.lang.reflect.Field; - -import per.goweii.anylayer.AnimatorHelper; -import per.goweii.anylayer.AnyLayer; -import per.goweii.anylayer.Layer; -import per.goweii.basic.ui.R; -import per.goweii.basic.utils.Utils; - -/** - * Toast的工具类 - * 防止重复点击队列形式出现长时间不消失 - * 链式调用,方便定制 - * - * @author Cuizhen - * @version v1.0.0 - * @date 2018/3/10-下午2:09 - */ -@SuppressLint("StaticFieldLeak") -public class ToastMaker { - private static final int MAKE = 0; - private static final int SHOW = 1; - private static final int DURATION = 2; - private static final int GRAVITY = 3; - private static final int VIEW = 4; - private static final int MARGIN = 5; - private static final int ANIMATION = 6; - private static final int TEXT = 7; - private static Context context = null; - private static Toast toast = null; - private static Handler sHandler = null; - - static { - Utils.onInit(new Utils.OnInit() { - @Override - public void onInit(Context context) { - init(context); - } - }); - } - - @SuppressLint("ShowToast") - static void init(Context appContext) { - context = appContext.getApplicationContext(); - toast = Toast.makeText(appContext, "", Toast.LENGTH_SHORT); - sHandler = new Handler(appContext.getMainLooper()) { - @Override - public void handleMessage(Message msg) { - synchronized (ToastMaker.class) { - switch (msg.what) { - default: - break; - case MAKE: - CharSequence message = (CharSequence) msg.obj; - toast.setText(message); - break; - case TEXT: - String text = (String) msg.obj; - toast.setText(text); - break; - case SHOW: - toast.show(); - break; - case DURATION: - toast.setDuration(msg.arg1); - break; - case GRAVITY: - Bundle gravity = msg.getData(); - toast.setGravity(gravity.getInt("gravity"), gravity.getInt("xOffset"), gravity.getInt("yOffset")); - break; - case VIEW: - View view = (View) msg.obj; - toast.setView(view); - break; - case MARGIN: - Bundle margin = msg.getData(); - toast.setMargin(margin.getFloat("horizontalMargin"), margin.getFloat("verticalMargin")); - break; - case ANIMATION: - Bundle anim = msg.getData(); - int animResId = anim.getInt("animResId", 0); - if (animResId != 0) { - initAnim(animResId); - } - break; - } - } - } - }; - } - - public static void showShort(CharSequence message) { - if (TextUtils.isEmpty(message)) { - return; - } -// make(message).show(); - float mb = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 80, Utils.getAppContext().getResources().getDisplayMetrics()); - AnyLayer.toast() - .message(message) - .removeOthers(true) - .duration(2000) - .gravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL) - .backgroundDrawable(R.drawable.basic_ui_toast_bg) - .marginBottom((int) mb) - .marginLeft(0) - .marginRight(0) - .marginTop(0) - .animator(new Layer.AnimatorCreator() { - @Override - public Animator createInAnimator(View target) { - return AnimatorHelper.createBottomAlphaInAnim(target); - } - - @Override - public Animator createOutAnimator(View target) { - return AnimatorHelper.createBottomAlphaOutAnim(target); - } - }) - .show(); - } - - public static void showShort(@StringRes int message) { - showShort(context.getString(message)); - } - - @SuppressLint("ShowToast") - public static ToastMaker make(CharSequence message) { - if (sHandler == null) { - throw new ToastMakerNotInitException("未在Application中初始化"); - } - if (message != null) { - Message msg = sHandler.obtainMessage(); - msg.what = MAKE; - msg.obj = message; - sHandler.sendMessage(msg); - } - return Holder.INSTANCE; - } - - private static void initAnim(int animResId) { - try { - Field mTNField = toast.getClass().getDeclaredField("mTN"); - mTNField.setAccessible(true); - Object mTN = mTNField.get(toast); - Field tnParamsField = mTN.getClass().getDeclaredField("mParams"); - tnParamsField.setAccessible(true); - WindowManager.LayoutParams params = (WindowManager.LayoutParams) tnParamsField.get(mTN); - params.windowAnimations = animResId; - Field tnNextViewField = mTN.getClass().getDeclaredField("mNextView"); - tnNextViewField.setAccessible(true); - tnNextViewField.set(mTN, toast.getView()); - } catch (Exception ignore) { - } - } - - public ToastMaker duration(int duration) { - Message msg = sHandler.obtainMessage(); - msg.what = DURATION; - msg.arg1 = duration; - sHandler.sendMessage(msg); - return this; - } - - public ToastMaker view(View view) { - Message msg = sHandler.obtainMessage(); - msg.what = VIEW; - msg.obj = view; - sHandler.sendMessage(msg); - return this; - } - - public ToastMaker text(String text) { - Message msg = sHandler.obtainMessage(); - msg.what = TEXT; - msg.obj = text; - sHandler.sendMessage(msg); - return this; - } - - public ToastMaker gravity(int gravity, int xOffset, int yOffset) { - Message msg = sHandler.obtainMessage(); - msg.what = GRAVITY; - Bundle data = new Bundle(3); - data.putInt("gravity", gravity); - data.putInt("xOffset", xOffset); - data.putInt("yOffset", yOffset); - msg.setData(data); - sHandler.sendMessage(msg); - return this; - } - - public ToastMaker margin(float horizontalMargin, float verticalMargin) { - Message msg = sHandler.obtainMessage(); - msg.what = MARGIN; - Bundle data = new Bundle(2); - data.putFloat("horizontalMargin", horizontalMargin); - data.putFloat("verticalMargin", verticalMargin); - msg.setData(data); - sHandler.sendMessage(msg); - return this; - } - - public ToastMaker animation(int animResId) { - Message msg = sHandler.obtainMessage(); - msg.what = ANIMATION; - Bundle data = new Bundle(1); - data.putInt("animResId", animResId); - msg.setData(data); - sHandler.sendMessage(msg); - return this; - } - - public void show() { - Message msg = sHandler.obtainMessage(); - msg.what = SHOW; - sHandler.sendMessage(msg); - } - - private static class Holder { - private static ToastMaker INSTANCE = new ToastMaker(); - } - - private static class ToastMakerNotInitException extends RuntimeException { - private ToastMakerNotInitException(String message) { - super(message); - } - } -} diff --git a/basic_ui/src/main/java/per/goweii/basic/ui/widget/StatusBarView.java b/basic_ui/src/main/java/per/goweii/basic/ui/widget/StatusBarView.java deleted file mode 100644 index 39e820f9..00000000 --- a/basic_ui/src/main/java/per/goweii/basic/ui/widget/StatusBarView.java +++ /dev/null @@ -1,44 +0,0 @@ -package per.goweii.basic.ui.widget; - -import android.content.Context; -import android.support.annotation.Nullable; -import android.util.AttributeSet; -import android.view.View; - -/** - * @author CuiZhen - * @date 2019/8/25 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public class StatusBarView extends View { - - public StatusBarView(Context context) { - super(context); - } - - public StatusBarView(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - } - - public StatusBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - super.onMeasure(widthMeasureSpec, heightMeasureSpec); - setMeasuredDimension( - MeasureSpec.getSize(widthMeasureSpec), - getStatusBarHeight() + getPaddingTop() + getPaddingBottom()); - } - - public int getStatusBarHeight() { - int resourceId = getContext().getResources().getIdentifier("status_bar_height", "dimen", "android"); - if (resourceId > 0) { - return getContext().getResources().getDimensionPixelSize(resourceId); - } - return 0; - } -} diff --git a/basic_ui/src/main/res/drawable/basic_ui_dialog_loading_bg.xml b/basic_ui/src/main/res/drawable/basic_ui_dialog_loading_bg.xml deleted file mode 100644 index 3c67ce3e..00000000 --- a/basic_ui/src/main/res/drawable/basic_ui_dialog_loading_bg.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/basic_ui/src/main/res/values-night/colors.xml b/basic_ui/src/main/res/values-night/colors.xml deleted file mode 100644 index 2010287c..00000000 --- a/basic_ui/src/main/res/values-night/colors.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - @color/surface - @color/text_surface - @color/text_surface - @color/text_surface_alpha - @color/line - - @color/transparent - - @color/main - \ No newline at end of file diff --git a/basic_ui/src/main/res/values/attrs.xml b/basic_ui/src/main/res/values/attrs.xml deleted file mode 100644 index 3a82b0c6..00000000 --- a/basic_ui/src/main/res/values/attrs.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/basic_ui/src/main/res/values/colors.xml b/basic_ui/src/main/res/values/colors.xml deleted file mode 100644 index 56512535..00000000 --- a/basic_ui/src/main/res/values/colors.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - @color/main - @color/text_invert - @color/text_invert - @color/text_invert_alpha - @color/line - - @color/transparent - - @color/main - \ No newline at end of file diff --git a/basic_ui/src/main/res/values/dimens.xml b/basic_ui/src/main/res/values/dimens.xml deleted file mode 100644 index 149584bd..00000000 --- a/basic_ui/src/main/res/values/dimens.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - 1dp - \ No newline at end of file diff --git a/basic_ui/src/main/res/values/strings.xml b/basic_ui/src/main/res/values/strings.xml deleted file mode 100644 index 857176bd..00000000 --- a/basic_ui/src/main/res/values/strings.xml +++ /dev/null @@ -1,34 +0,0 @@ - - BasicUI - - 确定 - 取消 - 关闭 - - - 发现新版本 - 立即更新 - 暂不更新 - - - 下载更新中... - 立即安装 - - - 暂不授权 - 去授权 - 去设置 - 未知 - 日历 - 照相机 - 通讯录 - 定位 - 麦克风 - 手机 - 短信 - 传感器 - 存储空间 - 相关 - 我们需要获取%s权限以确保应用的正常运行,请点击去授权完成权限申请。 - 您已拒绝应用使用%s权限,这将影响功能的正常运行,请前往设置打开权限。 - diff --git a/basic_utils/build.gradle b/basic_utils/build.gradle deleted file mode 100644 index 11e1aa66..00000000 --- a/basic_utils/build.gradle +++ /dev/null @@ -1,9 +0,0 @@ -apply plugin: 'com.android.library' -apply from: "../basic.gradle" - -android { -} - -dependencies { - api rootProject.ext.dependencies.blurred -} diff --git a/basic_utils/src/main/AndroidManifest.xml b/basic_utils/src/main/AndroidManifest.xml deleted file mode 100644 index ea99a5ca..00000000 --- a/basic_utils/src/main/AndroidManifest.xml +++ /dev/null @@ -1,3 +0,0 @@ - - diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/LogUtils.java b/basic_utils/src/main/java/per/goweii/basic/utils/LogUtils.java deleted file mode 100644 index 7d9c76d5..00000000 --- a/basic_utils/src/main/java/per/goweii/basic/utils/LogUtils.java +++ /dev/null @@ -1,158 +0,0 @@ -package per.goweii.basic.utils; - -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.support.annotation.IntDef; -import android.util.Log; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; - -/** - * Log输出工具类 - * - * @author Cuizhen - * @version v1.0.0 - * @date 2018/3/18-上午10:21 - */ -public final class LogUtils { - public static final int VERBOSE = 1; - public static final int DEBUG = 1 << 1; - public static final int INFO = 1 << 2; - public static final int WARN = 1 << 3; - public static final int ERROR = 1 << 4; - public static final int NONE = 0; - public static final int ALL = VERBOSE | DEBUG | INFO | WARN | ERROR; - public static final int DEBUG_ABOVE = DEBUG | INFO | WARN | ERROR; - public static final int INFO_ABOVE = INFO | WARN | ERROR; - public static final int WARN_ABOVE = WARN | ERROR; - - @Level - private static int FILTER = ALL; - private static boolean DEBUGGABLE = false; - - public static void setFilter(@Level int filter) { - LogUtils.FILTER = filter; - } - - public static void init(Context context) { - try { - ApplicationInfo info = context.getApplicationInfo(); - DEBUGGABLE = (info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; - } catch (Exception ignore) { - } - } - - public static void v(String tag, String msg) { - log(tag, msg, VERBOSE); - } - - public static void v(String tag, Object msg) { - log(tag, msg, VERBOSE); - } - - public static void v(String tag, Object[] msg) { - v(tag, Arrays.toString(msg)); - } - - public static void d(String tag, String msg) { - log(tag, msg, DEBUG); - } - - public static void d(String tag, Object msg) { - log(tag, msg, DEBUG); - } - - public static void d(String tag, Object[] msg) { - d(tag, Arrays.toString(msg)); - } - - public static void i(String tag, String msg) { - log(tag, msg, INFO); - } - - public static void i(String tag, Object msg) { - log(tag, msg, INFO); - } - - public static void i(String tag, Object[] msg) { - i(tag, Arrays.toString(msg)); - } - - public static void w(String tag, String msg) { - log(tag, msg, WARN); - } - - public static void w(String tag, Object msg) { - log(tag, msg, WARN); - } - - public static void w(String tag, Object[] msg) { - w(tag, Arrays.toString(msg)); - } - - public static void e(String tag, String msg) { - log(tag, msg, ERROR); - } - - public static void e(String tag, Object msg) { - log(tag, msg, ERROR); - } - - public static void e(String tag, Object[] msg) { - e(tag, Arrays.toString(msg)); - } - - public static void log(String tag, Object msg, int filter) { - if (msg instanceof char[]) { - log(tag, Arrays.toString((char[]) msg), filter); - } else if (msg instanceof byte[]) { - log(tag, Arrays.toString((byte[]) msg), filter); - } else if (msg instanceof short[]) { - log(tag, Arrays.toString((short[]) msg), filter); - } else if (msg instanceof int[]) { - log(tag, Arrays.toString((int[]) msg), filter); - } else if (msg instanceof long[]) { - log(tag, Arrays.toString((long[]) msg), filter); - } else if (msg instanceof float[]) { - log(tag, Arrays.toString((float[]) msg), filter); - } else if (msg instanceof double[]) { - log(tag, Arrays.toString((double[]) msg), filter); - } else if (msg instanceof boolean[]) { - log(tag, Arrays.toString((boolean[]) msg), filter); - } else { - log(tag, String.valueOf(msg), filter); - } - } - - private static void log(String tag, String msg, int filter) { - if (!DEBUGGABLE) { - return; - } - switch (FILTER & filter) { - case VERBOSE: - Log.v(tag, msg); - break; - case DEBUG: - Log.d(tag, msg); - break; - case INFO: - Log.i(tag, msg); - break; - case WARN: - Log.w(tag, msg); - break; - case ERROR: - Log.e(tag, msg); - break; - default: - break; - } - } - - @IntDef({VERBOSE, DEBUG, INFO, WARN, ERROR, NONE, ALL, DEBUG_ABOVE, INFO_ABOVE, WARN_ABOVE}) - @Retention(RetentionPolicy.SOURCE) - @interface Level { - } -} diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/Utils.java b/basic_utils/src/main/java/per/goweii/basic/utils/Utils.java deleted file mode 100644 index a6edb6d0..00000000 --- a/basic_utils/src/main/java/per/goweii/basic/utils/Utils.java +++ /dev/null @@ -1,48 +0,0 @@ -package per.goweii.basic.utils; - -import android.annotation.SuppressLint; -import android.content.Context; - -import java.util.ArrayList; -import java.util.List; - -import per.goweii.burred.Blurred; - -/** - * @author Cuizhen - */ -public class Utils { - - @SuppressLint("StaticFieldLeak") - private static Context context = null; - private static List sOnInits = null; - - public static void init(Context context) { - Utils.context = context; - LogUtils.init(context); - Blurred.init(context); - if (sOnInits != null) { - for (OnInit onInit : sOnInits) { - onInit.onInit(context); - } - } - } - - public static Context getAppContext() { - if (context == null) { - throw new RuntimeException("Utils未在Application中初始化"); - } - return context; - } - - public static void onInit(OnInit onInit) { - if (sOnInits == null) { - sOnInits = new ArrayList<>(); - } - sOnInits.add(onInit); - } - - public interface OnInit { - void onInit(Context context); - } -} diff --git a/basic_utils/src/main/java/per/goweii/basic/utils/bitmap/BitmapUtils.java b/basic_utils/src/main/java/per/goweii/basic/utils/bitmap/BitmapUtils.java deleted file mode 100644 index 254f8cf7..00000000 --- a/basic_utils/src/main/java/per/goweii/basic/utils/bitmap/BitmapUtils.java +++ /dev/null @@ -1,78 +0,0 @@ -package per.goweii.basic.utils.bitmap; - -import android.content.Intent; -import android.graphics.Bitmap; -import android.net.Uri; -import android.os.Environment; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; - -import per.goweii.basic.utils.UriUtils; -import per.goweii.basic.utils.Utils; -import per.goweii.basic.utils.file.CacheUtils; - -/** - * @author CuiZhen - * @date 2019/11/3 - * QQ: 302833254 - * E-mail: goweii@163.com - * GitHub: https://github.com/goweii - */ -public class BitmapUtils { - - /** - * @param bmp 获取的bitmap数据 - * @param picName 自定义的图片名 - */ - public static File saveGallery(Bitmap bmp, String picName) { - FileOutputStream outStream = null; - try { - File gallery = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); - File file = new File(gallery, picName + ".jpg"); - outStream = new FileOutputStream(file.getPath()); - bmp.compress(Bitmap.CompressFormat.JPEG, 100, outStream); - Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); - Uri uri = UriUtils.getFileUri(file); - intent.setData(uri); - Utils.getAppContext().sendBroadcast(intent); - return file; - } catch (Exception e) { - e.getStackTrace(); - return null; - } finally { - try { - if (outStream != null) { - outStream.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - public static File saveBitmapToCache(Bitmap bm) { - FileOutputStream outStream = null; - try { - String dir = CacheUtils.getCacheDir(); - File f = new File(dir, System.currentTimeMillis() + ".jpg"); - outStream = new FileOutputStream(f); - bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream); - outStream.flush(); - outStream.close(); - return f; - } catch (IOException e) { - e.printStackTrace(); - return null; - } finally { - try { - if (outStream != null) { - outStream.close(); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - } -} diff --git a/build.gradle b/build.gradle index 87aa3bc1..4bcc900a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,33 +1,36 @@ -apply from: "config.gradle" +apply from: "${rootDir.path}/gradle/config.gradle" +apply from: "${rootDir.path}/gradle/version.gradle" buildscript { - ext.kotlin_version = '1.3.50' + ext.kotlin_version = "2.0.0" + ext.kotlin_coroutines_version = "1.7.1" repositories { - maven { url "https://jitpack.io" } google() + mavenCentral() jcenter() - maven { - url 'http://oss.jfrog.org/artifactory/oss-snapshot-local' - } + maven { url "https://maven.aliyun.com/repository/google" } + maven { url "https://maven.aliyun.com/repository/jcenter" } } dependencies { - classpath 'com.android.tools.build:gradle:3.3.2' - classpath "io.realm:realm-gradle-plugin:5.12.0-SNAPSHOT" + classpath "com.android.tools.build:gradle:8.7.0" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath 'com.tencent.bugly:symtabfileuploader:2.2.1' } } allprojects { repositories { maven { url "https://jitpack.io" } + //maven { url "https://raw.githubusercontent.com/goweii/maven-repository/master/releases/" } + maven { url "https://gitee.com/goweii/maven-repository/raw/master/releases/" } google() + mavenCentral() jcenter() - maven { - url 'http://oss.jfrog.org/artifactory/oss-snapshot-local' - } + maven { url "https://maven.aliyun.com/repository/google" } + maven { url "https://maven.aliyun.com/repository/jcenter" } } } task clean(type: Delete) { delete rootProject.buildDir -} +} \ No newline at end of file diff --git a/component/ad/.gitignore b/component/ad/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/component/ad/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/component/ad/build.gradle b/component/ad/build.gradle new file mode 100644 index 00000000..54beabc4 --- /dev/null +++ b/component/ad/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'com.android.library' +apply from: "${rootDir.path}/gradle/basic.gradle" + +android { + namespace "per.goweii.component.ad" +} + +dependencies { + api project(":basic:utils") + + api 'com.google.android.gms:play-services-ads:22.0.0' +} \ No newline at end of file diff --git a/component/ad/consumer-rules.pro b/component/ad/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/component/ad/src/main/AndroidManifest.xml b/component/ad/src/main/AndroidManifest.xml new file mode 100644 index 00000000..30f9dbc8 --- /dev/null +++ b/component/ad/src/main/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + + + + \ No newline at end of file diff --git a/component/ad/src/main/java/per/goweii/component/ad/BannerAdProvider.kt b/component/ad/src/main/java/per/goweii/component/ad/BannerAdProvider.kt new file mode 100644 index 00000000..b4e1b97a --- /dev/null +++ b/component/ad/src/main/java/per/goweii/component/ad/BannerAdProvider.kt @@ -0,0 +1,106 @@ +package per.goweii.component.ad + +import android.content.Context +import android.util.TypedValue +import android.view.ViewGroup +import android.widget.FrameLayout +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import com.google.android.gms.ads.AdListener +import com.google.android.gms.ads.AdSize +import com.google.android.gms.ads.AdView +import com.google.android.gms.ads.LoadAdError +import com.google.android.gms.ads.admanager.AdManagerAdRequest +import per.goweii.basic.utils.LogUtils + + +class BannerAdProvider( + private val context: Context, + private val lifecycleOwner: LifecycleOwner, + private val adUnitId: String, + private val adSize: AdSize = AdSize.getCurrentOrientationInlineAdaptiveBannerAdSize(context, 320) +) { + companion object { + private const val TAG = "BannerAdProvider" + + private const val TEST_ID = "ca-app-pub-3940256099942544/9214589741" + + fun test(context: Context, lifecycleOwner: LifecycleOwner): NativeAdProvider { + return NativeAdProvider( + context = context, + lifecycleOwner = lifecycleOwner, + adUnitID = TEST_ID, + ) + } + } + + private var adView: AdView? = null + + var onAdLoaded: (() -> Unit)? = null + + init { + if (lifecycleOwner.lifecycle.currentState != Lifecycle.State.DESTROYED) { + val adView = AdView(context) + adView.adUnitId = adUnitId + adView.setAdSize(adSize) + + adView.adListener = object : AdListener() { + override fun onAdClicked() { + LogUtils.d(TAG, "onAdClicked") + } + + override fun onAdClosed() { + LogUtils.d(TAG, "onAdClosed") + } + + override fun onAdFailedToLoad(adError: LoadAdError) { + LogUtils.d(TAG, adError) + } + + override fun onAdImpression() { + LogUtils.d(TAG, "onAdImpression") + } + + override fun onAdLoaded() { + LogUtils.d(TAG, "onAdLoaded: ${adView.responseInfo}") + onAdLoaded?.invoke() + } + + override fun onAdOpened() { + LogUtils.d(TAG, "onAdOpened") + } + } + + this.adView = adView + + val adRequest = AdManagerAdRequest.Builder().build() + adView.loadAd(adRequest) + LogUtils.d(TAG, "loadAd") + } + + lifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { + override fun onDestroy(owner: LifecycleOwner) { + super.onDestroy(owner) + detachFromContainer() + adView?.destroy() + adView = null + } + }) + } + + fun attachToContainer(adContainerView: FrameLayout) { + if (adView == null) return + detachFromContainer() + adContainerView.removeAllViews() + adContainerView.addView(adView!!) + } + + private fun detachFromContainer() { + if (adView == null) return + val parent = adView!!.parent + if (parent is ViewGroup) { + parent.removeView(adView) + } + } +} \ No newline at end of file diff --git a/component/ad/src/main/java/per/goweii/component/ad/NativeAdProvider.kt b/component/ad/src/main/java/per/goweii/component/ad/NativeAdProvider.kt new file mode 100644 index 00000000..c2ed65a7 --- /dev/null +++ b/component/ad/src/main/java/per/goweii/component/ad/NativeAdProvider.kt @@ -0,0 +1,80 @@ +package per.goweii.component.ad + +import android.content.Context +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import com.google.android.gms.ads.AdListener +import com.google.android.gms.ads.AdLoader +import com.google.android.gms.ads.AdRequest +import com.google.android.gms.ads.LoadAdError +import com.google.android.gms.ads.nativead.NativeAd +import com.google.android.gms.ads.nativead.NativeAdOptions +import per.goweii.basic.utils.LogUtils +import per.goweii.basic.utils.listener.SimpleCallback + +class NativeAdProvider( + private val context: Context, + private val lifecycleOwner: LifecycleOwner, + private val adUnitID: String, +) { + companion object { + private const val TAG = "NativeAdProvider" + + private const val TEST_ID = "ca-app-pub-3940256099942544/2247696110" + + fun test(context: Context, lifecycleOwner: LifecycleOwner): NativeAdProvider { + return NativeAdProvider( + context = context, + lifecycleOwner = lifecycleOwner, + adUnitID = TEST_ID, + ) + } + } + + private var ad: NativeAd? = null + + private var onLoaded: SimpleCallback? = null + + init { + lifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver { + override fun onDestroy(owner: LifecycleOwner) { + super.onDestroy(owner) + ad?.destroy() + ad = null + } + }) + } + + fun load(listener: SimpleCallback) { + if (ad != null) { + listener.onResult(ad!!) + return + } + onLoaded = listener + val adLoader = AdLoader.Builder(context, adUnitID) + .forNativeAd { ad: NativeAd -> + LogUtils.d(TAG, ad) + if (lifecycleOwner.lifecycle.currentState == Lifecycle.State.DESTROYED) { + ad.destroy() + return@forNativeAd + } + this.ad = ad + if (onLoaded != null) { + listener.onResult(ad) + } + } + .withAdListener(object : AdListener() { + override fun onAdFailedToLoad(adError: LoadAdError) { + LogUtils.d(TAG, adError) + } + }) + .withNativeAdOptions( + NativeAdOptions.Builder() + .build() + ) + .build() + adLoader.loadAd(AdRequest.Builder().build()) + LogUtils.d(TAG, "start load ad") + } +} \ No newline at end of file diff --git a/config.gradle b/config.gradle deleted file mode 100644 index 988551a8..00000000 --- a/config.gradle +++ /dev/null @@ -1,131 +0,0 @@ -ext { - android = [ - applicationId : "per.goweii.wanandroid", - compileSdkVersion: 28, - minSdkVersion : 19, - targetSdkVersion : 28, - buildToolsVersion: "28.0.3", - versionCode : 32, - versionName : "1.3.1", - ] - - compileOptions = [ - sourceCompatibility: JavaVersion.VERSION_1_8, - targetCompatibility: JavaVersion.VERSION_1_8, - ] - - versions = [ - appcompat : "28.0.0", - multidex : "1.0.3", - constraintLayout : "1.1.3", - flexbox : "1.0.0", - - butterknife : "8.8.1", - eventbus : "3.1.1", - gson : "2.8.5", - glide : "4.8.0", - glideCompiler : "4.8.0", - glideOkhttp3 : "4.8.0", - bravh : "2.9.46", - smartRefreshLayout : "1.1.0", - smartRefreshHorizontal: "1.0.0-beta-1", - rxHttp : "2.2.9", - anyPermission : "1.1.2", - anyDialog : "3.0.3", - anyLayer : "3.2.5", - actionBarEx : "3.2.4", - percentImageView : "1.0.2", - roundedimageview : "2.3.0", - circleimageview : "2.2.0", - magicIndicator : "1.6.0", - photoView : "2.1.3", - materialProgressBar : "1.4.2", - realtimeBlurView : "1.1.2", - agentWeb : "4.0.0", - banner : "1.4.10", - persistentCookieJar : "v1.0.1", - revealLayout : "1.2.1", - anyView : "1.0.2", - keyboardCompat : "1.0.0", - swipelayout : "1.2.0@aar", - disklrucache : "2.0.2", - multiStateView : "1.3.2", - pictureSelector : "v2.3.1", - swipeBack : "1.1.0", - lazyfragment : "1.1.2", - heartView : "1.0.0", - zxingLibrary : "2.2", - tbs : "43697", - - arouterApi : "1.4.1", - arouterCompiler : "1.2.2", - - tinypinyin : "2.0.3", - blurred : "1.3.0", - lottie : "2.7.0", - - buglyCrashreport : "3.1.0", - crashActivity : "2.2.0", - jinrishici : "1.2", - ] - - dependencies = [ - appcompatV7 : "com.android.support:appcompat-v7:$versions.appcompat", - design : "com.android.support:design:$versions.appcompat", - multidex : "com.android.support:multidex:$versions.multidex", - constraintLayout : "com.android.support.constraint:constraint-layout:$versions.constraintLayout", - recyclerview : "com.android.support:recyclerview-v7:$versions.appcompat", - cardview : "com.android.support:cardview-v7:$versions.appcompat", - flexbox : "com.google.android:flexbox:$versions.flexbox", - - butterknife : "com.jakewharton:butterknife:$versions.butterknife", - butterknifeCompiler : "com.jakewharton:butterknife-compiler:$versions.butterknife", - butterknifeGradlePlugin: "com.jakewharton:butterknife-gradle-plugin:$versions.butterknife", - eventbus : "org.greenrobot:eventbus:$versions.eventbus", - gson : "com.google.code.gson:gson:$versions.gson", - glide : "com.github.bumptech.glide:glide:$versions.glide", - glideCompiler : "com.github.bumptech.glide:compiler:$versions.glideCompiler", - glideOkhttp3 : "com.github.bumptech.glide:okhttp3-integration:$versions.glideOkhttp3", - bravh : "com.github.CymChad:BaseRecyclerViewAdapterHelper:$versions.bravh", - smartRefreshLayout : "com.scwang.smartrefresh:SmartRefreshLayout:$versions.smartRefreshLayout", - smartRefreshHorizontal : "com.scwang.smartrefresh:SmartRefreshHorizontal:$versions.smartRefreshHorizontal", - rxHttp : "com.github.goweii:RxHttp:$versions.rxHttp", - anyPermission : "com.github.goweii:AnyPermission:$versions.anyPermission", - anyDialog : "com.github.goweii:AnyDialog:$versions.anyDialog", - anyLayer : "com.github.goweii:AnyLayer:$versions.anyLayer", - actionBarEx : "com.github.goweii:ActionBarEx:$versions.actionBarEx", - percentImageView : "com.github.goweii:PercentImageView:$versions.percentImageView", - roundedimageview : "com.makeramen:roundedimageview:$versions.roundedimageview", - circleimageview : "de.hdodenhof:circleimageview:$versions.circleimageview", - magicIndicator : "com.github.hackware1993:MagicIndicator:$versions.magicIndicator", - photoView : "com.github.chrisbanes:PhotoView:$versions.photoView", - materialProgressBar : "me.zhanghai.android.materialprogressbar:library:$versions.materialProgressBar", - realtimeBlurView : "com.github.mmin18:realtimeblurview:$versions.realtimeBlurView", - agentWeb : "com.just.agentweb:agentweb:$versions.agentWeb", - banner : "com.youth.banner:banner:$versions.banner", - persistentCookieJar : "com.github.franmontiel:PersistentCookieJar:$versions.persistentCookieJar", - revealLayout : "com.github.goweii:RevealLayout:$versions.revealLayout", - anyView : "com.github.goweii:AnyView:$versions.anyView", - keyboardCompat : "com.github.goweii:KeyboardCompat:$versions.keyboardCompat", - swipelayout : "com.daimajia.swipelayout:library:$versions.swipelayout", - disklrucache : "com.jakewharton:disklrucache:$versions.disklrucache", - multiStateView : "com.github.Kennyc1012:MultiStateView:$versions.multiStateView", - pictureSelector : "com.github.goweii:PictureSelector:$versions.pictureSelector", - swipeBack : "com.github.goweii:SwipeBack:$versions.swipeBack", - lazyfragment : "com.github.goweii.LazyFragment:lazyfragment:$versions.lazyfragment", - heartView : "com.github.goweii:HeartView:$versions.heartView", - zxingLibrary : "cn.yipianfengye.android:zxing-library:$versions.zxingLibrary", - tbs : "com.tencent.tbs.tbssdk:sdk:$versions.tbs", - - arouterApi : "com.alibaba:arouter-api:$versions.arouterApi", - arouterCompiler : "com.alibaba:arouter-compiler:$versions.arouterCompiler", - - tinypinyin : "com.github.promeg:tinypinyin:$versions.tinypinyin", - blurred : "com.github.goweii:blurred:$versions.blurred", - lottie : "com.airbnb.android:lottie:$versions.lottie", - - buglyCrashreport : "com.tencent.bugly:crashreport:$versions.buglyCrashreport", - crashActivity : "cat.ereza:customactivityoncrash:$versions.crashActivity", - jinrishici : "com.jinrishici:android-sdk:$versions.jinrishici", - ] -} \ No newline at end of file diff --git a/demo.jks b/demo.jks new file mode 100644 index 00000000..fb712620 Binary files /dev/null and b/demo.jks differ diff --git a/gradle.properties b/gradle.properties index 82618cec..6f0240d0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,10 +6,15 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true - - +kapt.incremental.apt=true +android.useDeprecatedNdk=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false +android.defaults.buildfeatures.buildconfig=true \ No newline at end of file diff --git a/gradle/basic.gradle b/gradle/basic.gradle new file mode 100644 index 00000000..a4cc7d2c --- /dev/null +++ b/gradle/basic.gradle @@ -0,0 +1,57 @@ +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-parcelize' +apply plugin: 'kotlin-kapt' + +android { + compileSdkVersion rootProject.ext.android.compileSdkVersion + buildToolsVersion rootProject.ext.android.buildToolsVersion + defaultConfig { + minSdkVersion rootProject.ext.android.minSdkVersion + targetSdkVersion rootProject.ext.android.targetSdkVersion + versionCode rootProject.ext.android.versionCode + versionName rootProject.ext.android.versionName + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + multiDexEnabled true + } + buildFeatures { + viewBinding true + dataBinding true + } + buildTypes { + release { + debuggable false + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), + "${rootProject.rootDir}/proguard-rules.pro", + 'proguard-rules.pro' + } + debug { + debuggable true + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), + "${rootProject.rootDir}/proguard-rules.pro", + 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility rootProject.ext.compileOptions.sourceCompatibility + targetCompatibility rootProject.ext.compileOptions.targetCompatibility + } + kotlinOptions { + jvmTarget = rootProject.ext.kotlinOptions.jvmTarget + } + resourcePrefix project.name + "_" + lintOptions { + abortOnError false + } +} + +dependencies { + // implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs') + implementation rootProject.ext.dependencies.androidx + implementation rootProject.ext.dependencies.androidxKtx + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlin_coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlin_coroutines_version" +} \ No newline at end of file diff --git a/gradle/config.gradle b/gradle/config.gradle new file mode 100644 index 00000000..8b0342c6 --- /dev/null +++ b/gradle/config.gradle @@ -0,0 +1,191 @@ +ext { + android = [ + applicationId : "per.goweii.wanandroid", + compileSdkVersion: 35, + minSdkVersion : 21, + targetSdkVersion : 35, + buildToolsVersion: "35.0.0", + versionCode : 92, + versionName : "2.3.6", //-beta1 + ] + + compileOptions = [ + sourceCompatibility: JavaVersion.VERSION_1_8, + targetCompatibility: JavaVersion.VERSION_1_8, + ] + + kotlinOptions = [ + jvmTarget: JavaVersion.VERSION_1_8, + ] + + versions = [ + androidx_core : "1.10.1", + androidx_appcompat : "1.7.1", + androidx_arch : "2.2.0", + androidx_annotation : "1.9.1", + androidx_multidex : "2.0.1", + androidx_fragment : "1.8.8", + androidx_lifecycle : "2.9.1", + androidx_room : "2.7.1", + sqlite_jdbc : "3.34.0", + androidx_recyclerview : "1.4.0", + androidx_cardview : "1.0.0", + palette : "1.0.0", + camerax_core : "1.4.2", + camerax_view : "1.4.2", + androidx_biometric : "1.4.0-alpha04", + localbroadcastmanager : "1.1.0", + startup : "1.2.0", + webkit : "1.14.0", + + constraintLayout : "2.2.1", + flexbox : "1.0.0", + + butterknife : "10.0.0", + eventbus : "3.3.1", + gson : "2.8.5", + okhttp : "3.11.0", + glide : "4.11.0", + glideCompiler : "4.11.0", + glideOkhttp3 : "4.11.0", + bravh : "2.9.50", + smartRefreshLayout : "2.0.3", + smartRefreshHorizontal: "2.0.0", + rxHttp : "2.2.12", + anyPermission : "1.1.2", + anyDialog : "3.0.3", + anyLayer : "5.0.0-alpha02", + visualeffect : "1.0.0", + actionBarEx : "3.3.0", + statusBarCompat : "1.2.1", + percentImageView : "1.0.2", + roundedimageview : "2.3.0", + cropImageView : "1.2.1", + circleimageview : "2.2.0", + magicIndicator : "1.6.0", + photoView : "2.1.3", + materialProgressBar : "1.4.2", +// realtimeBlurView : "1.1.2", + banner : "1.4.10", + persistentCookieJar : "v1.0.1", + revealLayout : "1.3.4", + anyView : "1.0.2", + keyboardCompat : "1.0.0", + swipelayout : "1.2.0@aar", + disklrucache : "2.0.2", + multiStateView : "1.3.2", + swipeBack : "3.1.0-beta", + lazyfragment : "1.1.2", + heartView : "1.0.0", + tbs : "43697", + shadowLayout : "3.1.7", + + arouterApi : "1.4.1", + arouterCompiler : "1.2.2", + + tinypinyin : "2.0.3", +// blurred : "1.3.0", + lottie : "2.7.0", + + crashActivity : "2.2.0", + jinrishici : "1.2", + jsoup : "1.13.1", + + ponyo : "1.0.0", + codex : "1.0.0", + ] + + dependencies = [ + androidx : "androidx.core:core:${versions.androidx_core}", + androidxKtx : "androidx.core:core-ktx:${versions.androidx_core}", + androidxAppcompat : "androidx.appcompat:appcompat:${versions.androidx_appcompat}", + lifecycle : "androidx.lifecycle:lifecycle-runtime-ktx:${versions.androidx_lifecycle}", + lifecycle_java8 : "androidx.lifecycle:lifecycle-common-java8:${versions.androidx_lifecycle}", + lifecycle_compiler : "androidx.lifecycle:lifecycle-compiler:${versions.androidx_lifecycle}", + annotation : "androidx.annotation:${versions.androidx_annotation}", + webkit : "androidx.webkit:webkit:${versions.webkit}", + biometric : "androidx.biometric:biometric:${versions.androidx_biometric}", + multidex : "androidx.multidex:multidex:${versions.androidx_multidex}", + recyclerview : "androidx.recyclerview:recyclerview:${versions.androidx_recyclerview}", + cardview : "androidx.cardview:cardview:${versions.androidx_cardview}", + palette : "androidx.palette:palette:${versions.palette}", + roomRuntime : "androidx.room:room-runtime:${versions.androidx_room}", + roomRxjava2 : "androidx.room:room-rxjava2:${versions.androidx_room}", + roomCompiler : "androidx.room:room-compiler:${versions.androidx_room}", + roomKtx : "androidx.room:room-ktx:${versions.androidx_room}", + constraintLayout : "com.android.support.constraint:constraint-layout:${versions.constraintLayout}", + design : "com.google.android.material:material:1.0.0", + flexbox : "com.google.android:flexbox:${versions.flexbox}", + localbroadcastmanager : "androidx.localbroadcastmanager:localbroadcastmanager:${versions.localbroadcastmanager}", + startup : "androidx.startup:startup-runtime:${versions.startup}", + + butterknife : "com.jakewharton:butterknife:${versions.butterknife}", + butterknifeCompiler : "com.jakewharton:butterknife-compiler:${versions.butterknife}", + butterknifeGradlePlugin : "com.jakewharton:butterknife-gradle-plugin:${versions.butterknife}", + eventbus : "org.greenrobot:eventbus:${versions.eventbus}", + gson : "com.google.code.gson:gson:${versions.gson}", + okhttp : "com.squareup.okhttp3:okhttp:${versions.okhttp}", + okhttpSse : "com.squareup.okhttp3:okhttp-sse:${versions.okhttp}", + glide : "com.github.bumptech.glide:glide:${versions.glide}", + glideCompiler : "com.github.bumptech.glide:compiler:${versions.glideCompiler}", + glideOkhttp3 : "com.github.bumptech.glide:okhttp3-integration:${versions.glideOkhttp3}", + bravh : "com.github.CymChad:BaseRecyclerViewAdapterHelper:${versions.bravh}", + smartRefreshLayoutKernel : "com.scwang.smart:refresh-layout-kernel:${versions.smartRefreshLayout}", + smartRefreshHeaderClassics : "com.scwang.smart:refresh-header-classics:${versions.smartRefreshLayout}", + smartRefreshHeaderTwoLevel : "com.scwang.smart:refresh-header-two-level:${versions.smartRefreshLayout}", + smartRefreshFooterClassics : "com.scwang.smart:refresh-footer-classics:${versions.smartRefreshLayout}", + smartRefreshHorizontal : "com.scwang.smart:refresh-layout-horizontal:${versions.smartRefreshHorizontal}", + rxHttp : "com.github.goweii:RxHttp:${versions.rxHttp}", + anyPermission : "com.github.goweii:AnyPermission:${versions.anyPermission}", + anyDialog : "com.github.goweii:AnyDialog:${versions.anyDialog}", + anyLayer : "com.github.goweii:AnyLayer:${versions.anyLayer}", +// visualeffectCore : "per.goweii.visualeffect:visualeffect-core:${versions.visualeffect}", +// visualeffectView : "per.goweii.visualeffect:visualeffect-view:${versions.visualeffect}", +// visualeffectBlur : "per.goweii.visualeffect:visualeffect-blur:${versions.visualeffect}", + actionBarEx : "com.github.goweii:ActionBarEx:${versions.actionBarEx}", + statusBarCompat : "com.github.goweii:StatusBarCompat:${versions.statusBarCompat}", + percentImageView : "com.github.goweii:PercentImageView:${versions.percentImageView}", + roundedimageview : "com.makeramen:roundedimageview:${versions.roundedimageview}", + circleimageview : "de.hdodenhof:circleimageview:${versions.circleimageview}", + cropImageView : "com.github.goweii:CropImageView:${versions.cropImageView}", + magicIndicator : "com.github.hackware1993:MagicIndicator:${versions.magicIndicator}", + photoView : "com.github.chrisbanes:PhotoView:${versions.photoView}", + materialProgressBar : "me.zhanghai.android.materialprogressbar:library:${versions.materialProgressBar}", +// realtimeBlurView : "com.github.mmin18:realtimeblurview:${versions.realtimeBlurView}", + banner : "com.youth.banner:banner:${versions.banner}", + persistentCookieJar : "com.github.franmontiel:PersistentCookieJar:${versions.persistentCookieJar}", + revealLayout : "com.github.goweii:RevealLayout:${versions.revealLayout}", + anyView : "com.github.goweii:AnyView:${versions.anyView}", + keyboardCompat : "com.github.goweii:KeyboardCompat:${versions.keyboardCompat}", + swipelayout : "com.daimajia.swipelayout:library:${versions.swipelayout}", + disklrucache : "com.jakewharton:disklrucache:${versions.disklrucache}", + multiStateView : "com.github.Kennyc1012:MultiStateView:${versions.multiStateView}", + swipeBack : "com.github.goweii:SwipeBack:${versions.swipeBack}", + lazyfragment : "com.github.goweii.LazyFragment:lazyfragment:${versions.lazyfragment}", + heartView : "com.github.goweii:HeartView:${versions.heartView}", + tbs : "com.tencent.tbs.tbssdk:sdk:${versions.tbs}", + shadowLayout : "com.github.lihangleo2:ShadowLayout:${versions.shadowLayout}", + + arouterApi : "com.alibaba:arouter-api:${versions.arouterApi}", + arouterCompiler : "com.alibaba:arouter-compiler:${versions.arouterCompiler}", + + tinypinyin : "com.github.promeg:tinypinyin:${versions.tinypinyin}", +// blurred : "com.github.goweii:blurred:${versions.blurred}", + lottie : "com.airbnb.android:lottie:${versions.lottie}", + + jinrishici : "com.jinrishici:android-sdk:${versions.jinrishici}", + jsoup : "org.jsoup:jsoup:${versions.jsoup}", + + ponyo : "per.goweii.ponyo:ponyo:${versions.ponyo}", + ponyoLog : "per.goweii.ponyo:ponyo-log:${versions.ponyo}", + ponyoCrash : "per.goweii.ponyo:ponyo-crash:${versions.ponyo}", + + codex_core : "per.goweii.codex:core:${versions.codex}", + codex_decorator_gesture : "per.goweii.codex:decorator-gesture:${versions.codex}", + codex_decorator_beep : "per.goweii.codex:decorator-beep:${versions.codex}", + codex_decorator_finder_wechat: "per.goweii.codex:decorator-finder-wechat:${versions.codex}", + codex_decorator_frozen : "per.goweii.codex:decorator-frozen:${versions.codex}", + codex_decorator_vibrate : "per.goweii.codex:decorator-vibrate:${versions.codex}", + codex_processor_zxing : "per.goweii.codex:processor-zxing:${versions.codex}", + ] +} \ No newline at end of file diff --git a/gradle/flavor.gradle b/gradle/flavor.gradle new file mode 100644 index 00000000..e8ea64b0 --- /dev/null +++ b/gradle/flavor.gradle @@ -0,0 +1,133 @@ +def KEYSTORE_FILE = "" +def KEYSTORE_FILE_PASSWORD = "" +def KEYSTORE_ALIAS = "" +def KEYSTORE_ALIAS_PASSWORD = "" + +def CDKEY_CLASS = "" +def DEVELOPER_ID = "" + +def WANPWD_FORMAT = "" +def WANPWD_PATTERN = "" +def WANPWD_TYPE_CREATE_CDKEY = "" +def WANPWD_TYPE_QQ = "" +def WANPWD_TYPE_FESTIVAL = "" +def WANPWD_TYPE_USERPAGE = "" +def WANPWD_TYPE_CDKEY = "" +def WANPWD_TYPE_WEB = "" +def WANPWD_TYPE_ABOUTME = "" + +def GOOGLE_AD_APP_ID = "" +def GOOGLE_AD_UNIT_ID_ARTICLE_LIST_NATIVE_AD = "" +def GOOGLE_AD_UNIT_ID_ARTICLE_DETAILS_BOTTOM_BANNER_AD = "" + +if (project.rootProject.file('local.properties').exists()) { + Properties properties = new Properties() + InputStream inputStream = project.rootProject.file('local.properties').newDataInputStream() + properties.load(inputStream) + + KEYSTORE_FILE = properties.getProperty('KEYSTORE_FILE') + KEYSTORE_FILE_PASSWORD = properties.getProperty('KEYSTORE_FILE_PASSWORD') + KEYSTORE_ALIAS = properties.getProperty('KEYSTORE_ALIAS') + KEYSTORE_ALIAS_PASSWORD = properties.getProperty('KEYSTORE_ALIAS_PASSWORD') + + WANPWD_FORMAT = properties.getProperty('WANPWD_FORMAT') + WANPWD_PATTERN = properties.getProperty('WANPWD_PATTERN') + WANPWD_TYPE_QQ = properties.getProperty('WANPWD_TYPE_QQ') + WANPWD_TYPE_FESTIVAL = properties.getProperty('WANPWD_TYPE_FESTIVAL') + WANPWD_TYPE_USERPAGE = properties.getProperty('WANPWD_TYPE_USERPAGE') + WANPWD_TYPE_CDKEY = properties.getProperty('WANPWD_TYPE_CDKEY') + WANPWD_TYPE_WEB = properties.getProperty('WANPWD_TYPE_WEB') + WANPWD_TYPE_ABOUTME = properties.getProperty('WANPWD_TYPE_ABOUTME') + WANPWD_TYPE_CREATE_CDKEY = properties.getProperty('WANPWD_TYPE_CREATE_CDKEY') + + CDKEY_CLASS = properties.getProperty('CDKEY_CLASS') + DEVELOPER_ID = properties.getProperty('DEVELOPER_ID') + + GOOGLE_AD_APP_ID = properties.getProperty('GOOGLE_AD_APP_ID') + GOOGLE_AD_UNIT_ID_ARTICLE_LIST_NATIVE_AD = properties.getProperty('GOOGLE_AD_UNIT_ID_ARTICLE_LIST_NATIVE_AD') + GOOGLE_AD_UNIT_ID_ARTICLE_DETAILS_BOTTOM_BANNER_AD = properties.getProperty('GOOGLE_AD_UNIT_ID_ARTICLE_DETAILS_BOTTOM_BANNER_AD') +} else { + KEYSTORE_FILE = "${rootProject.projectDir}/demo.jks" + KEYSTORE_FILE_PASSWORD = "123456" + KEYSTORE_ALIAS = "demo" + KEYSTORE_ALIAS_PASSWORD = "123456" +} + +android { + signingConfigs { + release { + if (KEYSTORE_FILE != null) { + storeFile file(KEYSTORE_FILE) + storePassword KEYSTORE_FILE_PASSWORD + keyAlias KEYSTORE_ALIAS + keyPassword KEYSTORE_ALIAS_PASSWORD + } + } + debug { + if (KEYSTORE_FILE != null) { + storeFile file(KEYSTORE_FILE) + storePassword KEYSTORE_FILE_PASSWORD + keyAlias KEYSTORE_ALIAS + keyPassword KEYSTORE_ALIAS_PASSWORD + } + } + } + buildTypes { + release { + shrinkResources false + signingConfig signingConfigs.release + bundle { + language { + enableSplit = false + } + } + } + debug { + shrinkResources false + signingConfig signingConfigs.debug + bundle { + language { + enableSplit = false + } + } + } + } + flavorDimensions "applicationId" + productFlavors { + per { + dimension "applicationId" + applicationId "per.goweii.wanandroid" + ext.enableInAppUpdate = true + } + com { + dimension "applicationId" + applicationId "com.goweii.wanandroid" + ext.enableInAppUpdate = true + } + googlePlay { + dimension "applicationId" + applicationId "com.cuizhen.wanandroid" + ext.enableInAppUpdate = false + } + } + productFlavors.all { flavor -> + manifestPlaceholders = [ + REQUEST_INSTALL_PACKAGES_PERMISSION: ext.enableInAppUpdate ? "android.permission.REQUEST_INSTALL_PACKAGES" : "android.permission.INTERNET", + GOOGLE_AD_APP_ID: "$GOOGLE_AD_APP_ID", + ] + buildConfigField "boolean", "ENABLE_IN_APP_UPDATE", "${ext.enableInAppUpdate}" + buildConfigField "String", "WANPWD_FORMAT", "\"${WANPWD_FORMAT}\"" + buildConfigField "String", "WANPWD_PATTERN", "\"${WANPWD_PATTERN}\"" + buildConfigField "String", "WANPWD_TYPE_QQ", "\"${WANPWD_TYPE_QQ}\"" + buildConfigField "String", "WANPWD_TYPE_FESTIVAL", "\"${WANPWD_TYPE_FESTIVAL}\"" + buildConfigField "String", "WANPWD_TYPE_USERPAGE", "\"${WANPWD_TYPE_USERPAGE}\"" + buildConfigField "String", "WANPWD_TYPE_CDKEY", "\"${WANPWD_TYPE_CDKEY}\"" + buildConfigField "String", "WANPWD_TYPE_WEB", "\"${WANPWD_TYPE_WEB}\"" + buildConfigField "String", "WANPWD_TYPE_ABOUTME", "\"${WANPWD_TYPE_ABOUTME}\"" + buildConfigField "String", "WANPWD_TYPE_CREATE_CDKEY", "\"${WANPWD_TYPE_CREATE_CDKEY}\"" + buildConfigField "String", "CDKEY_CLASS", "\"${CDKEY_CLASS}\"" + buildConfigField "String", "DEVELOPER_ID", "\"${DEVELOPER_ID}\"" + buildConfigField "String", "GOOGLE_AD_UNIT_ID_ARTICLE_LIST_NATIVE_AD", "\"${GOOGLE_AD_UNIT_ID_ARTICLE_LIST_NATIVE_AD}\"" + buildConfigField "String", "GOOGLE_AD_UNIT_ID_ARTICLE_DETAILS_BOTTOM_BANNER_AD", "\"${GOOGLE_AD_UNIT_ID_ARTICLE_DETAILS_BOTTOM_BANNER_AD}\"" + } +} \ No newline at end of file diff --git a/gradle/module.gradle b/gradle/module.gradle new file mode 100644 index 00000000..d4a143db --- /dev/null +++ b/gradle/module.gradle @@ -0,0 +1,8 @@ +apply from: "${rootDir.path}/gradle/basic.gradle" + +android { +} + +dependencies { + implementation project(':basic:core') +} \ No newline at end of file diff --git a/gradle/version.gradle b/gradle/version.gradle new file mode 100644 index 00000000..a918f7a3 --- /dev/null +++ b/gradle/version.gradle @@ -0,0 +1,27 @@ +subprojects { + project.configurations.all { + resolutionStrategy.eachDependency { details -> + String group = details.requested.group + String name = details.requested.name + if (group == 'androidx.annotation' && name == 'annotation') { + //details.useVersion rootProject.ext.versions.androidx_annotation + } else if (group == 'androidx.arch.core') { + //details.useVersion rootProject.ext.versions.androidx_arch + } else if (group == 'androidx.core') { + //details.useVersion rootProject.ext.versions.androidx_core + } else if (group == 'androidx.fragment' && name == 'fragment') { + //details.useVersion rootProject.ext.versions.androidx_fragment + } else if (group == 'androidx.lifecycle') { + //details.useVersion rootProject.ext.versions.androidx_lifecycle + } else if (group == 'androidx.multidex' && name == 'multidex') { + //details.useVersion rootProject.ext.versions.androidx_multidex + } else if (group == 'androidx.recyclerview' && name == 'recyclerview') { + //details.useVersion rootProject.ext.versions.androidx_recyclerview + } else if (group == 'androidx.room') { + //details.useVersion rootProject.ext.versions.androidx_room + } else if (group == 'org.xerial' && name == 'sqlite-jdbc') { + details.useVersion '3.34.0' + } + } + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..f6b961fd Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..7ecca487 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Fri Dec 25 22:09:45 CST 2020 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip diff --git a/module.gradle b/module.gradle deleted file mode 100644 index 639f82fd..00000000 --- a/module.gradle +++ /dev/null @@ -1,8 +0,0 @@ -apply from: "../basic.gradle" - -android { -} - -dependencies { - implementation project(':basic_core') -} \ No newline at end of file diff --git a/proguard-rules.pro b/proguard-rules.pro new file mode 100644 index 00000000..8221386c --- /dev/null +++ b/proguard-rules.pro @@ -0,0 +1,133 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# 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 *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改 +-optimizationpasses 5 +# 混合时不使用大小写混合,混合后的类名为小写 +-dontusemixedcaseclassnames +# 指定不去忽略非公共库的类 +-dontskipnonpubliclibraryclasses +# 指定不去忽略非公共库的类成员 +-dontskipnonpubliclibraryclassmembers +# 这句话能够使我们的项目混淆后产生映射文件 +# 包含有类名->混淆后类名的映射关系 +-verbose +# 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。 +-dontpreverify +# 保留Annotation不混淆 这在JSON实体映射时非常重要,比如fastJson +-keepattributes *Annotation* +# 避免混淆泛型 +-keepattributes Signature +# JS调用Java方法 +-keepattributes *JavascriptInterface* +# 抛出异常时保留代码行号 +-keepattributes SourceFile,LineNumberTable +# 指定混淆是采用的算法,后面的参数是一个过滤器 +# 这个过滤器是谷歌推荐的算法,一般不做更改 +-optimizations !code/simplification/cast,!field/*,!class/merging/* +# 忽略警告 +-ignorewarnings +# 设置是否允许改变作用域 +-allowaccessmodification +# 把混淆类中的方法名也混淆了 +-useuniqueclassmembernames +# apk 包内所有 class 的内部结构 +-dump class_files.txt +# 未混淆的类和成员 +-printseeds seeds_txt +# 列出从apk中删除的代码 +-printusage unused.txt +# 混淆前后的映射 +-printmapping mapping.txt + + +# 保持哪些类不被混淆 +-keep public class * extends android.support.v4.app.Fragment +-keep public class * extends androidx.fragment.app.Fragment +-keep public class * extends android.app.Fragment +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Application +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider +-keep public class * extends android.app.backup.BackupAgentHelper +-keep public class * extends android.preference.Preference +-keep public class com.android.vending.licensing.ILicensingService + + +# 不能混淆枚举中的value和valueOf方法,因为这两个方法是静态添加到代码中进行,也会被反射使用,所以无法混淆这两种方法。 +# 应用使用枚举将添加很多方法,增加了包中的方法数,将增加 dex 的大小。 +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +# 保持 native 方法不被混淆 +-keepclasseswithmembernames class * { + native ; +} + +# 保持自定义控件类不被混淆 +-keepclasseswithmembers class * { + public (android.content.Context); +} +-keepclasseswithmembers class * { + public (android.content.Context, android.util.AttributeSet); +} +-keepclasseswithmembers class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +# 保持自定义控件类不被混淆 +-keepclassmembers class * extends android.app.Activity { + public void *(android.view.View); +} + +# 保持 Parcelable 不被混淆 +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} +-keep class * implements java.io.Serializable { + public *; +} +-keepclassmembers class * implements java.io.Serializable { + static final long serialVersionUID; + private static final java.io.ObjectStreamField[] serialPersistentFields; + !static !transient ; + private void writeObject(java.io.ObjectOutputStream); + private void readObject(java.io.ObjectInputStream); + java.lang.Object writeReplace(); + java.lang.Object readResolve(); +} + +-keepclassmembers class android.webkit.Webview { + public *; +} + +-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); +} diff --git a/settings.gradle b/settings.gradle index 4b8d0449..bd70373f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,7 @@ -include ':app', ':basic_core', ':basic_utils', ':basic_ui', ':basic_res' +include ':app' +include ':basic:core' +include ':basic:utils' +include ':basic:ui' +include ':basic:res' +include ':basic:l10n' +include ':component:ad'