pull/5/head
fengyuecanzhu 4 years ago
parent ac07c5d670
commit 8d6b2adc5c
  1. 4
      .idea/assetWizardSettings.xml
  2. BIN
      .idea/caches/build_file_checksums.ser
  3. 1
      .idea/sqldialects.xml
  4. 194
      README.md
  5. 3
      app/build.gradle
  6. 4
      app/src/main/AndroidManifest.xml
  7. 13
      app/src/main/assets/updatelog.fy
  8. 3
      app/src/main/assets/webdavhelp.fy
  9. 69
      app/src/main/java/xyz/fycz/myreader/application/MyApplication.java
  10. 11
      app/src/main/java/xyz/fycz/myreader/application/SysManager.java
  11. 251
      app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java
  12. 244
      app/src/main/java/xyz/fycz/myreader/base/BaseActivity2.java
  13. 2
      app/src/main/java/xyz/fycz/myreader/base/BaseTabActivity.java
  14. 4
      app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
  15. 129
      app/src/main/java/xyz/fycz/myreader/model/backup/BackupAndRestore.java
  16. 333
      app/src/main/java/xyz/fycz/myreader/model/backup/UserService.java
  17. 5
      app/src/main/java/xyz/fycz/myreader/model/storage/WebDavHelp.kt
  18. 16
      app/src/main/java/xyz/fycz/myreader/ui/activity/AboutActivity.java
  19. 7
      app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
  20. 4
      app/src/main/java/xyz/fycz/myreader/ui/activity/BookstoreActivity.java
  21. 4
      app/src/main/java/xyz/fycz/myreader/ui/activity/CatalogActivity.java
  22. 6
      app/src/main/java/xyz/fycz/myreader/ui/activity/FontsActivity.java
  23. 231
      app/src/main/java/xyz/fycz/myreader/ui/activity/LoginActivity.java
  24. 4
      app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java
  25. 15
      app/src/main/java/xyz/fycz/myreader/ui/activity/MoreSettingActivity.java
  26. 1501
      app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
  27. 271
      app/src/main/java/xyz/fycz/myreader/ui/activity/RegisterActivity.java
  28. 5
      app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java
  29. 24
      app/src/main/java/xyz/fycz/myreader/ui/activity/WebDavSettingActivity.java
  30. 6
      app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseDetailedAdapter.java
  31. 6
      app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseDragAdapter.java
  32. 62
      app/src/main/java/xyz/fycz/myreader/ui/dialog/CopyContentDialog.java
  33. 169
      app/src/main/java/xyz/fycz/myreader/ui/dialog/DialogCreator.java
  34. 209
      app/src/main/java/xyz/fycz/myreader/ui/fragment/MineFragment.java
  35. 39
      app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java
  36. 1314
      app/src/main/java/xyz/fycz/myreader/ui/presenter/ReadPresenter.java
  37. 72
      app/src/main/java/xyz/fycz/myreader/util/AlarmHelper.java
  38. 125
      app/src/main/java/xyz/fycz/myreader/util/Anti_hijackingUtils.java
  39. 125
      app/src/main/java/xyz/fycz/myreader/util/BeanPropertiesUtil.java
  40. 431
      app/src/main/java/xyz/fycz/myreader/util/DownloadMangerUtils.java
  41. 14
      app/src/main/java/xyz/fycz/myreader/util/IdHelper.java
  42. 266
      app/src/main/java/xyz/fycz/myreader/util/Lunar.java
  43. 326
      app/src/main/java/xyz/fycz/myreader/util/LunarCalendar.java
  44. 50
      app/src/main/java/xyz/fycz/myreader/util/ToastUtils.java
  45. 68
      app/src/main/java/xyz/fycz/myreader/util/utils/SnackbarUtils.java
  46. 161
      app/src/main/java/xyz/fycz/myreader/util/utils/SystemBarUtils.java
  47. 16
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/base/ReadCrawler.java
  48. 21
      app/src/main/java/xyz/fycz/myreader/widget/page/PageLoader.java
  49. 20
      app/src/main/java/xyz/fycz/myreader/widget/page/PageView.java
  50. 7
      app/src/main/res/anim/slide_bottom_in.xml
  51. 7
      app/src/main/res/anim/slide_bottom_out.xml
  52. 8
      app/src/main/res/anim/slide_top_in.xml
  53. 6
      app/src/main/res/anim/slide_top_out.xml
  54. BIN
      app/src/main/res/drawable-hdpi/ic_keyboard_arrow_left_black_24dp.png
  55. BIN
      app/src/main/res/drawable-hdpi/ic_refresh_black_24dp.png
  56. BIN
      app/src/main/res/drawable-hdpi/ic_search_black_24dp.png
  57. BIN
      app/src/main/res/drawable-mdpi/ic_keyboard_arrow_left_black_24dp.png
  58. BIN
      app/src/main/res/drawable-mdpi/ic_refresh_black_24dp.png
  59. BIN
      app/src/main/res/drawable-mdpi/ic_search_black_24dp.png
  60. BIN
      app/src/main/res/drawable-xhdpi/arm_left.png
  61. BIN
      app/src/main/res/drawable-xhdpi/arm_right.png
  62. BIN
      app/src/main/res/drawable-xhdpi/bg.jpeg
  63. BIN
      app/src/main/res/drawable-xhdpi/ic_keyboard_arrow_left_black_24dp.png
  64. BIN
      app/src/main/res/drawable-xhdpi/ic_refresh_black_24dp.png
  65. BIN
      app/src/main/res/drawable-xhdpi/ic_search_black_24dp.png
  66. BIN
      app/src/main/res/drawable-xhdpi/icon_hand.png
  67. BIN
      app/src/main/res/drawable-xhdpi/iconfont_password.png
  68. BIN
      app/src/main/res/drawable-xhdpi/iconfont_user.png
  69. BIN
      app/src/main/res/drawable-xhdpi/owl_head.png
  70. BIN
      app/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_left_black_24dp.png
  71. BIN
      app/src/main/res/drawable-xxhdpi/ic_menu_overflow.png
  72. BIN
      app/src/main/res/drawable-xxhdpi/ic_menu_setting.png
  73. BIN
      app/src/main/res/drawable-xxhdpi/ic_refresh_black_24dp.png
  74. BIN
      app/src/main/res/drawable-xxhdpi/ic_search_black_24dp.png
  75. BIN
      app/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_left_black_24dp.png
  76. BIN
      app/src/main/res/drawable-xxxhdpi/ic_refresh_black_24dp.png
  77. BIN
      app/src/main/res/drawable-xxxhdpi/ic_search_black_24dp.png
  78. 13
      app/src/main/res/drawable/ic_bookmark.xml
  79. 9
      app/src/main/res/drawable/ic_copy.xml
  80. 10
      app/src/main/res/drawable/ic_link.xml
  81. 9
      app/src/main/res/drawable/ic_search2.xml
  82. 2
      app/src/main/res/layout/activity_catalog.xml
  83. 241
      app/src/main/res/layout/activity_login.xml
  84. 199
      app/src/main/res/layout/activity_read.xml
  85. 59
      app/src/main/res/layout/activity_read_new.xml
  86. 205
      app/src/main/res/layout/activity_register.xml
  87. 16
      app/src/main/res/layout/dialog_copy_content.xml
  88. 297
      app/src/main/res/layout/dialog_read_setting.xml
  89. 102
      app/src/main/res/layout/fragment_mine.xml
  90. 36
      app/src/main/res/layout/layout_about_content.xml
  91. 2
      app/src/main/res/layout/scroll_title_indicator.xml
  92. 34
      app/src/main/res/menu/menu_read.xml
  93. 10
      app/src/main/res/menu/webdav_help.xml
  94. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_round.png
  95. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_round.png
  96. BIN
      app/src/main/res/mipmap-xhdpi/add.png
  97. BIN
      app/src/main/res/mipmap-xhdpi/b9.png
  98. BIN
      app/src/main/res/mipmap-xhdpi/bd.png
  99. BIN
      app/src/main/res/mipmap-xhdpi/default_cover.jpg
  100. BIN
      app/src/main/res/mipmap-xhdpi/default_cover.png
  101. Some files were not shown because too many files have changed in this diff Show More

@ -19,8 +19,8 @@
<option name="values">
<map>
<entry key="assetSourceType" value="FILE" />
<entry key="outputName" value="ic_group" />
<entry key="sourceFile" value="F:\SVG图标\分组.svg" />
<entry key="outputName" value="ic_bookmark" />
<entry key="sourceFile" value="F:\SVG图标\书签.svg" />
</map>
</option>
</PersistentState>

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/app/src/main/java/xyz/fycz/myreader/model/backup/UserService.java" dialect="GenericSQL" />
<file url="PROJECT" dialect="MySQL" />
</component>
</project>

@ -1,18 +1,199 @@
# FYReader-master
风月读书,一款开源、无广告的小说阅读软件。
11个网络小说书源:天籁小说、笔趣阁44、品书网、笔趣阁、
demo下载:https://fycz.lanzoui.com/iBofFh42pxg
#### 一、关于书源
* 软件内置了15个书源如下:
* 11个网络小说书源:天籁小说、笔趣阁44、品书网、笔趣阁、
全本小说、米趣小说、九桃小说、云中书库、
搜小说网、全小说网、奇奇小说
4个实体书书源:超星图书·实体、作品集·实体、99藏书·实体、100本·实体
* 4个实体书书源:超星图书·实体、作品集·实体、99藏书·实体、100本·实体
发现界面:排行榜、分类、书城
* 如何自行制作并添加书源.
详细功能可查看图片或下载自行体验或自行编译
* 基于面向接口开发的思想,对于书源我设计了如下接口:
* ```java
// 这个接口位于xyz.fycz.myreader.webapi.crawler.base包下
public interface ReadCrawler {
String getSearchLink(); // 书源的搜索url
String getCharset(); // 书源的字符编码
String getSearchCharset(); // 书源搜索关键字的字符编码,和书源的字符编码就行
String getNameSpace(); // 书源主页地址
Boolean isPost(); // 是否以post请求搜索
String getContentFormHtml(String html); // 获取书籍内容规则
ArrayList<Chapter> getChaptersFromHtml(String html); // 获取书籍章节列表规则
ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html); // 搜索书籍规则
}
```
* 了解上述接口的方法,我们就可以开始创建书源了
* 第一步:创建一个书源类实现上述接口,下面以笔趣阁44为例进行说明
* ```java
// 注意:如果搜索书籍页没有图片、最新章节、书籍简介等信息,可以通过实现BookInfoCrawler接口,从书籍详情页获取
public class BiQuGe44ReadCrawler implements ReadCrawler, BookInfoCrawler {
//网站主页地址
public static final String NAME_SPACE = "https://www.wqge.cc";
/*
搜索url,搜索关键词以{key}进行占位
如果是post请求,以“,”分隔url,“,”前是搜索地址,“,”后是请求体,搜索关键词同样以{key}占位
例如:"https://www.9txs.com/search.html,searchkey={key}"
*/
public static final String NOVEL_SEARCH = "https://www.wqge.cc/modules/article/search.php?searchkey={key}";
// 书源字符编码
public static final String CHARSET = "GBK";
// 书源搜索关键词编码
public static final String SEARCH_CHARSET = "utf-8";
@Override
public String getSearchLink() {
return NOVEL_SEARCH;
}
@Override
public String getCharset() {
return CHARSET;
}
@Override
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public Boolean isPost() {
return false;
}
@Override
public String getSearchCharset() {
return SEARCH_CHARSET;
}
/**
* 从html中获取章节正文
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Document doc = Jsoup.parse(html);
Element divContent = doc.getElementById("content");
if (divContent != null) {
String content = Html.fromHtml(divContent.html()).toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ");
return content;
} else {
return "";
}
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
String readUrl = doc.select("meta[property=og:novel:read_url]").attr("content");
Element divList = doc.getElementById("list");
String lastTile = null;
int i = 0;
Elements elementsByTag = divList.getElementsByTag("dd");
for (int j = 9; j < elementsByTag.size(); j++) {
Element dd = elementsByTag.get(j);
Elements as = dd.getElementsByTag("a");
if (as.size() > 0) {
Element a = as.get(0);
String title = a.text() ;
if (!StringHelper.isEmpty(lastTile) && title.equals(lastTile)) {
continue;
}
Chapter chapter = new Chapter();
chapter.setNumber(i++);
chapter.setTitle(title);
String url = readUrl + a.attr("href");
chapter.setUrl(url);
chapters.add(chapter);
lastTile = title;
}
}
return chapters;
}
/**
* 从搜索html中得到书列表
* @param html
* @return
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
Elements divs = doc.getElementsByTag("table");
Element div = divs.get(0);
Elements elementsByTag = div.getElementsByTag("tr");
for (int i = 1; i < elementsByTag.size(); i++) {
Element element = elementsByTag.get(i);
Book book = new Book();
Elements info = element.getElementsByTag("td");
book.setName(info.get(0).text());
book.setChapterUrl(NAME_SPACE + info.get(0).getElementsByTag("a").attr("href"));
book.setAuthor(info.get(2).text());
book.setNewestChapterTitle(info.get(1).text());
book.setSource(BookSource.biquge44.toString());
// SearchBookBean用于合并相同书籍
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
return books;
}
/**
* 获取书籍详细信息
* @param book
*/
public Book getBookInfo(String html, Book book){
Document doc = Jsoup.parse(html);
Element img = doc.getElementById("fmimg");
book.setImgUrl(img.getElementsByTag("img").get(0).attr("src"));
Element desc = doc.getElementById("intro");
book.setDesc(desc.getElementsByTag("p").get(0).text());
Element type = doc.getElementsByClass("con_top").get(0);
book.setType(type.getElementsByTag("a").get(2).text());
return book;
}
}
```
* 第二步:注册书源信息。有两个地方需要注册:
* 1)在xyz.fycz.myreader.enums.BookSource类(这是个枚举类型)中添加你的书源的命名以及书源名称,例如:
* ```java
biquge44("笔趣阁44") // biquge44是书源的命名,笔趣阁44是书源名称
```
* 2)在app/src/main/resources/crawler.properties配置文件中添加书源类信息,例如:
* ```java
// biquge44书源的命名,与BookSource中的命名一致,xyz.fycz.myreader.webapi.crawler.read.BiQuGe44ReadCrawler是书源类的完整路径
biquge44=xyz.fycz.myreader.webapi.crawler.read.BiQuGe44ReadCrawler
```
* 第三步:启用书源(新增的书源默认禁用)。只需在软件内-我的-设置-禁用书源中取消该书源的禁用即可。
#### 二、关于发现界面
* 软件内置的两个发现源:
* 某点的排行榜、分类,全本小说网
* 制作发现源方法与书源类似,在此不再赘述
demo下载:https://fycz.lanzoui.com/iBofFh42pxg
如有问题请加QQ群:1085028304
![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/1.png)
![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/2.png)
@ -23,4 +204,3 @@ demo下载:https://fycz.lanzoui.com/iBofFh42pxg
![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/7.png)
![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/8.png)
![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/9.png)
![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/10.png)

@ -138,6 +138,9 @@ dependencies {
//RxAndroid
implementation 'io.reactivex.rxjava2:rxjava:2.2.19'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
//ImmersionBar
implementation 'com.gyf.immersionbar:immersionbar:3.0.0'
}
greendao {

@ -110,10 +110,6 @@
</activity>
<activity android:name=".ui.activity.FontsActivity"/>
<activity android:name=".ui.activity.LoginActivity"/>
<activity android:name=".ui.activity.RegisterActivity"/>
<activity android:name=".ui.activity.CatalogActivity"/>
<activity android:name=".ui.activity.AboutActivity"/>

@ -1,3 +1,16 @@
2020.11.03
风月读书v1.20.1110311
1、修复已知bug
2、新增WebDav备份教程
2020.10.22
风月读书v1.20.102217
1、优化阅读界面菜单
2、新增阅读内容拷贝对话框
3、优化备份与恢复(支持备份书签、搜索历史)
4、新增WebDav服务(可将书籍备份至WebDav服务器)
5、修复已知问题
2020.10.03
风月读书v1.20.100315
1、优化搜索书籍高频刷新的问题

@ -0,0 +1,3 @@
1、 正确填写WebDAV 服务器地址、WebDAV 账号、WebDAV 密码;(要获得这三项的信息,需要注册一个坚果云账号,如果直接在手机上注册,坚果云会让你下载app,过程比较麻烦,为了一步到位,最好是在电脑上打开这个注册链接:https://www.jianguoyun.com/d/signup;注册后,进入坚果云;点击右上角账户名处选择 “账户信息”,然后选择“安全选项”;在“安全选项” 中找到“第三方应用管理”,并选择“添加应用”,输入名称如“阅读”后,会生成密码,选择完成;其中https://dav.jianguoyun.com/dav/就是填入“WebDAV 服务器地址”的内容,“使用情况”后面的邮箱地址就是你的“WebDAV 账号”,点击显示密码后得到的密码就是你的“WebDAV 密码”。)
2、 无需操作,APP默认每天自动云备份一次。

@ -41,7 +41,6 @@ import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.ui.dialog.APPDownloadTip;
@ -72,13 +71,9 @@ public class MyApplication extends Application {
createNotificationChannel();
}
mFixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());//初始化线程池
BaseActivity.setCloseAntiHijacking(true);
initNightTheme();
}
public void initNightTheme() {
if (isNightFS()){
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
@ -247,49 +242,6 @@ public class MyApplication extends Application {
/**
* 检查更新
*/
/*public static void checkVersionByServer(final Activity activity, final boolean isManualCheck) {
MyApplication.getApplication().newThread(new Runnable() {
@Override
public void run() {
Document doc = null;
try {
doc = Jsoup.connect("https://novel.fycz.xyz/app/update.html").get();
int newestVersion = 0;
String updateContent = "";
String downloadLink = null;
boolean isForceUpdate = false;
StringBuilder s = new StringBuilder();
assert doc != null;
Elements nodes = doc.getElementsByClass("secd-rank-list");
newestVersion = Integer.valueOf(nodes.get(0).getElementsByTag("a").get(1).text());
downloadLink = nodes.get(0).getElementsByTag("a").get(1).attr("href");
updateContent = nodes.get(0).getElementsByTag("a").get(2).text();
isForceUpdate = Boolean.parseBoolean(nodes.get(0).getElementsByTag("a").get(3).text());
String[] updateContents = updateContent.split("/");
for (String string : updateContents) {
s.append(string);
s.append("\n");
}
int versionCode = getVersionCode();
if (newestVersion > versionCode) {
MyApplication m = new MyApplication();
Setting setting = SysManager.getSetting();
if (isManualCheck || setting.getNewestVersionCode() < newestVersion || isForceUpdate) {
setting.setNewestVersionCode(newestVersion);
SysManager.saveSetting(setting);
int i = setting.getNewestVersionCode();
m.updateApp(activity, downloadLink, newestVersion, s.toString(), isForceUpdate);
}
} else if (isManualCheck) {
TextHelper.showText("已经是最新版本!");
}
} catch (IOException e) {
e.printStackTrace();
TextHelper.showText("无网络连接!");
}
}
});
}*/
public static void checkVersionByServer(final AppCompatActivity activity, final boolean isManualCheck,
final BookcaseFragment mBookcaseFragment) {
MyApplication.getApplication().newThread(() -> {
@ -437,25 +389,4 @@ public class MyApplication extends Application {
return mActivity == null || mActivity.isFinishing() || mActivity.isDestroyed();
}
/****************
*
* 发起添加群流程群号风月读书交流群(1085028304) key 8PIOnHFuH6A38hgxvD_Rp2Bu-Ke1ToBn
* 调用 joinQQGroup(8PIOnHFuH6A38hgxvD_Rp2Bu-Ke1ToBn) 即可发起手Q客户端申请加群 风月读书交流群(1085028304)
*
* @param key 由官网生成的key
* @return 返回true表示呼起手Q成功返回false表示呼起失败
******************/
public static boolean joinQQGroup(Context context, String key) {
Intent intent = new Intent();
intent.setData(Uri.parse("mqqopensdkapi://bizAgent/qm/qr?url=http%3A%2F%2Fqm.qq.com%2Fcgi-bin%2Fqm%2Fqr%3Ffrom%3Dapp%26p%3Dandroid%26jump_from%3Dwebapi%26k%3D" + key));
// 此Flag可根据具体产品需要自定义,如设置,则在加群界面按返回,返回手Q主界面,不设置,按返回会返回到呼起产品界面 //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
try {
context.startActivity(intent);
return true;
} catch (Exception e) {
// 未安装手Q或安装的版本不支持
return false;
}
}
}

@ -5,10 +5,9 @@ import xyz.fycz.myreader.enums.BookcaseStyle;
import xyz.fycz.myreader.enums.Font;
import xyz.fycz.myreader.enums.Language;
import xyz.fycz.myreader.enums.ReadStyle;
import xyz.fycz.myreader.model.storage.Backup;
import xyz.fycz.myreader.util.CacheHelper;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.widget.page.PageMode;
import static xyz.fycz.myreader.application.MyApplication.getVersionCode;
@ -79,13 +78,7 @@ public class SysManager {
public static void resetSetting(){
Setting setting = getSetting();
/*setting.setVolumeTurnPage(true);
setting.setMatchChapter(true);
setting.setRefreshWhenStart(true);
setting.setOpenBookStore(true);
setting.setResetScreen(3);*/
ReadCrawlerUtil.resetReaderCrawlers();
Backup.INSTANCE.backup(MyApplication.getmContext(), APPCONST.BACKUP_FILE_DIR,null, false);
setting.setSettingVersion(APPCONST.SETTING_VERSION);
saveSetting(setting);
}
}

@ -1,115 +1,192 @@
package xyz.fycz.myreader.base;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.view.Menu;
import android.view.MenuItem;
import androidx.annotation.LayoutRes;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.Toolbar;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import xyz.fycz.myreader.ActivityManage;
import xyz.fycz.myreader.util.Anti_hijackingUtils;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.util.StatusBarUtil;
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public abstract class BaseActivity extends AppCompatActivity {
private static final int INVALID_VAL = -1;
public class BaseActivity extends AppCompatActivity {
public static int width = 0;
public static int height = 0;
public static boolean home;
public static boolean back;
private boolean catchHomeKey = false;
private boolean disallowAntiHijacking;//暂停防界面劫持
protected CompositeDisposable mDisposable;
//ButterKnife
private Toolbar mToolbar;
private static boolean closeAntiHijacking;//关闭防界面劫持
private Unbinder unbinder;
private int curNightMode;
/****************************abstract area*************************************/
private InputMethodManager mInputMethodManager; //输入管理器
@LayoutRes
protected abstract int getContentId();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/************************init area************************************/
protected void addDisposable(Disposable d){
if (mDisposable == null){
mDisposable = new CompositeDisposable();
}
mDisposable.add(d);
}
//将每一个Activity都加入activity管理器
ActivityManage.addActivity(this);
Log.d("ActivityName: ",getLocalClassName());
DisplayMetrics dm = new DisplayMetrics();
//获取屏幕宽高
if(height == 0 || width == 0){
getWindowManager().getDefaultDisplay().getMetrics(dm);
width = dm.widthPixels;
height = dm.heightPixels;
/**
* 配置Toolbar
* @param toolbar
*/
protected void setUpToolbar(Toolbar toolbar){
}
mInputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
protected void initData(Bundle savedInstanceState){
}
/**
* 初始化零件
*/
protected void initWidget() {
public static void setCloseAntiHijacking(boolean closeAntiHijacking) {
BaseActivity.closeAntiHijacking = closeAntiHijacking;
}
/**
* 初始化点击事件
*/
protected void initClick(){
}
/**
* 逻辑使用区
*/
protected void processLogic(){
}
/**
* @return 是否夜间模式
*/
protected boolean isNightTheme() {
return !SysManager.getSetting().isDayStyle();
}
@Override
protected void onDestroy() {
ActivityManage.removeActivity(this);
super.onDestroy();
/**
* 设置夜间模式
* @param isNightMode
*/
protected void setNightTheme(boolean isNightMode) {
Setting setting = SysManager.getSetting();
setting.setDayStyle(!isNightMode);
MyApplication.getApplication().initNightTheme();
}
/*************************lifecycle area*****************************************************/
@Override
protected void onPause() {
if (!disallowAntiHijacking && !closeAntiHijacking) {
Anti_hijackingUtils.getinstance().onPause(this);//防界面劫持提示任务
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initTheme();
ActivityManage.addActivity(this);
setContentView(getContentId());
unbinder = ButterKnife.bind(this);
initData(savedInstanceState);
initToolbar();
initWidget();
initClick();
processLogic();
}
private void initToolbar(){
//更严谨是通过反射判断是否存在Toolbar
mToolbar = findViewById(R.id.toolbar);
if (mToolbar != null){
supportActionBar(mToolbar);
setUpToolbar(mToolbar);
}
super.onPause();
}
@Override
protected void onResume() {
if (!closeAntiHijacking) {
Anti_hijackingUtils.getinstance().onResume(this);//注销防界面劫持提示任务
}
BaseActivity.home = false;
BaseActivity.back = false;
disallowAntiHijacking = false;
super.onResume();
if (isThemeChange()){
recreate();
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK){
back = true;//以便于判断是否按返回键触发界面劫持提示
protected void onDestroy() {
super.onDestroy();
ActivityManage.removeActivity(this);
unbinder.unbind();
if (mDisposable != null){
mDisposable.dispose();
}
return super.onKeyDown(keyCode, event);
}
/**
* 初始化主题
*/
public void initTheme() {
//if (isNightTheme()) {
//setTheme(R.style.AppNightTheme);
curNightMode = AppCompatDelegate.getDefaultNightMode();
/*} else {
//curNightMode = false;
//setTheme(R.style.AppDayTheme);
}*/
}
public void setDisallowAntiHijacking(boolean disallowAntiHijacking) {
this.disallowAntiHijacking = disallowAntiHijacking;
protected boolean isThemeChange(){
return curNightMode != AppCompatDelegate.getDefaultNightMode();
}
/**************************used method area*******************************************/
protected void startActivity(Class<? extends AppCompatActivity> activity){
Intent intent = new Intent(this, activity);
startActivity(intent);
}
protected ActionBar supportActionBar(Toolbar toolbar){
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null){
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
}
mToolbar.setNavigationOnClickListener(
(v) -> finish()
);
return actionBar;
}
/**
* 设置状态栏颜色
* @param colorId
*/
public void setStatusBar(int colorId, boolean dark){
protected void setStatusBarColor(int statusColor, boolean dark){
//沉浸式代码配置
//当FitsSystemWindows设置 true 时,会在屏幕最上方预留出状态栏高度的 padding
StatusBarUtil.setRootViewFitsSystemWindows(this, true);
//设置状态栏透明
StatusBarUtil.setTranslucentStatus(this);
if (colorId != 0) {
StatusBarUtil.setStatusBarColor(this, getResources().getColor(colorId));
}
StatusBarUtil.setStatusBarColor(this, getResources().getColor(statusColor));
//一般的手机的状态栏文字和图标都是白色的, 可如果你的应用也是纯白色的, 或导致状态栏文字看不清
//所以如果你是这种情况,请使用以下代码, 设置状态使用深色文字图标风格, 否则你可以选择性注释掉这个if内容
if (!dark) {
@ -119,11 +196,49 @@ public class BaseActivity extends AppCompatActivity {
StatusBarUtil.setStatusBarColor(this, 0x55000000);
}
}
} /**
* 设置MENU图标颜色
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
for (int i = 0; i < menu.size(); i++) {
Drawable drawable = menu.getItem(i).getIcon();
if (drawable != null) {
drawable.mutate();
drawable.setColorFilter(getColor(R.color.textPrimary), PorterDuff.Mode.SRC_ATOP);
}
}
return super.onCreateOptionsMenu(menu);
}
@SuppressLint("PrivateApi")
@SuppressWarnings("unchecked")
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
if (menu != null) {
//展开菜单显示图标
if (menu.getClass().getSimpleName().equalsIgnoreCase("MenuBuilder")) {
try {
Method method = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
method.setAccessible(true);
method.invoke(menu, true);
method = menu.getClass().getDeclaredMethod("getNonActionItems");
ArrayList<MenuItem> menuItems = (ArrayList<MenuItem>) method.invoke(menu);
if (!menuItems.isEmpty()) {
for (MenuItem menuItem : menuItems) {
Drawable drawable = menuItem.getIcon();
if (drawable != null) {
drawable.mutate();
drawable.setColorFilter(getResources().getColor(R.color.textPrimary), PorterDuff.Mode.SRC_ATOP);
}
}
}
} catch (Exception ignored) {
}
}
public InputMethodManager getmInputMethodManager() {
return mInputMethodManager;
}
return super.onMenuOpened(featureId, menu);
}
}

@ -1,244 +0,0 @@
package xyz.fycz.myreader.base;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import androidx.annotation.LayoutRes;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.Toolbar;
import butterknife.ButterKnife;
import butterknife.Unbinder;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import xyz.fycz.myreader.ActivityManage;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.util.StatusBarUtil;
import java.lang.reflect.Method;
import java.util.ArrayList;
/**
* @author fengyue
* @date 2020/8/12 20:02
*/
public abstract class BaseActivity2 extends AppCompatActivity {
private static final int INVALID_VAL = -1;
protected CompositeDisposable mDisposable;
//ButterKnife
private Toolbar mToolbar;
private Unbinder unbinder;
private int curNightMode;
/****************************abstract area*************************************/
@LayoutRes
protected abstract int getContentId();
/************************init area************************************/
protected void addDisposable(Disposable d){
if (mDisposable == null){
mDisposable = new CompositeDisposable();
}
mDisposable.add(d);
}
/**
* 配置Toolbar
* @param toolbar
*/
protected void setUpToolbar(Toolbar toolbar){
}
protected void initData(Bundle savedInstanceState){
}
/**
* 初始化零件
*/
protected void initWidget() {
}
/**
* 初始化点击事件
*/
protected void initClick(){
}
/**
* 逻辑使用区
*/
protected void processLogic(){
}
/**
* @return 是否夜间模式
*/
protected boolean isNightTheme() {
return !SysManager.getSetting().isDayStyle();
}
/**
* 设置夜间模式
* @param isNightMode
*/
protected void setNightTheme(boolean isNightMode) {
Setting setting = SysManager.getSetting();
setting.setDayStyle(!isNightMode);
MyApplication.getApplication().initNightTheme();
}
/*************************lifecycle area*****************************************************/
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initTheme();
ActivityManage.addActivity(this);
setContentView(getContentId());
initData(savedInstanceState);
unbinder = ButterKnife.bind(this);
initToolbar();
initWidget();
initClick();
processLogic();
}
private void initToolbar(){
//更严谨是通过反射判断是否存在Toolbar
mToolbar = findViewById(R.id.toolbar);
if (mToolbar != null){
supportActionBar(mToolbar);
setUpToolbar(mToolbar);
}
}
@Override
protected void onResume() {
super.onResume();
if (isThemeChange()){
recreate();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityManage.removeActivity(this);
unbinder.unbind();
if (mDisposable != null){
mDisposable.dispose();
}
}
/**
* 初始化主题
*/
public void initTheme() {
//if (isNightTheme()) {
//setTheme(R.style.AppNightTheme);
curNightMode = AppCompatDelegate.getDefaultNightMode();
/*} else {
//curNightMode = false;
//setTheme(R.style.AppDayTheme);
}*/
}
protected boolean isThemeChange(){
return curNightMode != AppCompatDelegate.getDefaultNightMode();
}
/**************************used method area*******************************************/
protected void startActivity(Class<? extends AppCompatActivity> activity){
Intent intent = new Intent(this, activity);
startActivity(intent);
}
protected ActionBar supportActionBar(Toolbar toolbar){
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null){
actionBar.setDisplayHomeAsUpEnabled(true);
actionBar.setDisplayShowHomeEnabled(true);
}
mToolbar.setNavigationOnClickListener(
(v) -> finish()
);
return actionBar;
}
protected void setStatusBarColor(int statusColor, boolean dark){
//沉浸式代码配置
//当FitsSystemWindows设置 true 时,会在屏幕最上方预留出状态栏高度的 padding
StatusBarUtil.setRootViewFitsSystemWindows(this, true);
//设置状态栏透明
StatusBarUtil.setTranslucentStatus(this);
StatusBarUtil.setStatusBarColor(this, getResources().getColor(statusColor));
//一般的手机的状态栏文字和图标都是白色的, 可如果你的应用也是纯白色的, 或导致状态栏文字看不清
//所以如果你是这种情况,请使用以下代码, 设置状态使用深色文字图标风格, 否则你可以选择性注释掉这个if内容
if (!dark) {
if (!StatusBarUtil.setStatusBarDarkTheme(this, true)) {
//如果不支持设置深色风格 为了兼容总不能让状态栏白白的看不清, 于是设置一个状态栏颜色为半透明,
//这样半透明+白=灰, 状态栏的文字能看得清
StatusBarUtil.setStatusBarColor(this, 0x55000000);
}
}
} /**
* 设置MENU图标颜色
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
for (int i = 0; i < menu.size(); i++) {
Drawable drawable = menu.getItem(i).getIcon();
if (drawable != null) {
drawable.mutate();
drawable.setColorFilter(getColor(R.color.textPrimary), PorterDuff.Mode.SRC_ATOP);
}
}
return super.onCreateOptionsMenu(menu);
}
@SuppressLint("PrivateApi")
@SuppressWarnings("unchecked")
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
if (menu != null) {
//展开菜单显示图标
if (menu.getClass().getSimpleName().equalsIgnoreCase("MenuBuilder")) {
try {
Method method = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
method.setAccessible(true);
method.invoke(menu, true);
method = menu.getClass().getDeclaredMethod("getNonActionItems");
ArrayList<MenuItem> menuItems = (ArrayList<MenuItem>) method.invoke(menu);
if (!menuItems.isEmpty()) {
for (MenuItem menuItem : menuItems) {
Drawable drawable = menuItem.getIcon();
if (drawable != null) {
drawable.mutate();
drawable.setColorFilter(getResources().getColor(R.color.textPrimary), PorterDuff.Mode.SRC_ATOP);
}
}
}
} catch (Exception ignored) {
}
}
}
return super.onMenuOpened(featureId, menu);
}
}

@ -16,7 +16,7 @@ import java.util.List;
* @date 2020/8/12 20:02
*/
public abstract class BaseTabActivity extends BaseActivity2 {
public abstract class BaseTabActivity extends BaseActivity {
/**************View***************/
@BindView(R.id.tab_tl_indicator)
protected TabLayout mTlIndicator;

@ -13,7 +13,7 @@ public class APPCONST {
public static String publicKey = "";//服务端公钥
public static String privateKey;//app私钥
public final static String s = "11940364935628058505";
public static final String KEY = "";
public static final String KEY = "readerByFengyue";
public static final String ALARM_SCHEDULE_MSG = "alarm_schedule_msg";
@ -79,7 +79,7 @@ public class APPCONST {
public static final int PERMISSIONS_REQUEST_STORAGE = 10001;
//设置版本号
public static final int SETTING_VERSION = 5;
public static final int SETTING_VERSION = 6;
public static final String FORMAT_FILE_DATE = "yyyy-MM-dd";

@ -1,129 +0,0 @@
package xyz.fycz.myreader.model.backup;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.util.IOUtils;
import xyz.fycz.myreader.util.utils.FileUtils;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author fengyue
* @date 2020/4/25 9:07
*/
public class BackupAndRestore {
BookService mBookService = BookService.getInstance();
/**
* 备份书架
* @return 是否备份成功
*/
public boolean backup(String backupName){
List<Book> books = mBookService.getAllBooks();
StringBuilder s = new StringBuilder();
for (Book book : books) {
s.append(book);
s.append(",\n");
}
s.deleteCharAt(s.lastIndexOf(","));
File booksFile = FileUtils.getFile(APPCONST.FILE_DIR + backupName + "/books" + FileUtils.SUFFIX_FY);
File settingFile = FileUtils.getFile(APPCONST.FILE_DIR + backupName + "/setting" + FileUtils.SUFFIX_FY);
BufferedWriter bw = null;
ObjectOutputStream oos = null;
try {
bw = new BufferedWriter(new FileWriter(booksFile));
bw.write(s.toString());
bw.flush();
oos = new ObjectOutputStream(new FileOutputStream(settingFile));
oos.writeObject(SysManager.getSetting());
oos.flush();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
IOUtils.close(bw, oos);
}
}
/**
* 恢复书架
* @return 是否恢复成功
*/
public boolean restore(String backupName) {
File booksFile = FileUtils.getFile(APPCONST.FILE_DIR + backupName + "/books" + FileUtils.SUFFIX_FY);
File settingFile = FileUtils.getFile(APPCONST.FILE_DIR + backupName + "/setting" + FileUtils.SUFFIX_FY);
if (!booksFile.exists() || !settingFile.exists()){
return false;
}
BufferedReader br = null;
ObjectInputStream ois = null;
try {
br = new BufferedReader(new FileReader(booksFile));
String tem = "";
StringBuilder s = new StringBuilder();
while ((tem = br.readLine()) != null){
s.append(tem).append("\n");
}
String[] sBooks = s.toString().split("\\},");
List<Book> books = new ArrayList<>();
for (String sBook : sBooks){
sBook.replace("{", "");
sBook.replace("}", "");
String[] sBookFields = sBook.split(",\n");
for (int i = 0; i < sBookFields.length; i++) {
sBookFields[i] = sBookFields[i].substring(sBookFields[i].indexOf("'") + 1, sBookFields[i].lastIndexOf("'"));
}
String source = "null";
boolean isCloseUpdate = false;
boolean isDownloadAll = true;
String group = "allBook";
String infoUrl = "";
if(!sBookFields[2].contains("novel.fycz.xyz")){
source = sBookFields[17];
}
if ("本地书籍".equals(sBookFields[4])){
sBookFields[15] = "0";
}
if (sBookFields.length >= 19){
isCloseUpdate = Boolean.parseBoolean(sBookFields[18]);
}
if (sBookFields.length >= 20){
isDownloadAll = Boolean.parseBoolean(sBookFields[19]);
}
if (sBookFields.length >= 21){
group = sBookFields[20];
}
if (sBookFields.length >= 22){
infoUrl = sBookFields[21];
}
Book book = new Book(sBookFields[0], sBookFields[1], sBookFields[2], infoUrl, sBookFields[3], sBookFields[4],
sBookFields[5], sBookFields[6], sBookFields[7], sBookFields[8], sBookFields[9], sBookFields[10],
sBookFields[11], Integer.parseInt(sBookFields[12]), Integer.parseInt(sBookFields[13]),
Integer.parseInt(sBookFields[14]), Integer.parseInt(sBookFields[15]), Integer.parseInt(sBookFields[16])
, source, isCloseUpdate, isDownloadAll, group, 0);
books.add(book);
}
mBookService.deleteAllBooks();
mBookService.addBooks(books);
ois = new ObjectInputStream(new FileInputStream(settingFile));
Object obj = ois.readObject();
if (obj instanceof Setting){
Setting setting = (Setting) obj;
SysManager.saveSetting(setting);
}
return true;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return false;
} finally {
IOUtils.close(br, ois);
}
}
}

@ -1,333 +0,0 @@
package xyz.fycz.myreader.model.backup;
import io.reactivex.annotations.NonNull;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.model.storage.Backup;
import xyz.fycz.myreader.model.storage.Restore;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.util.*;
import xyz.fycz.myreader.util.utils.FileUtils;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
/**
* @author fengyue
* @date 2020/4/26 11:03
*/
public class UserService {
/**
* 登录
* @param userLoginInfo 用户名输入的用户名和密码等登录信息
* @return 是否成功登录
*/
public static void login(final Map<String, String> userLoginInfo, final ResultCallback resultCallback) {
MyApplication.getApplication().newThread(() -> {
HttpURLConnection conn = null;
try {
URL url = new URL(URLCONST.APP_WEB_URL + "login");
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setConnectTimeout(60 * 1000);
conn.setReadTimeout(60 * 1000);
conn.setDoInput(true);
conn.setDoOutput(true);
String params = "username=" + userLoginInfo.get("loginName") +
"&password=" + userLoginInfo.get("loginPwd") + makeSignalParam();
// 获取URLConnection对象对应的输出流
PrintWriter out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(params);
// flush输出流的缓冲
out.flush();
InputStream in = conn.getInputStream();
BufferedReader bw = new BufferedReader(new InputStreamReader(in, "utf-8"));
StringBuilder sb = new StringBuilder();
String line = bw.readLine();
while (line != null) {
sb.append(line);
line = bw.readLine();
}
resultCallback.onFinish(sb.toString(), 1);
} catch (IOException e) {
e.printStackTrace();
resultCallback.onError(e);
}finally {
if (conn != null) {
conn.disconnect();
}
}
});
}
public static void register(final Map<String, String> userRegisterInfo, final ResultCallback resultCallback) {
MyApplication.getApplication().newThread(() -> {
HttpURLConnection conn = null;
try {
URL url = new URL(URLCONST.APP_WEB_URL + "reg");
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
String params = "username=" + userRegisterInfo.get("username") + "&password=" +
CyptoUtils.encode(APPCONST.KEY, userRegisterInfo.get("password")) + "&key=" +
CyptoUtils.encode(APPCONST.KEY, APPCONST.publicKey) + makeSignalParam();
// 获取URLConnection对象对应的输出流
PrintWriter out = new PrintWriter(conn.getOutputStream());
// 发送请求参数
out.print(params);
// flush输出流的缓冲
out.flush();
BufferedReader bw = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
StringBuilder sb = new StringBuilder();
String line = bw.readLine();
while (line != null) {
sb.append(line);
line = bw.readLine();
}
resultCallback.onFinish(sb.toString(), 1);
} catch (IOException e) {
e.printStackTrace();
resultCallback.onError(e);
} finally {
if (conn != null) {
conn.disconnect();
}
}
});
}
/**
* 写配置
* @param userLoginInfo
* @return
*/
public static boolean writeConfig(Map<String,String> userLoginInfo){
FileOutputStream fos = null;
try {
fos = MyApplication.getApplication().openFileOutput("userConfig.fy", MyApplication.getApplication().MODE_PRIVATE);
String userInfo = "username='" + userLoginInfo.get("loginName") + "',\npassword='" + userLoginInfo.get("loginPwd") + "'";
byte[] bs = userInfo.getBytes();
fos.write(bs);
//写完后一定要刷新
fos.flush();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
} finally {
IOUtils.close(fos);
}
}
/**
* 读配置
* @return
*/
public static Map<String,String> readConfig(){
File file = MyApplication.getApplication().getFileStreamPath("userConfig.fy");
if (!file.exists()){
return null;
}
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(file));
String tem;
StringBuilder config = new StringBuilder();
while ((tem = br.readLine()) != null){
config.append(tem);
}
String[] user = config.toString().split(",");
String userName = user[0].substring(user[0].indexOf("'") + 1, user[0].lastIndexOf("'"));
String password = user[1].substring(user[1].indexOf("'") + 1, user[1].lastIndexOf("'"));
Map<String,String> userInfo = new HashMap<>();
userInfo.put("userName", userName);
userInfo.put("password", password);
return userInfo;
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtils.close(br);
}
return null;
}
public static void writeUsername(String username){
File file = FileUtils.getFile(APPCONST.QQ_DATA_DIR + "user");
BufferedWriter bw = null;
try {
bw = new BufferedWriter(new FileWriter(file));
bw.write(username);
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtils.close(bw);
}
}
public static String readUsername(){
File file = new File(APPCONST.QQ_DATA_DIR + "user");
if (!file.exists()){
return "";
}
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(file));
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
return "";
} finally {
IOUtils.close(br);
}
}
/**
* 网络备份
* @return
*/
public static void webBackup(ResultCallback rc){
Map<String,String> userInfo = readConfig();
if (userInfo == null){
rc.onFinish(false, 0);
}
Backup.INSTANCE.backup(MyApplication.getmContext(), APPCONST.FILE_DIR + "webBackup/", new Backup.CallBack() {
@Override
public void backupSuccess() {
MyApplication.getApplication().newThread(() ->{
File inputFile = FileUtils.getFile(APPCONST.FILE_DIR + "webBackup");
if (!inputFile.exists()) {
rc.onFinish(false, 0);
}
File zipFile = FileUtils.getFile(APPCONST.FILE_DIR + "webBackup.zip");
FileInputStream fis = null;
HttpURLConnection conn = null;
try {
//压缩文件
ZipUtils.zipFile(inputFile, zipFile);
fis = new FileInputStream(zipFile);
URL url = new URL(URLCONST.APP_WEB_URL + "bak?username=" + userInfo.get("userName") +
makeSignalParam());
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-type", "multipart/form-data");
conn.setDoInput(true);
conn.setDoOutput(true);
OutputStream out = conn.getOutputStream();
byte[] bytes = new byte[1024];
int len = -1;
while ((len = fis.read(bytes)) != -1){
out.write(bytes, 0, len);
}
out.flush();
zipFile.delete();
BufferedReader bw = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
StringBuilder sb = new StringBuilder();
String line = bw.readLine();
while (line != null) {
sb.append(line);
line = bw.readLine();
}
String[] info = sb.toString().split(":");
int code = Integer.parseInt(info[0].trim());
rc.onFinish(code == 104, 0);
} catch (Exception e) {
e.printStackTrace();
rc.onError(e);
} finally {
IOUtils.close(fis);
if (conn != null) {
conn.disconnect();
}
}
});
}
@Override
public void backupError(@NonNull String msg) {
ToastUtils.showError(msg);
rc.onFinish(false, 0);
}
}, false);
}
/**
* 网络恢复
* @return
*/
public static void webRestore(ResultCallback rc){
Map<String,String> userInfo = readConfig();
if (userInfo == null){
rc.onFinish(false, 0);
}
FileOutputStream fos = null;
File zipFile = FileUtils.getFile(APPCONST.FILE_DIR + "webBackup.zip");
HttpURLConnection conn = null;
try {
URL url = new URL(URLCONST.APP_WEB_URL + "ret?username=" + userInfo.get("userName") +
makeSignalParam());
conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoInput(true);
InputStream is = conn.getInputStream();
fos = new FileOutputStream(zipFile);
//一边读,一边写
byte[] bytes = new byte[512];
int readCount = 0;
while ((readCount = is.read(bytes)) != -1) {
fos.write(bytes,0, readCount);
}
//刷新,输出流一定要刷新
fos.flush();
if (zipFile.length() == 0){
zipFile.delete();
rc.onFinish(false, 0);
}
ZipUtils.unzipFile(zipFile.getAbsolutePath(), APPCONST.FILE_DIR);
Restore.INSTANCE.restore(APPCONST.FILE_DIR + "webBackup/", new Restore.CallBack() {
@Override
public void restoreSuccess() {
zipFile.delete();
rc.onFinish(true, 0);
}
@Override
public void restoreError(@NonNull String msg) {
ToastUtils.showError(msg);
rc.onFinish(false, 0);
}
});
} catch (Exception e) {
e.printStackTrace();
rc.onError(e);
}finally {
IOUtils.close(fos);
if (conn != null) {
conn.disconnect();
}
}
}
private static String makeSignalParam(){
return "&signal=" + AppInfoUtils.getSingInfo(MyApplication.getmContext(),
MyApplication.getApplication().getPackageName(), AppInfoUtils.SHA1);
}
/**
* 判断是否登录
* @return
*/
public static boolean isLogin(){
File file = MyApplication.getApplication().getFileStreamPath("userConfig.fy");
return file.exists();
}
}

@ -53,8 +53,9 @@ object WebDavHelp {
if (initWebDav()) {
var files = WebDav(url + "FYReader/").listFiles()
files = files.reversed()
for (index: Int in 0 until min(10, files.size)) {
files[index].displayName?.let {
//for (index: Int in 0 until min(10, files.size)) {
for (element in files) {
element.displayName?.let {
names.add(it)
}
}

@ -11,7 +11,7 @@ import androidx.cardview.widget.CardView;
import butterknife.BindView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.util.ShareUtils;
@ -22,7 +22,7 @@ import xyz.fycz.myreader.util.ToastUtils;
* @author fengyue
* @date 2020/9/18 22:21
*/
public class AboutActivity extends BaseActivity2 {
public class AboutActivity extends BaseActivity {
@BindView(R.id.tv_version_name)
TextView tvVersionName;
@BindView(R.id.vm_author)
@ -33,8 +33,6 @@ public class AboutActivity extends BaseActivity2 {
CardView vmUpdate;
@BindView(R.id.vw_update_log)
CardView vmUpdateLog;
@BindView(R.id.vw_qq)
CardView vmQQ;
@BindView(R.id.vw_git)
CardView vmGit;
@BindView(R.id.vw_disclaimer)
@ -75,16 +73,6 @@ public class AboutActivity extends BaseActivity2 {
SharedPreUtils.getInstance().getString(getString(R.string.downloadLink, URLCONST.LAN_ZOUS_URL))));
vmUpdate.setOnClickListener(v -> MyApplication.checkVersionByServer(this, true, null));
vmUpdateLog.setOnClickListener(v -> DialogCreator.createAssetTipDialog(this, "更新日志", "updatelog.fy"));
vmQQ.setOnClickListener(v -> {
if (!MyApplication.joinQQGroup(this,"8PIOnHFuH6A38hgxvD_Rp2Bu-Ke1ToBn")){
//数据
ClipData mClipData = ClipData.newPlainText("Label", "1085028304");
//把数据设置到剪切板上
assert mClipboardManager != null;
mClipboardManager.setPrimaryClip(mClipData);
ToastUtils.showError("未安装手Q或安装的版本不支持!\n已复制QQ群号,您可自行前往QQ添加!");
}
});
vmGit.setOnClickListener(v -> openIntent(Intent.ACTION_VIEW, getString(R.string.this_github_url)));
vmDisclaimer.setOnClickListener(v -> DialogCreator.createAssetTipDialog(this, "免责声明", "disclaimer.fy"));

@ -18,12 +18,11 @@ import butterknife.BindView;
import butterknife.OnClick;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.ui.dialog.SourceExchangeDialog;
@ -50,7 +49,7 @@ import java.util.ArrayList;
* @author fengyue
* @date 2020/8/17 11:39
*/
public class BookDetailedActivity extends BaseActivity2 {
public class BookDetailedActivity extends BaseActivity {
@BindView(R.id.book_detail_iv_cover)
CoverImageView mIvCover;
/* @BindView(R.id.book_detail_iv_blur_cover)
@ -337,7 +336,7 @@ public class BookDetailedActivity extends BaseActivity2 {
private RequestBuilder<Drawable> defaultCover() {
return Glide.with(this)
.load(R.mipmap.no_image)
.load(R.mipmap.default_cover)
.apply(RequestOptions.bitmapTransform(new BlurTransformation(this, 25)));
}

@ -16,7 +16,7 @@ import butterknife.BindView;
import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
@ -43,7 +43,7 @@ import java.util.List;
* @author fengyue
* @date 2020/9/13 21:11
*/
public class BookstoreActivity extends BaseActivity2 {
public class BookstoreActivity extends BaseActivity {
@BindView(R.id.refresh_layout)
RefreshLayout mRlRefresh;
@BindView(R.id.rv_type_list)

@ -10,7 +10,7 @@ import androidx.viewpager.widget.ViewPager;
import butterknife.BindView;
import com.google.android.material.tabs.TabLayout;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.ui.adapter.TabFragmentPageAdapter;
@ -20,7 +20,7 @@ import xyz.fycz.myreader.ui.fragment.CatalogFragment;
/**
* 书籍目录activity
*/
public class CatalogActivity extends BaseActivity2 {
public class CatalogActivity extends BaseActivity {
@BindView(R.id.catalog_tab)

@ -8,14 +8,12 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.enums.Font;
import xyz.fycz.myreader.ui.adapter.FontsAdapter;
@ -35,7 +33,7 @@ import static xyz.fycz.myreader.util.UriFileUtil.getPath;
* @author fengyue
* @date 2020/9/19 12:04
*/
public class FontsActivity extends BaseActivity2 {
public class FontsActivity extends BaseActivity {
@BindView(R.id.lv_fonts)
ListView lvFonts;
@BindView(R.id.pb_loading)

@ -1,231 +0,0 @@
package xyz.fycz.myreader.ui.activity;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import com.google.android.material.textfield.TextInputLayout;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.model.backup.UserService;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.util.CodeUtil;
import xyz.fycz.myreader.util.CyptoUtils;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* @author fengyue
* @date 2020/9/18 22:27
*/
public class LoginActivity extends BaseActivity2 implements TextWatcher {
@BindView(R.id.et_user)
TextInputLayout user;
@BindView(R.id.et_password)
TextInputLayout password;
@BindView(R.id.bt_login)
Button loginBtn;
@BindView(R.id.tv_register)
TextView tvRegister;
@BindView(R.id.et_captcha)
TextInputLayout etCaptcha;
@BindView(R.id.iv_captcha)
ImageView ivCaptcha;
private String code;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@SuppressLint("HandlerLeak")
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
loginBtn.setEnabled(true);
break;
case 2:
createCaptcha();
break;
}
}
};
@Override
protected int getContentId() {
return R.layout.activity_login;
}
@Override
protected void setUpToolbar(Toolbar toolbar) {
super.setUpToolbar(toolbar);
setStatusBarColor(R.color.colorPrimary, true);
getSupportActionBar().setTitle("登录");
}
@Override
protected void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
}
@Override
protected void initWidget() {
super.initWidget();
mHandler.sendMessage(mHandler.obtainMessage(2));
String username = UserService.readUsername();
user.getEditText().setText(username);
user.getEditText().requestFocus(username.length());
//监听内容改变 -> 控制按钮的点击状态
user.getEditText().addTextChangedListener(this);
password.getEditText().addTextChangedListener(this);
etCaptcha.getEditText().addTextChangedListener(this);
}
@Override
protected void initClick() {
super.initClick();
ivCaptcha.setOnClickListener(v -> mHandler.sendMessage(mHandler.obtainMessage(2)));
loginBtn.setOnClickListener(v -> {
mHandler.sendMessage(mHandler.obtainMessage(2));
if (!code.toLowerCase().equals(etCaptcha.getEditText().getText().toString().toLowerCase())){
DialogCreator.createTipDialog(this, "验证码错误!");
return;
}
if (!NetworkUtils.isNetWorkAvailable()) {
ToastUtils.showError("无网络连接!");
return;
}
ProgressDialog dialog = DialogCreator.createProgressDialog(this, null, "正在登陆...");
loginBtn.setEnabled(false);
final String loginName = user.getEditText().getText().toString().trim();
String loginPwd = password.getEditText().getText().toString();
final Map<String, String> userLoginInfo = new HashMap<>();
userLoginInfo.put("loginName", loginName);
userLoginInfo.put("loginPwd", CyptoUtils.encode(APPCONST.KEY, loginPwd));
//验证用户名和密码
UserService.login(userLoginInfo, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
String result = (String) o;
String[] info = result.split(":");
int resultCode = Integer.parseInt(info[0].trim());
String resultName = info[1].trim();
//最后输出结果
if (resultCode == 102) {
UserService.writeConfig(userLoginInfo);
UserService.writeUsername(loginName);
Intent intent = new Intent();
intent.putExtra("isLogin", true);
setResult(Activity.RESULT_OK, intent);
finish();
ToastUtils.showSuccess(resultName);
} else {
mHandler.sendMessage(mHandler.obtainMessage(1));
dialog.dismiss();
ToastUtils.showWarring(resultName);
}
}
@Override
public void onError(Exception e) {
ToastUtils.showError("登录失败\n" + e.getLocalizedMessage());
mHandler.sendMessage(mHandler.obtainMessage(1));
dialog.dismiss();
}
});
});
tvRegister.setOnClickListener(v -> {
Intent intent = new Intent(LoginActivity.this, RegisterActivity.class);
startActivity(intent);
});
}
public void createCaptcha() {
code = CodeUtil.getInstance().createCode();
Bitmap codeBitmap = CodeUtil.getInstance().createBitmap(code);
ivCaptcha.setImageBitmap(codeBitmap);
}
/**
* 当有控件获得焦点focus 自动弹出键盘
* 1. 点击软键盘的enter键 自动收回键盘
* 2. 代码控制 InputMethodManager
* requestFocus
* showSoftInput:显示键盘 必须先让这个view成为焦点requestFocus
*
* hideSoftInputFromWindow 隐藏键盘
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN){
//隐藏键盘
//1.获取系统输入的管理器
InputMethodManager inputManager =
(InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
//2.隐藏键盘
inputManager.hideSoftInputFromWindow(user.getWindowToken(),0);
//3.取消焦点
View focusView = getCurrentFocus();
if (focusView != null) {
focusView.clearFocus(); //取消焦点
}
//getCurrentFocus().clearFocus();
//focusView.requestFocus();//请求焦点
}
return true;
}
@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) {
//禁止输入中文
StringUtils.isNotChinese(s);
//判断两个输入框是否有内容
if (user.getEditText().getText().toString().length() > 0 &&
password.getEditText().getText().toString().length() > 0 &&
etCaptcha.getEditText().getText().toString().length() > 0){
//按钮可以点击
loginBtn.setEnabled(true);
}else{
//按钮不能点击
loginBtn.setEnabled(false);
}
}
}

@ -21,7 +21,7 @@ import com.google.android.material.bottomnavigation.BottomNavigationView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.fragment.BookcaseFragment;
@ -40,7 +40,7 @@ import static androidx.fragment.app.FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CU
* @author fengyue
* @date 2020/9/13 13:03
*/
public class MainActivity extends BaseActivity2 {
public class MainActivity extends BaseActivity {
@BindView(R.id.bottom_navigation_view)
BottomNavigationView bottomNavigation;
@BindView(R.id.view_pager_main)

@ -3,8 +3,6 @@ package xyz.fycz.myreader.ui.activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.*;
@ -13,19 +11,11 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.SwitchCompat;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import com.google.android.material.textfield.TextInputLayout;
import io.reactivex.Single;
import io.reactivex.SingleOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.base.observer.MySingleObserver;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.model.storage.BackupRestoreUi;
import xyz.fycz.myreader.model.storage.WebDavHelp;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MultiChoiceDialog;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
@ -33,7 +23,6 @@ import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.ToastUtils;
@ -51,7 +40,7 @@ import static xyz.fycz.myreader.common.APPCONST.BOOK_CACHE_PATH;
* 阅读界面的更多设置
*/
public class MoreSettingActivity extends BaseActivity2 {
public class MoreSettingActivity extends BaseActivity {
@BindView(R.id.more_setting_ll_webdav)
LinearLayout mLlWebdav;
@BindView(R.id.more_setting_rl_volume)

@ -1,271 +0,0 @@
package xyz.fycz.myreader.ui.activity;
import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
import android.text.TextWatcher;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import com.google.android.material.textfield.TextInputLayout;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.model.backup.UserService;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.util.CodeUtil;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* @author fengyue
* @date 2020/9/18 22:37
*/
public class RegisterActivity extends BaseActivity2 {
@BindView(R.id.et_username)
TextInputLayout etUsername;
@BindView(R.id.et_password)
TextInputLayout etPassword;
@BindView(R.id.et_rp_password)
TextInputLayout etRpPassword;
@BindView(R.id.et_captcha)
TextInputLayout etCaptcha;
@BindView(R.id.iv_captcha)
ImageView ivCaptcha;
@BindView(R.id.bt_register)
Button btRegister;
@BindView(R.id.tv_register_tip)
TextView tvRegisterTip;
@BindView(R.id.cb_agreement)
CheckBox cbAgreement;
@BindView(R.id.tv_agreement)
TextView tvAgreement;
private String code;
private String username = "";
private String password = "";
private String rpPassword = "";
private String inputCode = "";
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@SuppressLint("HandlerLeak")
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
createCaptcha();
break;
case 2:
showTip((String) msg.obj);
break;
case 3:
tvRegisterTip.setVisibility(View.GONE);
break;
}
}
};
@Override
protected int getContentId() {
return R.layout.activity_register;
}
@Override
protected void setUpToolbar(Toolbar toolbar) {
super.setUpToolbar(toolbar);
setStatusBarColor(R.color.colorPrimary, true);
getSupportActionBar().setTitle("注册");
}
@Override
protected void initWidget() {
super.initWidget();
mHandler.sendMessage(mHandler.obtainMessage(1));
etUsername.requestFocus();
etUsername.getEditText().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) {
StringUtils.isNotChinese(s);
username = s.toString();
if (username.length() < 6 || username.length() >14){
mHandler.sendMessage(mHandler.obtainMessage(2, "用户名必须在6-14位之间"));
} else if(!username.substring(0, 1).matches("^[A-Za-z]$")){
mHandler.sendMessage(mHandler.obtainMessage(2,
"用户名只能以字母开头"));
}else if(!username.matches("^[A-Za-z0-9-_]+$")){
mHandler.sendMessage(mHandler.obtainMessage(2,
"用户名只能由数字、字母、下划线、减号组成"));
}else {
mHandler.sendMessage(mHandler.obtainMessage(3));
}
checkNotNone();
}
});
etPassword.getEditText().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) {
password = s.toString();
if (password.length() < 8 || password.length() > 16){
mHandler.sendMessage(mHandler.obtainMessage(2, "密码必须在8-16位之间"));
} else if(password.matches("^\\d+$")){
mHandler.sendMessage(mHandler.obtainMessage(2, "密码不能是纯数字"));
} else {
mHandler.sendMessage(mHandler.obtainMessage(3));
}
checkNotNone();
}
});
etRpPassword.getEditText().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) {
rpPassword = s.toString();
if (!rpPassword.equals(password)){
mHandler.sendMessage(mHandler.obtainMessage(2, "两次输入的密码不一致"));
} else {
mHandler.sendMessage(mHandler.obtainMessage(3));
}
checkNotNone();
}
});
etCaptcha.getEditText().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) {
inputCode = s.toString().trim().toLowerCase();
if (!inputCode.equals(code.toLowerCase())){
mHandler.sendMessage(mHandler.obtainMessage(2, "验证码错误"));
} else {
mHandler.sendMessage(mHandler.obtainMessage(3));
}
checkNotNone();
}
});
tvAgreement.setMovementMethod(LinkMovementMethod.getInstance());
}
@Override
protected void initClick() {
super.initClick();
ivCaptcha.setOnClickListener(v -> mHandler.sendMessage(mHandler.obtainMessage(1)));
btRegister.setOnClickListener(v -> {
if (!username.matches("^[A-Za-z][A-Za-z0-9]{5,13}$")){
DialogCreator.createTipDialog(this, "用户名格式错误",
"用户名必须在6-14位之间\n用户名只能以字母开头\n用户名只能由数字、字母、下划线、减号组成");
}else if(password.matches("^\\d+$") || !password.matches("^.{8,16}$")){
DialogCreator.createTipDialog(this, "密码格式错误",
"密码必须在8-16位之间\n密码不能是纯数字");
}else if(!password.equals(rpPassword)){
DialogCreator.createTipDialog(this, "重复密码错误",
"两次输入的密码不一致");
}else if(!inputCode.trim().toLowerCase().equals(code.toLowerCase())){
DialogCreator.createTipDialog(this, "验证码错误");
}else if(!cbAgreement.isChecked()){
DialogCreator.createTipDialog(this, "请勾选同意《用户服务协议》");
}else {
ProgressDialog dialog = DialogCreator.createProgressDialog(this, null, "正在注册...");
Map<String, String> userRegisterInfo = new HashMap<>();
userRegisterInfo.put("username", username);
userRegisterInfo.put("password", password);
UserService.register(userRegisterInfo, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
String[] info = ((String) o).split(":");
int result = Integer.parseInt(info[0].trim());
if (result == 101){
UserService.writeUsername(username);
ToastUtils.showSuccess(info[1]);
finish();
}else {
ToastUtils.showWarring(info[1]);
}
dialog.dismiss();
}
@Override
public void onError(Exception e) {
ToastUtils.showError("注册失败:\n" + e.getLocalizedMessage());
dialog.dismiss();
}
});
}
mHandler.sendMessage(mHandler.obtainMessage(1));
});
}
public void createCaptcha() {
code = CodeUtil.getInstance().createCode();
Bitmap codeBitmap = CodeUtil.getInstance().createBitmap(code);
ivCaptcha.setImageBitmap(codeBitmap);
}
public void showTip(String tip) {
tvRegisterTip.setVisibility(View.VISIBLE);
tvRegisterTip.setText(tip);
}
public void checkNotNone(){
if ("".equals(username) || "".equals(password) || "".equals(rpPassword) || "".equals(inputCode)){
btRegister.setEnabled(false);
}else {
btRegister.setEnabled(true);
}
}
}

@ -22,7 +22,7 @@ import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import me.gujun.android.taggroup.TagGroup;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.model.SearchEngine;
@ -44,14 +44,13 @@ import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* @author fengyue
* @date 2020/9/18 21:58
*/
public class SearchBookActivity extends BaseActivity2 {
public class SearchBookActivity extends BaseActivity {
@BindView(R.id.et_search_key)
EditText etSearchKey;
@BindView(R.id.tv_search_conform)

@ -2,8 +2,11 @@ package xyz.fycz.myreader.ui.activity;
import android.os.Bundle;
import android.text.InputType;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import io.reactivex.Single;
@ -11,11 +14,12 @@ import io.reactivex.SingleOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.base.observer.MySingleObserver;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.model.storage.BackupRestoreUi;
import xyz.fycz.myreader.model.storage.WebDavHelp;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.StringHelper;
@ -27,7 +31,7 @@ import java.util.ArrayList;
* @author fengyue
* @date 2020/10/4 20:44
*/
public class WebDavSettingActivity extends BaseActivity2 {
public class WebDavSettingActivity extends BaseActivity {
@BindView(R.id.webdav_setting_webdav_url)
LinearLayout llWebdavUrl;
@BindView(R.id.tv_webdav_url)
@ -128,4 +132,20 @@ public class WebDavSettingActivity extends BaseActivity2 {
});
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.webdav_help, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
if (item.getItemId() == R.id.action_tip){
DialogCreator.createAssetTipDialog(this, "如何使用WebDav进行云备份?", "webdavhelp.fy");
}
return super.onOptionsItemSelected(item);
}
}

@ -10,21 +10,17 @@ import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import java.io.IOException;
import java.util.ArrayList;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.ui.activity.ReadActivity;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.ui.activity.BookDetailedActivity;
import xyz.fycz.myreader.ui.activity.ReadActivity;
import xyz.fycz.myreader.ui.presenter.BookcasePresenter;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.ToastUtils;

@ -8,21 +8,17 @@ import android.view.ViewGroup;
import androidx.appcompat.app.AlertDialog;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import java.io.IOException;
import java.util.ArrayList;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.ui.activity.ReadActivity;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.ui.activity.BookDetailedActivity;
import xyz.fycz.myreader.ui.activity.ReadActivity;
import xyz.fycz.myreader.ui.presenter.BookcasePresenter;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.ToastUtils;

@ -0,0 +1,62 @@
package xyz.fycz.myreader.ui.dialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.Gravity;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import androidx.annotation.NonNull;
import butterknife.BindView;
import butterknife.ButterKnife;
import xyz.fycz.myreader.R;
/**
* Created by Zhouas666 on 2019-04-14
* Github: https://github.com/zas023
* <p>
* 自由复制dialog
*/
public class CopyContentDialog extends Dialog {
private static final String TAG = "CopyContentDialog";
@BindView(R.id.dialog_tv_content)
TextView dialogTvContent;
private String content;
/***************************************************************************/
public CopyContentDialog(@NonNull Context context, String content) {
super(context);
this.content = content;
}
/*****************************Initialization********************************/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_copy_content);
ButterKnife.bind(this);
setUpWindow();
dialogTvContent.setText(content);
}
/**
* 设置Dialog显示的位置
*/
private void setUpWindow() {
Window window = getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.CENTER;
window.setAttributes(lp);
}
}

@ -1,5 +1,6 @@
package xyz.fycz.myreader.ui.dialog;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
@ -72,9 +73,11 @@ public class DialogCreator {
dialog.setContentView(view);
dialog.setCancelable(true);
dialog.setCanceledOnTouchOutside(true);
//触摸外部关闭
view.findViewById(R.id.ll_bottom_view).setOnClickListener(null);
view.setOnTouchListener(new View.OnTouchListener() {
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
dialog.dismiss();
@ -84,7 +87,7 @@ public class DialogCreator {
//设置全屏
Window window = dialog.getWindow();
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
window.setWindowAnimations(R.style.dialogWindowAnim);
//阅读背景风格
final ImageView ivCommonStyle = (ImageView) view.findViewById(R.id.iv_common_style);
final ImageView ivLeatherStyle = (ImageView) view.findViewById(R.id.iv_leather_style);
@ -306,168 +309,6 @@ public class DialogCreator {
}
}
/**
* 阅读设置对话框
*
* @param context
* @param isDayStyle
* @param chapterProgress
* @param backListener
* @param lastChapterListener
* @param nextChapterListener
* @param chapterListListener
* @param onClickNightAndDayListener
* @param settingListener
* @return
*/
public static Dialog createReadSetting(final Context context, final boolean isDayStyle, int chapterProgress, int maxProcess, final Book mBook, Chapter mChapter,
View.OnClickListener backListener,
View.OnClickListener changeSourceListener,
View.OnClickListener refreshListener,
View.OnClickListener bookMarkListener,
final OnSkipChapterListener lastChapterListener,
final OnSkipChapterListener nextChapterListener,
View.OnClickListener chapterListListener,
final OnClickNightAndDayListener onClickNightAndDayListener,
View.OnClickListener settingListener,
SeekBar.OnSeekBarChangeListener onSeekBarChangeListener,
View.OnClickListener voiceOnClickListener,
final OnClickDownloadAllChapterListener onClickDownloadAllChapterListener) {
final Dialog dialog = new Dialog(context, R.style.jmui_default_dialog_style);
final View view = LayoutInflater.from(context).inflate(R.layout.dialog_read_setting, null);
dialog.setContentView(view);
LinearLayout llBack = (LinearLayout) view.findViewById(R.id.ll_title_back);
LinearLayout llBook = view.findViewById(R.id.ll_book_name);
TextView tvBookName = view.findViewById(R.id.tv_book_name_top);
ImageView ivChangeSource = view.findViewById(R.id.iv_change_source);
ImageView ivRefresh = view.findViewById(R.id.iv_refresh);
ImageView ivBookMark = view.findViewById(R.id.iv_book_mark);
ImageView ivMore = view.findViewById(R.id.iv_more);
LinearLayout llChapter = view.findViewById(R.id.ll_chapter_view);
final TextView tvChapterTitle = view.findViewById(R.id.tv_chapter_title_top);
final TextView tvChapterUrl = view.findViewById(R.id.tv_chapter_url);
TextView tvLastChapter = (TextView) view.findViewById(R.id.tv_last_chapter);
TextView tvNextChapter = (TextView) view.findViewById(R.id.tv_next_chapter);
final SeekBar sbChapterProgress = (SeekBar) view.findViewById(R.id.sb_read_chapter_progress);
LinearLayout llChapterList = (LinearLayout) view.findViewById(R.id.ll_chapter_list);
LinearLayout llNightAndDay = (LinearLayout) view.findViewById(R.id.ll_night_and_day);
LinearLayout llSetting = (LinearLayout) view.findViewById(R.id.ll_setting);
final ImageView ivNightAndDay = (ImageView) view.findViewById(R.id.iv_night_and_day);
final TextView tvNightAndDay = (TextView) view.findViewById(R.id.tv_night_and_day);
ImageView ivVoice = (ImageView)view.findViewById(R.id.iv_voice_read);
view.findViewById(R.id.rl_title_view).setOnClickListener(null);
view.findViewById(R.id.ll_bottom_view).setOnClickListener(null);
Window window = dialog.getWindow();
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
if (Build.VERSION.SDK_INT >= 21) {
window.setStatusBarColor(dialog.getContext().getColor(R.color.sys_dialog_setting_bg));
}
view.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
dialog.dismiss();
return false;
}
});
tvBookName.setText(mBook.getName());
llBook.setOnClickListener(v -> {
/*Intent intent = new Intent(context, BookDetailedActivity.class);
intent.putExtra(APPCONST.BOOK, mBook);
context.startActivity(intent);*/
});
if ("本地书籍".equals(mBook.getType())){
ivChangeSource.setVisibility(View.GONE);
}
//换源
ivChangeSource.setOnClickListener(changeSourceListener);
//刷新
ivRefresh.setOnClickListener(refreshListener);
String url = mChapter.getUrl();
if ("null".equals(mBook.getSource()) || BookSource.fynovel.equals(mBook.getSource())) {
if (!url.contains("novel.fycz.xyz")) {
url = URLCONST.nameSpace_FY + url;
}
}
//书签
ivBookMark.setOnClickListener(bookMarkListener);
tvChapterTitle.setText(mChapter.getTitle());
tvChapterUrl.setText(StringHelper.isEmpty(url) ? mChapter.getId() : url);
//跳转对应章节
llChapter.setOnClickListener(v -> {
String url1 = tvChapterUrl.getText().toString();
if (!"本地书籍".equals(mBook.getType()) && !StringHelper.isEmpty(url1)) {
Intent intent = new Intent(Intent.ACTION_VIEW);
Uri uri = Uri.parse(url1);
intent.setData(uri);
context.startActivity(intent);
}
});
if (!isDayStyle) {
ivNightAndDay.setImageResource(R.mipmap.z4);
tvNightAndDay.setText(context.getString(R.string.day));
}
llBack.setOnClickListener(backListener);
tvLastChapter.setOnClickListener(v -> {
if (lastChapterListener != null){
lastChapterListener.onClick(tvChapterTitle, tvChapterUrl, sbChapterProgress);
}
});
tvNextChapter.setOnClickListener(v -> {
if (nextChapterListener != null){
nextChapterListener.onClick(tvChapterTitle, tvChapterUrl, sbChapterProgress);
}
});
sbChapterProgress.setProgress(chapterProgress);
sbChapterProgress.setMax(maxProcess);
llChapterList.setOnClickListener(chapterListListener);
llSetting.setOnClickListener(settingListener);
sbChapterProgress.setOnSeekBarChangeListener(onSeekBarChangeListener);
ivVoice.setOnClickListener(voiceOnClickListener);
//日夜切换
llNightAndDay.setOnClickListener(view1 -> {
boolean isDay;
if (tvNightAndDay.getText().toString().equals(context.getString(R.string.day))) {
isDay = false;
ivNightAndDay.setImageResource(R.mipmap.ao);
tvNightAndDay.setText(context.getString(R.string.night));
} else {
isDay = true;
ivNightAndDay.setImageResource(R.mipmap.z4);
tvNightAndDay.setText(context.getString(R.string.day));
}
if (onClickNightAndDayListener != null) {
onClickNightAndDayListener.onClick(dialog, view, isDay);
}
});
//缓存章节
final TextView tvDownloadProgress = view.findViewById(R.id.tv_download_progress);
LinearLayout llDonwloadCache = view.findViewById(R.id.ll_download_cache);
llDonwloadCache.setOnClickListener(v -> {
if (onClickDownloadAllChapterListener != null){
onClickDownloadAllChapterListener.onClick(dialog,v,tvDownloadProgress);
}
});
dialog.setCancelable(true);
dialog.setCanceledOnTouchOutside(true);
return dialog;
}
/**
* 创建一个普通对话框包含确定取消按键
*
@ -702,7 +543,7 @@ public class DialogCreator {
* 从assets文件夹之中读取文件并显示提示框
* @param mContext
* @param title
* @param assetName
* @param assetName 需要后缀名
*/
public static void createAssetTipDialog(Context mContext, String title, String assetName){
BufferedReader br = null;

@ -8,65 +8,43 @@ import android.os.Message;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import butterknife.BindView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.model.backup.BackupAndRestore;
import xyz.fycz.myreader.model.backup.UserService;
import xyz.fycz.myreader.base.BaseFragment;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.model.storage.Backup;
import xyz.fycz.myreader.model.storage.Restore;
import xyz.fycz.myreader.ui.activity.MainActivity;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.ui.activity.AboutActivity;
import xyz.fycz.myreader.ui.activity.LoginActivity;
import xyz.fycz.myreader.ui.activity.MoreSettingActivity;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import static android.app.Activity.RESULT_OK;
/**
* @author fengyue
* @date 2020/9/13 13:20
*/
public class MineFragment extends BaseFragment {
@BindView(R.id.mine_rl_user)
RelativeLayout mRlUser;
@BindView(R.id.tv_user)
TextView mTvUser;
@BindView(R.id.mine_rl_backup)
RelativeLayout mRlBackup;
@BindView(R.id.mine_rl_syn)
RelativeLayout mRlSyn;
@BindView(R.id.mine_rl_setting)
RelativeLayout mRlSetting;
@BindView(R.id.mine_rl_theme_mode)
RelativeLayout mRlThemeMode;
@BindView(R.id.tv_theme_mode_select)
TextView tvThemeModeSelect;
@BindView(R.id.mine_rl_feedback)
RelativeLayout mRlFeedback;
@BindView(R.id.mine_rl_about)
RelativeLayout mRlAbout;
private boolean isLogin;
private BackupAndRestore mBackupAndRestore;
private Setting mSetting;
private String[] webSynMenu;
private String[] backupMenu;
@ -79,7 +57,7 @@ public class MineFragment extends BaseFragment {
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 1:
mTvUser.setText("登录/注册");
break;
case 2:
backup();
@ -102,8 +80,6 @@ public class MineFragment extends BaseFragment {
@Override
protected void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
isLogin = UserService.isLogin();
mBackupAndRestore = new BackupAndRestore();
mSetting = SysManager.getSetting();
webSynMenu = new String[]{
MyApplication.getmContext().getString(R.string.menu_backup_webBackup),
@ -121,35 +97,12 @@ public class MineFragment extends BaseFragment {
@Override
protected void initWidget(Bundle savedInstanceState) {
super.initWidget(savedInstanceState);
if (isLogin) {
mTvUser.setText(UserService.readUsername());
}
tvThemeModeSelect.setText(themeModeArr[themeMode]);
}
@Override
protected void initClick() {
super.initClick();
mRlUser.setOnClickListener(v -> {
if (isLogin) {
DialogCreator.createCommonDialog(getActivity(), "退出登录", "确定要退出登录吗?"
, true, (dialog, which) -> {
File file = MyApplication.getApplication().getFileStreamPath("userConfig.fy");
if (file.delete()) {
ToastUtils.showSuccess("退出成功");
isLogin = false;
mHandler.sendEmptyMessage(1);
Intent intent = new Intent(getActivity(), LoginActivity.class);
getActivity().startActivityForResult(intent, APPCONST.REQUEST_LOGIN);
} else {
ToastUtils.showError("退出失败(Error:file.delete())");
}
}, (dialog, which) -> dialog.dismiss());
} else {
Intent intent = new Intent(getActivity(), LoginActivity.class);
getActivity().startActivityForResult(intent, APPCONST.REQUEST_LOGIN);
}
});
mRlBackup.setOnClickListener(v -> {
AlertDialog bookDialog = MyAlertDialog.build(getContext())
.setTitle(getContext().getResources().getString(R.string.menu_bookcase_backup))
@ -168,46 +121,6 @@ public class MineFragment extends BaseFragment {
.create();
bookDialog.show();
});
mRlSyn.setOnClickListener(v -> {
if (!UserService.isLogin()) {
ToastUtils.showWarring("请先登录!");
Intent loginIntent = new Intent(getActivity(), LoginActivity.class);
getActivity().startActivityForResult(loginIntent, APPCONST.REQUEST_LOGIN);
return;
}
if (mSetting.isAutoSyn()) {
webSynMenu[2] = MyApplication.getmContext().getString(R.string.menu_backup_autoSyn) + "已开启";
} else {
webSynMenu[2] = MyApplication.getmContext().getString(R.string.menu_backup_autoSyn) + "已关闭";
}
MyAlertDialog.build(getContext())
.setTitle(getActivity().getString(R.string.menu_bookcase_syn))
.setItems(webSynMenu, (dialog, which) -> {
switch (which) {
case 0:
synBookcaseToWeb(false);
break;
case 1:
webRestore();
break;
case 2:
String tip = "";
if (mSetting.isAutoSyn()) {
mSetting.setAutoSyn(false);
tip = "每日自动同步已关闭!";
} else {
mSetting.setAutoSyn(true);
tip = "每日自动同步已开启!";
}
SysManager.saveSetting(mSetting);
ToastUtils.showSuccess(tip);
break;
}
})
.setNegativeButton(null, null)
.setPositiveButton(null, null)
.show();
});
mRlSetting.setOnClickListener(v -> {
Intent settingIntent = new Intent(getActivity(), MoreSettingActivity.class);
startActivity(settingIntent);
@ -256,20 +169,6 @@ public class MineFragment extends BaseFragment {
startActivity(aboutIntent);
});
mRlFeedback.setOnClickListener(v -> {
DialogCreator.createCommonDialog(getContext(), "问题反馈", "请加入QQ群(1085028304)反馈问题!", true,
"加入QQ群", "取消", (dialog, which) -> {
if (!MyApplication.joinQQGroup(getContext(), "8PIOnHFuH6A38hgxvD_Rp2Bu-Ke1ToBn")) {
ClipboardManager mClipboardManager = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
//数据
ClipData mClipData = ClipData.newPlainText("Label", "1085028304");
//把数据设置到剪切板上
assert mClipboardManager != null;
mClipboardManager.setPrimaryClip(mClipData);
ToastUtils.showError("未安装手Q或安装的版本不支持!\n已复制QQ群号,您可自行前往QQ添加!");
}
}, null);
});
}
@Override
@ -342,112 +241,6 @@ public class MineFragment extends BaseFragment {
}, (dialogInterface, i) -> dialogInterface.dismiss());
}
/**
* 同步书架
*/
private void synBookcaseToWeb(boolean isAutoSyn) {
if (!NetworkUtils.isNetWorkAvailable()) {
if (!isAutoSyn) {
ToastUtils.showWarring("无网络连接!");
}
return;
}
ArrayList<Book> mBooks = (ArrayList<Book>) BookService.getInstance().getAllBooks();
if (mBooks.size() == 0) {
if (!isAutoSyn) {
ToastUtils.showWarring("当前书架无任何书籍,无法同步!");
}
return;
}
Date nowTime = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd");
String nowTimeStr = sdf.format(nowTime);
SharedPreUtils spb = SharedPreUtils.getInstance();
String synTime = spb.getString(getString(R.string.synTime));
if (!nowTimeStr.equals(synTime) || !isAutoSyn) {
UserService.webBackup(new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
if ((boolean) o) {
spb.putString(getString(R.string.synTime), nowTimeStr);
if (!isAutoSyn) {
DialogCreator.createTipDialog(getContext(), "成功将书架同步至网络!");
}
} else {
if (!isAutoSyn) {
DialogCreator.createTipDialog(getContext(), "同步失败,请重试!");
}
}
}
@Override
public void onError(Exception e) {
if (!isAutoSyn) {
DialogCreator.createTipDialog(getContext(), "同步失败,请重试!\n" + e.getLocalizedMessage());
}
}
});
}
}
/**
* 恢复
*/
private void webRestore() {
if (!NetworkUtils.isNetWorkAvailable()) {
ToastUtils.showWarring("无网络连接!");
return;
}
DialogCreator.createCommonDialog(getContext(), "确认同步吗?", "将书架从网络同步至本地会覆盖原有书架!", true,
(dialogInterface, i) -> {
dialogInterface.dismiss();
MyApplication.getApplication().newThread(() -> {
/*if (UserService.webRestore()) {
mHandler.sendMessage(mHandler.obtainMessage(7));
// DialogCreator.createTipDialog(mMainActivity,
// "恢复成功!\n注意:本功能属于实验功能,书架恢复后,书籍初次加载时可能加载失败,返回重新加载即可!");、
mSetting = SysManager.getSetting();
ToastUtils.showSuccess("成功将书架从网络同步至本地!");
} else {
DialogCreator.createTipDialog(getContext(), "未找到同步文件,同步失败!");
}*/
UserService.webRestore(new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
if ((boolean) o) {
mHandler.sendMessage(mHandler.obtainMessage(7));
// DialogCreator.createTipDialog(mMainActivity,
// "恢复成功!\n注意:本功能属于实验功能,书架恢复后,书籍初次加载时可能加载失败,返回重新加载即可!");、
mSetting = SysManager.getSetting();
ToastUtils.showSuccess("成功将书架从网络同步至本地!");
} else {
DialogCreator.createTipDialog(getContext(), "未找到同步文件,同步失败!");
}
}
@Override
public void onError(Exception e) {
DialogCreator.createTipDialog(getContext(), "未找到同步文件,同步失败!\n" + e.getLocalizedMessage());
}
});
});
}, (dialogInterface, i) -> dialogInterface.dismiss());
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
case APPCONST.REQUEST_LOGIN:
assert data != null;
isLogin = data.getBooleanExtra("isLogin", false);
if (isLogin) {
mTvUser.setText(UserService.readUsername());
}
break;
}
}
}
public boolean isRecreate() {
return unbinder == null;

@ -23,9 +23,7 @@ import android.widget.PopupMenu;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -41,8 +39,6 @@ import xyz.fycz.myreader.ui.dialog.MultiChoiceDialog;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.greendao.entity.BookGroup;
import xyz.fycz.myreader.greendao.service.BookGroupService;
import xyz.fycz.myreader.model.backup.BackupAndRestore;
import xyz.fycz.myreader.model.backup.UserService;
import xyz.fycz.myreader.ui.activity.*;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
@ -83,7 +79,6 @@ public class BookcasePresenter implements BasePresenter {
private Setting mSetting;
private final List<Book> errorLoadingBooks = new ArrayList<>();
private int finishLoadBookCount = 0;
private final BackupAndRestore mBackupAndRestore;
// private int notifyId = 11;
private ExecutorService es = Executors.newFixedThreadPool(1);//更新/下载线程池
@ -179,7 +174,6 @@ public class BookcasePresenter implements BasePresenter {
mMainActivity = (MainActivity) (mBookcaseFragment.getActivity());
// mChapterService = new ChapterService();
mSetting = SysManager.getSetting();
mBackupAndRestore = new BackupAndRestore();
}
//启动
@ -195,10 +189,6 @@ public class BookcasePresenter implements BasePresenter {
getData();
if (mSetting.isAutoSyn() && UserService.isLogin()) {
synBookcaseToWeb(true);
}
//是否启用下拉刷新(默认启用)
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
mBookcaseFragment.getSrlContent().setEnableRefresh(false);
@ -956,35 +946,6 @@ public class BookcasePresenter implements BasePresenter {
}
return;
}
Date nowTime = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd");
String nowTimeStr = sdf.format(nowTime);
SharedPreUtils spb = SharedPreUtils.getInstance();
String synTime = spb.getString(mMainActivity.getString(R.string.synTime));
if (!nowTimeStr.equals(synTime) || !isAutoSyn) {
UserService.webBackup(new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
if ((boolean) o){
spb.putString(mMainActivity.getString(R.string.synTime), nowTimeStr);
if (!isAutoSyn) {
DialogCreator.createTipDialog(mMainActivity, "成功将书架同步至网络!");
}
}else {
if (!isAutoSyn) {
DialogCreator.createTipDialog(mMainActivity, "同步失败,请重试!");
}
}
}
@Override
public void onError(Exception e) {
if (!isAutoSyn) {
DialogCreator.createTipDialog(mMainActivity, "同步失败,请重试!\n" + e.getLocalizedMessage());
}
}
});
}
}
/*****************************************用于返回按钮判断*************************************/

@ -1,72 +0,0 @@
package xyz.fycz.myreader.util;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import xyz.fycz.myreader.common.APPCONST;
import java.util.Date;
import static android.content.Context.ALARM_SERVICE;
import static android.content.Intent.FLAG_INCLUDE_STOPPED_PACKAGES;
public class AlarmHelper {
private static String alarmActicon = "xyz.fycz.kl.gxdw";
private static String AntiHijackingActicon = "xyz.fycz.kl.gxdw.AntiHijacking";
public static void addOneShotAlarm(Context context, long time, String msg, int id){
Date date = new Date();
if(time <= date.getTime()){
return;
}
Intent intent = new Intent(alarmActicon);
intent.putExtra(APPCONST.ALARM_SCHEDULE_MSG,msg);
intent.setFlags(FLAG_INCLUDE_STOPPED_PACKAGES);
PendingIntent pi = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP,time,pi);
}else {
alarmManager.set(AlarmManager.RTC_WAKEUP,time,pi);
}
}
public static void removeOneShotAlarm(Context context, int id){
Intent intent = new Intent(alarmActicon);
intent.setFlags(FLAG_INCLUDE_STOPPED_PACKAGES);
PendingIntent pi = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(ALARM_SERVICE);
alarmManager.cancel(pi);
}
public static void addAlarm(Context context, long time, int id){
// Date date = new Date();
/* if(time <= date.getTime()){
return;
}*/
Intent intent = new Intent(AntiHijackingActicon);
intent.setFlags(FLAG_INCLUDE_STOPPED_PACKAGES);
PendingIntent pi = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(ALARM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP,new Date().getTime() + time,pi);
}else {
alarmManager.set(AlarmManager.RTC_WAKEUP,new Date().getTime() + time,pi);
}
}
public static void removeAlarm(Context context, int id){
Intent intent = new Intent(AntiHijackingActicon);
intent.setFlags(FLAG_INCLUDE_STOPPED_PACKAGES);
PendingIntent pi = PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(ALARM_SERVICE);
alarmManager.cancel(pi);
}
}

@ -1,125 +0,0 @@
package xyz.fycz.myreader.util;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import xyz.fycz.myreader.R;
/**
* 防界面劫持提示
*/
public class Anti_hijackingUtils {
// private int id = 12345678;//记录定时ID
private static boolean home;
private static boolean back;
/**
* 用于执行定时任务
*/
private Timer timer = null;
/**
* 用于保存当前任务
*/
private List<MyTimerTask> tasks = null;
/**
* 唯一实例
*/
private static Anti_hijackingUtils anti_hijackingUtils;
private Anti_hijackingUtils() {
// 初始化
tasks = new ArrayList<MyTimerTask>();
timer = new Timer();
}
/**
* 获取唯一实例
*
* @return 唯一实例
*/
public static Anti_hijackingUtils getinstance() {
if (anti_hijackingUtils == null) {
anti_hijackingUtils = new Anti_hijackingUtils();
}
return anti_hijackingUtils;
}
/**
* 在activity的onPause()方法中调用
*
* @param activity
*/
public void onPause(final AppCompatActivity activity) {
MyTimerTask task = new MyTimerTask(activity);
tasks.add(task);
timer.schedule(task, 2000);
// AlarmHelper.addAlarm(activity,2000,id);
}
/**
* 在activity的onResume()方法中调用
*/
public void onResume(final AppCompatActivity activity) {
if (tasks.size() > 0) {
tasks.get(tasks.size() - 1).setCanRun(false);
tasks.remove(tasks.size() - 1);
}
// AlarmHelper.removeAlarm(activity,id);
}
/**
* 自定义TimerTask类
*/
class MyTimerTask extends TimerTask {
/**
* 任务是否有效
*/
private boolean canRun = true;
private AppCompatActivity activity;
private void setCanRun(boolean canRun) {
this.canRun = canRun;
}
public MyTimerTask(AppCompatActivity activity) {
this.activity = activity;
}
@Override
public void run() {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (canRun) {
// 程序退到后台,进行风险警告
if (home || back){
Toast.makeText(activity, activity.getString(R.string.anti_hijacking_tips_home), Toast.LENGTH_LONG).show();
tasks.remove(MyTimerTask.this);
home = false;
}else {
Toast.makeText(activity, activity.getString(R.string.anti_hijacking_tips), Toast.LENGTH_LONG).show();
// TextHelper.showLongText(MyApplication.getApplication().getString(R.string.anti_hijacking_tips));
tasks.remove(MyTimerTask.this);
}
}
}
});
}
}
}

@ -1,125 +0,0 @@
package xyz.fycz.myreader.util;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* Created by Kodulf
*/
public class BeanPropertiesUtil {
/**
* 利用反射实现对象之间属性复制
* @param from
* @param to
*/
public static void copyProperties(Object from, Object to) throws Exception {
copyPropertiesExclude(from, to, null);
}
/**
* 复制对象属性
* @param from
* @param to
* @param excludsArray 排除属性列表
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static void copyPropertiesExclude(Object from, Object to, String[] excludsArray) throws Exception {
List<String> excludesList = null;
if(excludsArray != null && excludsArray.length > 0) {
excludesList = Arrays.asList(excludsArray); //构造列表对象
}
Method[] fromMethods = from.getClass().getDeclaredMethods();
Method[] toMethods = to.getClass().getDeclaredMethods();
Method fromMethod = null, toMethod = null;
String fromMethodName = null, toMethodName = null;
for (int i = 0; i < fromMethods.length; i++) {
fromMethod = fromMethods[i];
fromMethodName = fromMethod.getName();
if (!fromMethodName.contains("get") || fromMethodName.contains("getId"))
continue;
//排除列表检测
if(excludesList != null && excludesList.contains(fromMethodName.substring(3).toLowerCase())) {
continue;
}
toMethodName = "set" + fromMethodName.substring(3);
toMethod = findMethodByName(toMethods, toMethodName);
if (toMethod == null)
continue;
Object value = fromMethod.invoke(from);
if(value == null)
continue;
//集合类判空处理
if(value instanceof Collection) {
Collection newValue = (Collection)value;
if(newValue.size() <= 0)
continue;
}
toMethod.invoke(to, value);
}
}
/**
* 对象属性值复制仅复制指定名称的属性值
* @param from
* @param to
* @param includsArray
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static void copyPropertiesInclude(Object from, Object to, String[] includsArray) throws Exception {
List<String> includesList = null;
if(includsArray != null && includsArray.length > 0) {
includesList = Arrays.asList(includsArray); //构造列表对象
} else {
return;
}
Method[] fromMethods = from.getClass().getDeclaredMethods();
Method[] toMethods = to.getClass().getDeclaredMethods();
Method fromMethod = null, toMethod = null;
String fromMethodName = null, toMethodName = null;
for (int i = 0; i < fromMethods.length; i++) {
fromMethod = fromMethods[i];
fromMethodName = fromMethod.getName();
if (!fromMethodName.contains("get"))
continue;
//排除列表检测
String str = fromMethodName.substring(3);
if(!includesList.contains(str.substring(0,1).toLowerCase() + str.substring(1))) {
continue;
}
toMethodName = "set" + fromMethodName.substring(3);
toMethod = findMethodByName(toMethods, toMethodName);
if (toMethod == null)
continue;
Object value = fromMethod.invoke(from);
if(value == null)
continue;
//集合类判空处理
if(value instanceof Collection) {
Collection newValue = (Collection)value;
if(newValue.size() <= 0)
continue;
}
toMethod.invoke(to, value);
}
}
/**
* 从方法数组中获取指定名称的方法
*
* @param methods
* @param name
* @return
*/
public static Method findMethodByName(Method[] methods, String name) {
for (int j = 0; j < methods.length; j++) {
if (methods[j].getName().equals(name))
return methods[j];
}
return null;
}
}

@ -1,431 +0,0 @@
package xyz.fycz.myreader.util;
import android.Manifest;
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.util.HashMap;
import java.util.Map;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import static android.app.DownloadManager.Request.VISIBILITY_HIDDEN;
public class DownloadMangerUtils {
public static final String FILE_DIR = "gxdw";
private static Map<Long, BroadcastReceiver> mBroadcastReceiverMap = new HashMap<>();
/**
* 文件下载
*
* @param context
* @param fileDir
* @param url
* @param fileName
*/
public static void downloadFile(Context context, String fileDir, String url, String fileName) {
try {
Log.d("http download:", url);
//String Url = "10.10.123.16:8080/gxqdw_ubap/mEmailController.thumb?getAttachmentStream&fileId=1&fileName=自我探索——我是谁.ppt&emailId=36&token=d1828248-cc71-4719-8218-adc31ffc9cca&type=inbox&fileSize=14696446";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
// DownloadManager.Request.setDestinationInExternalPublicDir();
request.setDescription(fileName);
request.setTitle("附件");
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
// request.setNotificationVisibility(VISIBILITY_HIDDEN);
}
// int i = Build.VERSION.SDK_INT;
if (Build.VERSION.SDK_INT > 17) {
request.setDestinationInExternalPublicDir(fileDir, fileName);
} else {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
request.setDestinationInExternalPublicDir(fileDir, fileName);
} else {
Log.d("download", "android版本过低,不存在外部存储,下载路径无法指定,默认路径:/data/data/com.android.providers.downloads/cache/");
DialogCreator.createCommonDialog(context, "文件下载", "android版本过低或系统兼容性问题,不存在外部存储,无法指定下载路径,文件下载到系统默认路径,请到文件管理搜索文件名", true,
"关闭", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
}
}
// get download service and enqueue file
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
} catch (Exception e) {
e.printStackTrace();
lowVersionNoSDDownload(context, url, fileName);
}
}
/**
* 低版本无外置存储下载
*
* @param context
* @param url
* @param fileName
*/
private static void lowVersionNoSDDownload(Context context, String url, String fileName) {
try {
Log.d("http download:", url);
//String Url = "10.10.123.16:8080/gxqdw_ubap/mEmailController.thumb?getAttachmentStream&fileId=1&fileName=自我探索——我是谁.ppt&emailId=36&token=d1828248-cc71-4719-8218-adc31ffc9cca&type=inbox&fileSize=14696446";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
// DownloadManager.Request.setDestinationInExternalPublicDir();
request.setDescription(fileName);
request.setTitle("附件");
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
// request.setNotificationVisibility(VISIBILITY_HIDDEN);
}
// int i = Build.VERSION.SDK_INT;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
request.setDestinationInExternalPublicDir(APPCONST.FILE_DIR, fileName);
} else {
Log.d("download", "android版本过低,不存在外部存储,下载路径无法指定,默认路径:/data/data/com.android.providers.downloads/cache/");
DialogCreator.createCommonDialog(context, "文件下载", "android版本过低或系统兼容性问题,不存在外部存储,无法指定下载路径,文件下载到系统默认路径,请到文件管理搜索文件名", true,
"关闭", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
}
// get download service and enqueue file
DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
} catch (Exception e) {
e.printStackTrace();
ToastUtils.showError("下载错误:" + e.getLocalizedMessage());
}
}
/**
* 文件下载有回调无通知
*
* @param context
* @param fileDir
* @param fileName
* @param url
* @param listener
*/
public static void downloadFileByFinishListener(Context context, String fileDir, String fileName, String url,
final DownloadCompleteListener listener) {
try {
if (isPermission(context)) {
Log.d("http download:", url);
// String Url = "10.10.123.16:8080/gxqdw_ubap/mEmailController.thumb?getAttachmentStream&fileId=1&fileName=自我探索——我是谁.ppt&emailId=36&token=d1828248-cc71-4719-8218-adc31ffc9cca&type=inbox&fileSize=14696446";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
// DownloadManager.Request.setDestinationInExternalPublicDir();
/* request.setDescription(fileName);
request.setTitle("附件");*/
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
// request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setNotificationVisibility(VISIBILITY_HIDDEN);
}
// int i = Build.VERSION.SDK_INT;
if (Build.VERSION.SDK_INT > 17) {
request.setDestinationInExternalPublicDir(fileDir, fileName);
} else {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
request.setDestinationInExternalPublicDir(fileDir, fileName);
} else {
Log.i("download", "android版本过低,不存在外部存储,下载路径无法指定,默认路径:/data/data/com.android.providers.downloads/cache/");
}
}
// get download service and enqueue file
final DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
final long id = manager.enqueue(request);
// 注册广播监听系统的下载完成事件。
IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
long ID = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (ID == id) {
listener.onFinish(manager.getUriForDownloadedFile(id));
context.unregisterReceiver(mBroadcastReceiverMap.get(id));
mBroadcastReceiverMap.remove(id);
/* Toast.makeText(getApplicationContext(), "任务:" + Id + " 下载完成!", Toast.LENGTH_LONG).show();*/
}
}
};
context.registerReceiver(broadcastReceiver, intentFilter);
mBroadcastReceiverMap.put(id, broadcastReceiver);
}
} catch (Exception e) {
lowVersionNoSDDownloadFileByFinishListener(context, fileDir, fileName, url, listener);
// listener.onError(e.toString());
}
}
/**
* 低版本无外置存储文件下载有回调无通知
*
* @param context
* @param fileDir
* @param fileName
* @param url
* @param listener
*/
private static void lowVersionNoSDDownloadFileByFinishListener(Context context, String fileDir, String fileName, String url,
final DownloadCompleteListener listener) {
try {
Log.d("http download:", url);
// String Url = "10.10.123.16:8080/gxqdw_ubap/mEmailController.thumb?getAttachmentStream&fileId=1&fileName=自我探索——我是谁.ppt&emailId=36&token=d1828248-cc71-4719-8218-adc31ffc9cca&type=inbox&fileSize=14696446";
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
// DownloadManager.Request.setDestinationInExternalPublicDir();
/* request.setDescription(fileName);
request.setTitle("附件");*/
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
// request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setNotificationVisibility(VISIBILITY_HIDDEN);
}
// int i = Build.VERSION.SDK_INT;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
request.setDestinationInExternalPublicDir(APPCONST.FILE_DIR, fileName);
} else {
Log.d("download", "android版本过低,不存在外部存储,下载路径无法指定,默认路径:/data/data/com.android.providers.downloads/cache/");
}
// get download service and enqueue file
final DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
final long id = manager.enqueue(request);
// 注册广播监听系统的下载完成事件。
IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
long ID = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (ID == id) {
listener.onFinish(manager.getUriForDownloadedFile(id));
context.unregisterReceiver(mBroadcastReceiverMap.get(id));
mBroadcastReceiverMap.remove(id);
/* Toast.makeText(getApplicationContext(), "任务:" + Id + " 下载完成!", Toast.LENGTH_LONG).show();*/
}
}
};
context.registerReceiver(broadcastReceiver, intentFilter);
mBroadcastReceiverMap.put(id, broadcastReceiver);
} catch (Exception e) {
listener.onError(e.toString());
}
}
/**
* 文件下载有回调有通知
*
* @param context
* @param fileDir
* @param fileName
* @param url
* @param title
* @param listener
*/
public static void downloadFileOnNotificationByFinishListener(final Context context, final String fileDir, final String fileName, final String url,
final String title, final DownloadCompleteListener listener) {
try {
if (isPermission(context)) {
Log.d("http download:", url);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setTitle(title);
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
}
if (Build.VERSION.SDK_INT > 17) {
/* File file = new File(Environment.getExternalStorageDirectory() + "/gxdw/apk/app_gxdw_186.apk");
if (!file.exists()){
boolean flag = file.createNewFile();
}*/
request.setDestinationInExternalPublicDir(fileDir, fileName);
} else {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
request.setDestinationInExternalPublicDir(fileDir, fileName);
} else {
Log.d("download", "android版本过低,不存在外部存储,下载路径无法指定,默认路径:/data/data/com.android.providers.downloads/cache/");
}
}
// get download service and enqueue file
final DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
final long id = manager.enqueue(request);
// 注册广播监听系统的下载完成事件。
IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
long ID = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (ID == id) {
listener.onFinish(manager.getUriForDownloadedFile(id));
context.unregisterReceiver(mBroadcastReceiverMap.get(id));
mBroadcastReceiverMap.remove(id);
/* Toast.makeText(getApplicationContext(), "任务:" + Id + " 下载完成!", Toast.LENGTH_LONG).show();*/
}
}
};
context.registerReceiver(broadcastReceiver, intentFilter);
mBroadcastReceiverMap.put(id, broadcastReceiver);
}
} catch (Exception e) {
e.printStackTrace();
lowVersionDownloadFileOnNotificationByFinishListener(context, fileDir, fileName, url, title, listener);
}
}
/**
* 低版本文件下载有回调有通知
*
* @param context
* @param fileDir
* @param fileName
* @param url
* @param title
* @param listener
*/
private static void lowVersionDownloadFileOnNotificationByFinishListener(final Context context, final String fileDir, final String fileName, final String url,
final String title, final DownloadCompleteListener listener) {
try {
Log.d("http download:", url);
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setTitle(title);
// in order for this if to run, you must use the android 3.2 to compile your app
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE);
}
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
request.setDestinationInExternalPublicDir(APPCONST.FILE_DIR, fileName);
} else {
Log.d("download", "android版本过低,不存在外部存储,下载路径无法指定,默认路径:/data/data/com.android.providers.downloads/cache/");
}
// get download service and enqueue file
final DownloadManager manager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
final long id = manager.enqueue(request);
// 注册广播监听系统的下载完成事件。
IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
final BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
long ID = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (ID == id) {
listener.onFinish(manager.getUriForDownloadedFile(id));
context.unregisterReceiver(mBroadcastReceiverMap.get(id));
mBroadcastReceiverMap.remove(id);
}
}
};
context.registerReceiver(broadcastReceiver, intentFilter);
mBroadcastReceiverMap.put(id, broadcastReceiver);
} catch (Exception e) {
e.printStackTrace();
listener.onError(e.toString());
}
}
/**
* 读写权限判断
*
* @param context
* @return
*/
public static boolean isPermission(Context context) {
boolean permission = false;
if (Build.VERSION.SDK_INT >= 23) {
int checkReadPhoneStatePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (checkReadPhoneStatePermission != PackageManager.PERMISSION_GRANTED) {
// 弹出对话框接收权限
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
ToastUtils.showWarring("当前应用未拥有存储设备读写权限");
} else if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// 弹出对话框接收权限
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
ToastUtils.showWarring("当前应用未拥有存储设备读写权限");
} else {
permission = true;
}
} else {
permission = true;
}
return permission;
}
public interface DownloadCompleteListener {
void onFinish(Uri uri);
void onError(String s);
}
}

@ -1,14 +0,0 @@
package xyz.fycz.myreader.util;
public class IdHelper {
public static String getId(){
java.util.Date date = new java.util.Date();
int rand = (int)(1+ Math.random()*(25-0+1));
// char c = (char) ('a' + rand);
return String.valueOf(date.getTime()%100000000);
}
}

@ -1,266 +0,0 @@
package xyz.fycz.myreader.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class Lunar
{
private int year;
private int month;
private int day;
private boolean leap;
final static String chineseNumber[] =
{ "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二" };
final static String Big_Or_Small[] =
{ "大", "小", "大", "小", "大", "小", "大", "大", "小", "大", "小", "大" };
private String[] LunarHolDayName =
{ "小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至",
"小暑", "大暑", "立秋", "处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪",
"冬至" };
static SimpleDateFormat chineseDateFormat = new SimpleDateFormat(" yyyy年MM月dd日 ");
final static long[] lunarInfo = new long[]
{ 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0,
0x09ad0, 0x055d2, 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255,
0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, 0x04970, 0x0a4b0,
0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2,
0x04970, 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60,
0x186e3, 0x092e0, 0x1c8d7, 0x0c950, 0x0d4a0, 0x1d8a6, 0x0b550,
0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557,
0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0,
0x0a9a8, 0x0e950, 0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4,
0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0,
0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540,
0x0b5a0, 0x195a6, 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a,
0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, 0x04af5, 0x04970,
0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5,
0x092e0, 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0,
0x0abb7, 0x025d0, 0x092d0, 0x0cab5, 0x0a950, 0x0b4a0, 0x0baa4,
0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930,
0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0,
0x0d260, 0x0ea65, 0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7,
0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0,
0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255,
0x06d20, 0x0ada0 };
// ====== 传回农历 y年的总天数
final private static int yearDays(int y)
{
int i, sum = 348;
for (i = 0x8000; i > 0x8; i >>= 1)
{
if ((lunarInfo[y - 1900] & i) != 0)
sum += 1;
}
return (sum + leapDays(y));
}
// ====== 传回农历 y年闰月的天数
final private static int leapDays(int y)
{
if (leapMonth(y) != 0)
{
if ((lunarInfo[y - 1900] & 0x10000) != 0)
return 30;
else
return 29;
}
else
return 0;
}
// ====== 传回农历 y年闰哪个月 1-12 , 没闰传回 0
final private static int leapMonth(int y)
{
return (int) (lunarInfo[y - 1900] & 0xf);
}
// ====== 传回农历 y年m月的总天数
final private static int monthDays(int y, int m)
{
if ((lunarInfo[y - 1900] & (0x10000 >> m)) == 0)
return 29;
else
return 30;
}
// ====== 传回农历 y年的生肖
final public String animalsYear()
{
final String[] Animals = new String[]
{ "鼠", "牛", "虎", "兔", "龙", "蛇", "马", "羊", "猴", "鸡", "狗", "猪" };
return Animals[(year - 4) % 12];
}
// ====== 传入 月日的offset 传回干支, 0=甲子
final private static String cyclicalm(int num)
{
final String[] Gan = new String[]
{ "甲", "乙", "丙", "丁", "戊", "己", "庚", "辛", "壬", "癸" };
final String[] Zhi = new String[]
{ "子", "丑", "寅", "卯", "辰", "巳", "午", "未", "申", "酉", "戌", "亥" };
return (Gan[num % 10] + Zhi[num % 12]);
}
// ====== 传入 offset 传回干支, 0=甲子
final public String cyclical()
{
int num = year - 1900 + 36;
return (cyclicalm(num));
}
/** */
/**
* 传出y年m月d日对应的农历. yearCyl3:农历年与1864的相差数 ? monCyl4:从1900年1月31日以来,闰月数
* dayCyl5:与1900年1月31日相差的天数,再加40 ?
*
* @param cal
* @return
*/
public Lunar(Calendar cal)
{
// cal.add(cal.get(Calendar.DAY_OF_MONTH),1);
@SuppressWarnings(" unused ")
int yearCyl, monCyl, dayCyl;
int leapMonth = 0;
Date baseDate = null;
try
{
baseDate = chineseDateFormat.parse(" 1900年1月31日 ");
}
catch (ParseException e)
{
e.printStackTrace(); // To change body of catch statement use
// Options | File Templates.
}
// 求出和1900年1月31日相差的天数
int offset = (int) ((cal.getTime().getTime() - baseDate.getTime()) / 86400000L);
dayCyl = offset + 40;
monCyl = 14;
// 用offset减去每农历年的天数
// 计算当天是农历第几天
// i最终结果是农历的年份
// offset是当年的第几天
int iYear, daysOfYear = 0;
for (iYear = 1900; iYear < 2050 && offset > 0; iYear++)
{
daysOfYear = yearDays(iYear);
offset -= daysOfYear;
monCyl += 12;
}
if (offset < 0)
{
offset += daysOfYear;
iYear--;
monCyl -= 12;
}
// 农历年份
year = iYear;
yearCyl = iYear - 1864;
leapMonth = leapMonth(iYear); // 闰哪个月,1-12
leap = false;
// 用当年的天数offset,逐个减去每月(农历)的天数,求出当天是本月的第几天
int iMonth, daysOfMonth = 0;
for (iMonth = 1; iMonth < 13 && offset > 0; iMonth++)
{
// 闰月
if (leapMonth > 0 && iMonth == (leapMonth + 1) && !leap)
{
--iMonth;
leap = true;
daysOfMonth = leapDays(year);
}
else
daysOfMonth = monthDays(year, iMonth);
offset -= daysOfMonth;
// 解除闰月
if (leap && iMonth == (leapMonth + 1))
leap = false;
if (!leap)
monCyl++;
}
// offset为0时,并且刚才计算的月份是闰月,要校正
if (offset == 0 && leapMonth > 0 && iMonth == leapMonth + 1)
{
if (leap)
{
leap = false;
}
else
{
leap = true;
--iMonth;
--monCyl;
}
}
// offset小于0时,也要校正
if (offset < 0)
{
offset += daysOfMonth;
--iMonth;
--monCyl;
}
month = iMonth;
day = offset + 1;
}
public static String getChinaDayString(int day)
{
String chineseTen[] =
{ "初", "十", "廿", "卅" };
int n = day % 10 == 0 ? 9 : day % 10 - 1;
if (day > 30)
return "";
if (day == 10)
return "初十";
else
return chineseTen[day / 10] + chineseNumber[n];
}
public String toString()
{
return /* cyclical() + "年" + */(leap ? "闰" : "")
+ chineseNumber[month - 1] + "月" + getChinaDayString(day);
}
public String numeric_md()
{// 返回阿拉伯数字的阴历日期
String temp_day;
String temp_mon;
temp_mon = month < 10 ? "0" + month : "" + month;
temp_day = day < 10 ? "0" + day : "" + day;
return temp_mon + temp_day;
}
public String get_month()
{// 返回阴历的月份
return chineseNumber[month - 1];
}
public String get_date(int year, int month, int date)
{
/* // 返回阴历的天
LunarCalendar lunarCalendar = LunarCalendar.getInstance();
String str = lunarCalendar.CalculateLunarCalendar(year,month,date);
if(StringHelper.isEmpty(str)){
str = getChinaDayString(day);
}*/
return getChinaDayString(day);
}
public String get_Big_Or_Small()
{// 返回的月份的大或小
return Big_Or_Small[month - 1];
}
}

@ -1,326 +0,0 @@
package xyz.fycz.myreader.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class LunarCalendar {
private int lyear;
private int lmonth;
private int lday;
private boolean leap;
private String solarTerms = "";
private int yearCyl, monCyl, dayCyl;
private String solarFestival = "";
private String lunarFestival = "";
private Calendar baseDate = Calendar.getInstance();
private Calendar offDate = Calendar.getInstance();
private SimpleDateFormat chineseDateFormat = new SimpleDateFormat(
"yyyy年MM月dd日");
final static long[] lunarInfo = new long[]{0x04bd8, 0x04ae0, 0x0a570,
0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2,
0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0,
0x0ada2, 0x095b0, 0x14977, 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50,
0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, 0x06566,
0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0,
0x1c8d7, 0x0c950, 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4,
0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, 0x06ca0, 0x0b550,
0x15355, 0x04da0, 0x0a5d0, 0x14573, 0x052d0, 0x0a9a8, 0x0e950,
0x06aa0, 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260,
0x0f263, 0x0d950, 0x05b57, 0x056a0, 0x096d0, 0x04dd5, 0x04ad0,
0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b5a0, 0x195a6,
0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40,
0x0af46, 0x0ab60, 0x09570, 0x04af5, 0x04970, 0x064b0, 0x074a3,
0x0ea50, 0x06b58, 0x055c0, 0x0ab60, 0x096d5, 0x092e0, 0x0c960,
0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0,
0x092d0, 0x0cab5, 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9,
0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, 0x07954, 0x06aa0,
0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65,
0x0d530, 0x05aa0, 0x076a3, 0x096d0, 0x04bd7, 0x04ad0, 0x0a4d0,
0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, 0x0b5a0, 0x056d0, 0x055b2,
0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0};
final static String[] Gan = new String[]{"甲", "乙", "丙", "丁", "戊", "己",
"庚", "辛", "壬", "癸"};
final static String[] Zhi = new String[]{"子", "丑", "寅", "卯", "辰", "巳",
"午", "未", "申", "酉", "戌", "亥"};
final static String[] Animals = new String[]{"鼠", "牛", "虎", "兔", "龙",
"蛇", "马", "羊", "猴", "鸡", "狗", "猪"};
final static String[] SolarTerm = new String[]{"小寒", "大寒", "立春", "雨水",
"惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至", "小暑", "大暑", "立秋",
"处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪", "冬至"};
final static long[] STermInfo = new long[]{0, 21208, 42467, 63836, 85337,
107014, 128867, 150921, 173149, 195551, 218072, 240693, 263343,
285989, 308563, 331033, 353350, 375494, 397447, 419210, 440795,
462224, 483532, 504758};
final static String chineseMonthNumber[] = {"正", "二", "三", "四", "五", "六",
"七", "八", "九", "十", "冬", "腊"};
final static String[] sFtv = new String[]{"0101*元旦", "0214 情人节",
"0308 妇女节", "0312 植树节", /*"0314 国际警察日", "0315 消费者权益日", "0323 世界气象日",*/
"0401 愚人节", /*"0407 世界卫生日",*/ "0501*劳动节", "0504 青年节", /*"0508 红十字日",*/
"0512 护士节", /*"0515 国际家庭日", "0517 世界电信日", "0519 全国助残日", "0531 世界无烟日",*/
"0601 儿童节", /*"0605 世界环境日", "0606 全国爱眼日", "0623 奥林匹克日", "0625 全国土地日",*/
/*"0626 反毒品日",*/ "0701 建党节", /*"0707 抗战纪念日", "0711 世界人口日",*/ "0801 建军节",
/* "0908 国际扫盲日", "0909 毛xx逝世纪念",*/ "0910 教师节", /*"0917 国际和平日",*/
/* "0920 国际爱牙日", "0922 国际聋人节", "0927 世界旅游日", "0928 孔子诞辰",*/ "1001*国庆节",
/* "1004 世界动物日", "1006 老人节", "1007 国际住房日", "1009 世界邮政日", "1015 国际盲人节",*/
/* "1016 世界粮食日", "1024 联合国日", */"1031 万圣节",/* "1108 中国记者日", "1109 消防宣传日",*/
/* "1112 孙中山诞辰", "1114 世界糖尿病日", "1117 国际大学生节",*/ "1128 感恩节",
/* "1201 世界艾滋病日", "1203 世界残疾人日", "1209 世界足球日", "1220 澳门回归",*/
"1225 圣诞节", /*"1226 毛xx诞辰"*/};
final static String[] lFtv = {"0101*春节", "0115 元宵", "0505 端午",
"0707 七夕", "0815 中秋", "0909 重阳", "1208 腊八", "1223 小年",
"0100*除夕"};
final static String[] wFtv = {"0521 母亲节", "0631 父亲节"};//每年6月第3个星期日是父亲节,5月的第2个星期日是母亲节
//星期日是一个周的第1天第3个星期日也就是第3个完整周的第一天
//
private LunarCalendar() {
baseDate.setMinimalDaysInFirstWeek(7);//设置一个月的第一个周是一个完整周
}
final private static int lYearDays(int y)//====== 传回农历 y年的总天数
{
int i, sum = 348;
for (i = 0x8000; i > 0x8; i >>= 1) {
if ((lunarInfo[y - 1900] & i) != 0)
sum += 1;
}
return (sum + leapDays(y));
}
final private static int leapDays(int y)//====== 传回农历 y年闰月的天数
{
if (leapMonth(y) != 0) {
if ((lunarInfo[y - 1900] & 0x10000) != 0)
return 30;
else
return 29;
} else
return 0;
}
final private static int leapMonth(int y)//====== 传回农历 y年闰哪个月 1-12 , 没闰传回 0
{
return (int) (lunarInfo[y - 1900] & 0xf);
}
final public static int monthDays(int y, int m)//====== 传回农历 y年m月的总天数
{
if ((lunarInfo[y - 1900] & (0x10000 >> m)) == 0)
return 29;
else
return 30;
}
final private static String AnimalsYear(int y)//====== 传回农历 y年的生肖
{
return Animals[(y - 4) % 12];
}
final private static String cyclical(int num)//====== 传入 的offset 传回干支,
// 0=甲子
{
return (Gan[num % 10] + Zhi[num % 12]);
}
// ===== 某年的第n个节气为几日(从0小寒起算)
final private int sTerm(int y, int n) {
offDate.set(1900, 0, 6, 2, 5, 0);
long temp = offDate.getTime().getTime();
offDate
.setTime(new Date(
(long) ((31556925974.7 * (y - 1900) + STermInfo[n] * 60000L) + temp)));
return offDate.get(Calendar.DAY_OF_MONTH);
}
/**
* 传出y年m月d日对应的农历.
*/
public String CalculateLunarCalendar(int y, int m, int d) {
int leapMonth = 0;
try {
baseDate.setTime(chineseDateFormat.parse("1900年1月31日"));
} catch (ParseException e) {
e.printStackTrace();
}
long base = baseDate.getTimeInMillis();
try {
baseDate.setTime(chineseDateFormat.parse(y + "年" + m + "月" + d
+ "日"));
} catch (ParseException e) {
e.printStackTrace();
}
long obj = baseDate.getTimeInMillis();
int offset = (int) ((obj - base) / 86400000L);
//System.out.println(offset);
//求出和1900年1月31日相差的天数
dayCyl = offset + 40;//干支天
monCyl = 14;//干支月
//用offset减去每农历年的天数
// 计算当天是农历第几天
//i最终结果是农历的年份
//offset是当年的第几天
int iYear, daysOfYear = 0;
for (iYear = 1900; iYear < 2050 && offset > 0; iYear++) {
daysOfYear = lYearDays(iYear);
offset -= daysOfYear;
monCyl += 12;
}
if (offset < 0) {
offset += daysOfYear;
iYear--;
monCyl -= 12;
}
//农历年份
lyear = iYear;
yearCyl = iYear - 1864;//***********干支年**********//
leapMonth = leapMonth(iYear); //闰哪个月,1-12
leap = false;
//用当年的天数offset,逐个减去每月(农历)的天数,求出当天是本月的第几天
int iMonth, daysOfMonth = 0;
for (iMonth = 1; iMonth < 13 && offset > 0; iMonth++) {
//闰月
if (leapMonth > 0 && iMonth == (leapMonth + 1) && !leap) {
--iMonth;
leap = true;
daysOfMonth = leapDays(iYear);
} else
daysOfMonth = monthDays(iYear, iMonth);
offset -= daysOfMonth;
//解除闰月
if (leap && iMonth == (leapMonth + 1))
leap = false;
if (!leap)
monCyl++;
}
//offset为0时,并且刚才计算的月份是闰月,要校正
if (offset == 0 && leapMonth > 0 && iMonth == leapMonth + 1) {
if (leap) {
leap = false;
} else {
leap = true;
--iMonth;
--monCyl;
}
}
//offset小于0时,也要校正
if (offset < 0) {
offset += daysOfMonth;
--iMonth;
--monCyl;
}
lmonth = iMonth;
lday = offset + 1;
//******************计算节气**********//
if (d == sTerm(y, (m - 1) * 2))
solarTerms = SolarTerm[(m - 1) * 2];
else if (d == sTerm(y, (m - 1) * 2 + 1))
solarTerms = SolarTerm[(m - 1) * 2 + 1];
else
solarTerms = "";
//计算公历节日
this.solarFestival = "";
for (int i = 0; i < sFtv.length; i++) {
if (Integer.parseInt(sFtv[i].substring(0, 2)) == m
&& Integer.parseInt(sFtv[i].substring(2, 4)) == d) {
solarFestival = sFtv[i].substring(5);
break;
}
}
//计算农历节日
this.lunarFestival = "";
for (int i = 0; i < lFtv.length; i++) {
if (Integer.parseInt(lFtv[i].substring(0, 2)) == lmonth
&& Integer.parseInt(lFtv[i].substring(2, 4)) == lday) {
lunarFestival = lFtv[i].substring(5);
break;
}
}
//计算月周节日
// System.out.println(baseDate.get(Calendar.WEEK_OF_MONTH) + ""
// + baseDate.get(Calendar.DAY_OF_WEEK));
for (int i = 0; i < wFtv.length; i++) {
if (Integer.parseInt(wFtv[i].substring(0, 2)) == m
&& Integer.parseInt(wFtv[i].substring(2, 3)) == baseDate
.get(Calendar.WEEK_OF_MONTH)
&& Integer.parseInt(wFtv[i].substring(3, 4)) == baseDate
.get(Calendar.DAY_OF_WEEK)) {
solarFestival += wFtv[i].substring(5);
}
}
if(!StringHelper.isEmpty(lunarFestival)){
return lunarFestival;
}
if(!StringHelper.isEmpty(solarFestival)){
return solarFestival;
}
if(!StringHelper.isEmpty(solarTerms)){
return solarTerms;
}
return "";
}
//set方法
public void set(int y, int m, int d) {
CalculateLunarCalendar(y, m, d);
}
public void set(Calendar cal) {
CalculateLunarCalendar(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH));
}
// //get方法组
public static LunarCalendar getInstance() {
return new LunarCalendar();
}
}

@ -1,5 +1,7 @@
package xyz.fycz.myreader.util;
import android.os.Build;
import android.widget.Toast;
import androidx.annotation.NonNull;
import es.dmoral.toasty.Toasty;
import xyz.fycz.myreader.R;
@ -16,50 +18,76 @@ public class ToastUtils {
}
public static void show(@NonNull String msg) {
MyApplication.runOnUiThread(() -> Toasty.custom(MyApplication.getmContext(), msg,
MyApplication.runOnUiThread(() -> {
if (showOld(msg)) return;
Toasty.custom(MyApplication.getmContext(), msg,
MyApplication.getmContext().getDrawable(R.drawable.ic_smile_face),
MyApplication.getmContext().getColor(R.color.toast_default),
MyApplication.getmContext().getColor(R.color.white),
Toasty.LENGTH_SHORT, true, true).show());
Toasty.LENGTH_SHORT, true, true).show();
});
}
//红色
public static void showError(@NonNull String msg) {
MyApplication.runOnUiThread(() -> Toasty.custom(MyApplication.getmContext(), msg,
MyApplication.runOnUiThread(() -> {
if (showOld(msg)) return;
Toasty.custom(MyApplication.getmContext(), msg,
MyApplication.getmContext().getDrawable(R.drawable.ic_error),
MyApplication.getmContext().getColor(R.color.toast_red),
MyApplication.getmContext().getColor(R.color.white),
Toasty.LENGTH_SHORT, true, true).show());
Toasty.LENGTH_SHORT, true, true).show();
});
}
//绿色
public static void showSuccess(@NonNull String msg) {
MyApplication.runOnUiThread(() -> Toasty.custom(MyApplication.getmContext(), msg,
MyApplication.runOnUiThread(() -> {
if (showOld(msg)) return;
Toasty.custom(MyApplication.getmContext(), msg,
MyApplication.getmContext().getDrawable(R.drawable.ic_success),
MyApplication.getmContext().getColor(R.color.toast_green),
MyApplication.getmContext().getColor(R.color.white),
Toasty.LENGTH_SHORT, true, true).show());
Toasty.LENGTH_SHORT, true, true).show();
});
}
//蓝色
public static void showInfo(@NonNull String msg) {
MyApplication.runOnUiThread(() -> Toasty.custom(MyApplication.getmContext(), msg,
MyApplication.runOnUiThread(() -> {
if (showOld(msg)) return;
Toasty.custom(MyApplication.getmContext(), msg,
MyApplication.getmContext().getDrawable(R.drawable.ic_smile_face),
MyApplication.getmContext().getColor(R.color.toast_blue),
MyApplication.getmContext().getColor(R.color.white),
Toasty.LENGTH_SHORT, true, true).show());
Toasty.LENGTH_SHORT, true, true).show();
});
}
//黄色
public static void showWarring(@NonNull String msg) {
MyApplication.runOnUiThread(() -> Toasty.warning(MyApplication.getmContext(), msg, Toasty.LENGTH_SHORT, true).show());
MyApplication.runOnUiThread(() -> {
if (showOld(msg)) return;
Toasty.warning(MyApplication.getmContext(), msg, Toasty.LENGTH_SHORT, true).show();
});
}
public static void showExit(@NonNull String msg) {
MyApplication.runOnUiThread(() -> Toasty.custom(MyApplication.getmContext(), msg,
MyApplication.runOnUiThread(() -> {
if (showOld(msg)) return;
Toasty.custom(MyApplication.getmContext(), msg,
MyApplication.getmContext().getDrawable(R.drawable.ic_cry_face),
MyApplication.getmContext().getColor(R.color.toast_blue),
MyApplication.getmContext().getColor(R.color.white),
Toasty.LENGTH_SHORT, true, true).show());
Toasty.LENGTH_SHORT, true, true).show();
});
}
private static boolean showOld(@NonNull String msg){
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
Toast.makeText(MyApplication.getmContext(), msg, Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
}

@ -0,0 +1,68 @@
package xyz.fycz.myreader.util.utils;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.google.android.material.snackbar.Snackbar;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
/**
* Created by zhouas666 on 2017/12/28.
* Snackbar工具类
*/
public class SnackbarUtils {
public static void show(@NonNull View view, @NonNull String msg) {
show(view, msg, true, null, null);
}
/**
* 展示snackBar
*
* @param view view
* @param msg 消息
* @param isDismiss 是否自动消失
* @param action 事件名
* @param iSnackBarClickEvent 事件处理接口
*/
public static void show(@NonNull View view, @NonNull String msg, boolean isDismiss, String action, final ISnackBarClickEvent iSnackBarClickEvent) {
//snackBar默认显示时间为LENGTH_LONG
int duringTime = Snackbar.LENGTH_LONG;
if (!isDismiss) {
duringTime = Snackbar.LENGTH_INDEFINITE;
}
Snackbar snackbar;
snackbar = Snackbar.make(view, msg, duringTime);
if (action != null)
snackbar.setAction(action, view1 -> {
//以接口方式发送出去,便于使用者处理自己的业务逻辑
iSnackBarClickEvent.clickEvent();
});
//设置snackBar和titleBar颜色一致
snackbar.getView().setBackgroundColor(MyApplication.getmContext().getColor(R.color.textPrimary));
//设置action文字的颜色
snackbar.setActionTextColor(MyApplication.getmContext().getColor(R.color.md_white_1000));
//设置snackBar图标 这里是获取到snackBar的textView 然后给textView增加左边图标的方式来实现的
View snackBarView = snackbar.getView();
TextView textView = snackBarView.findViewById(R.id.snackbar_text);
/*Drawable drawable = getResources().getDrawable(R.mipmap.ic_notification);//图片自己选择
drawable.setBounds(0, 0, drawable.getMinimumWidth(), drawable.getMinimumHeight());
textView.setCompoundDrawables(drawable, null, null, null);*/
//增加文字和图标的距离
textView.setCompoundDrawablePadding(20);
//展示snackBar
snackbar.show();
}
/**
* snackBar的action事件
*/
public interface ISnackBarClickEvent {
void clickEvent();
}
}

@ -0,0 +1,161 @@
package xyz.fycz.myreader.util.utils;
import android.app.Activity;
import android.os.Build;
import android.view.View;
import android.view.WindowManager;
/**
* 基于 Android 4.4
*
* 主要参数说明:
*
* SYSTEM_UI_FLAG_FULLSCREEN : 隐藏StatusBar
* SYSTEM_UI_FLAG_HIDE_NAVIGATION : 隐藏NavigationBar
* SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN: 视图扩展到StatusBar的位置并且StatusBar不消失
* 这里需要一些处理一般是将StatusBar设置为全透明或者半透明之后还需要使用fitSystemWindows=防止视图扩展到Status
* Bar上面(会在StatusBar上加一层View该View可被移动)
* SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION: 视图扩展到NavigationBar的位置
* SYSTEM_UI_FLAG_LAYOUT_STABLE:稳定效果
* SYSTEM_UI_FLAG_IMMERSIVE_STICKY:保证点击任意位置不会退出
*
* 可设置特效说明:
* 1. 全屏特效
* 2. 全屏点击不退出特效
* 3. 注意在19 <=sdk <=21 时候必须通过Window设置透明栏
*/
public class SystemBarUtils {
private static final int UNSTABLE_STATUS = View.SYSTEM_UI_FLAG_FULLSCREEN;
private static final int UNSTABLE_NAV = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
private static final int STABLE_STATUS = View.SYSTEM_UI_FLAG_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
private static final int STABLE_NAV = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
private static final int EXPAND_STATUS = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
private static final int EXPAND_NAV = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
//设置隐藏StatusBar(点击任意地方会恢复)
public static void hideUnStableStatusBar(Activity activity){
//App全屏,隐藏StatusBar
setFlag(activity, UNSTABLE_STATUS);
}
public static void showUnStableStatusBar(Activity activity){
clearFlag(activity, UNSTABLE_STATUS);
}
//隐藏NavigationBar(点击任意地方会恢复)
public static void hideUnStableNavBar(Activity activity){
setFlag(activity,UNSTABLE_NAV);
}
public static void showUnStableNavBar(Activity activity){
clearFlag(activity,UNSTABLE_NAV);
}
public static void hideStableStatusBar(Activity activity){
//App全屏,隐藏StatusBar
setFlag(activity,STABLE_STATUS);
}
public static void showStableStatusBar(Activity activity){
clearFlag(activity,STABLE_STATUS);
}
public static void hideStableNavBar(Activity activity){
//App全屏,隐藏StatusBar
setFlag(activity,STABLE_NAV);
}
public static void showStableNavBar(Activity activity){
clearFlag(activity,STABLE_NAV);
}
/**
* 视图扩充到StatusBar
*/
public static void expandStatusBar(Activity activity){
setFlag(activity, EXPAND_STATUS);
}
/**
* 视图扩充到NavBar
* @param activity
*/
public static void expandNavBar(Activity activity){
setFlag(activity, EXPAND_NAV);
}
public static void transparentStatusBar(Activity activity){
if (Build.VERSION.SDK_INT >= 21){
expandStatusBar(activity);
activity.getWindow()
.setStatusBarColor(activity.getResources().getColor(android.R.color.transparent));
}
else if (Build.VERSION.SDK_INT >= 19){
WindowManager.LayoutParams attrs = activity.getWindow().getAttributes();
attrs.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | attrs.flags);
activity.getWindow().setAttributes(attrs);
}
}
public static void transparentNavBar(Activity activity){
if (Build.VERSION.SDK_INT >= 21){
expandNavBar(activity);
//下面这个方法在sdk:21以上才有
activity.getWindow()
.setNavigationBarColor(activity.getResources().getColor(android.R.color.transparent));
}
}
public static void setFlag(Activity activity, int flag){
if (Build.VERSION.SDK_INT >= 19){
View decorView = activity.getWindow().getDecorView();
int option = decorView.getSystemUiVisibility() | flag;
decorView.setSystemUiVisibility(option);
}
}
//取消flag
public static void clearFlag(Activity activity, int flag){
if (Build.VERSION.SDK_INT >= 19){
View decorView = activity.getWindow().getDecorView();
int option = decorView.getSystemUiVisibility() & (~flag);
decorView.setSystemUiVisibility(option);
}
}
public static void setToggleFlag(Activity activity, int option){
if (Build.VERSION.SDK_INT >= 19){
if (isFlagUsed(activity,option)){
clearFlag(activity,option);
}
else {
setFlag(activity,option);
}
}
}
/**
* @param activity
* @return flag是否已被使用
*/
public static boolean isFlagUsed(Activity activity, int flag) {
int currentFlag = activity.getWindow().getDecorView().getSystemUiVisibility();
if((currentFlag & flag)
== flag) {
return true;
}else {
return false;
}
}
}

@ -13,12 +13,12 @@ import java.util.ArrayList;
*/
public interface ReadCrawler {
String getSearchLink();
String getCharset();
String getSearchCharset();
String getNameSpace();
Boolean isPost();
String getContentFormHtml(String html);
ArrayList<Chapter> getChaptersFromHtml(String html);
ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html);
String getSearchLink(); // 书源的搜索url
String getCharset(); // 书源的字符编码
String getSearchCharset(); // 书源搜索关键字的字符编码,和书源的字符编码就行
String getNameSpace(); // 书源主页地址
Boolean isPost(); // 是否以post请求搜索
String getContentFormHtml(String html); // 获取书籍内容规则
ArrayList<Chapter> getChaptersFromHtml(String html); // 获取书籍章节列表规则
ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html); // 搜索书籍规则
}

@ -21,6 +21,7 @@ import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.RxUtils;
import xyz.fycz.myreader.util.utils.ScreenUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
import xyz.fycz.myreader.widget.animation.PageAnimation;
import java.io.BufferedReader;
import java.io.File;
@ -1036,7 +1037,6 @@ public abstract class PageLoader {
//对内容进行绘制
for (int i = mCurPage.titleLines; i < mCurPage.lines.size(); ++i) {
str = mCurPage.lines.get(i);
canvas.drawText(str, mMarginWidth, top, mTextPaint);
if (str.endsWith("\n")) {
top += para;
@ -1592,7 +1592,22 @@ public abstract class PageLoader {
}
return strLength;
}
/**
* @return 本页内容
*/
public String getContent() {
if (mCurPageList == null) return null;
if (mCurPageList.size() == 0) return null;
TxtPage txtPage = mCurPage;
StringBuilder s = new StringBuilder();
int size = txtPage.lines.size();
//int start = mPageMode == PageMode.SCROLL ? Math.min(Math.max(0, linePos), size - 1) : 0;
int start = 0;
for (int i = start; i < size; i++) {
s.append(txtPage.lines.get(i));
}
return s.toString();
}
/*****************************************interface*****************************************/
public interface OnPageChangeListener {
@ -1631,7 +1646,5 @@ public abstract class PageLoader {
*/
void onPageChange(int pos);
void preLoading();
}
}

@ -11,6 +11,7 @@ import android.view.ViewConfiguration;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.service.ChapterService;
import xyz.fycz.myreader.util.utils.SnackbarUtils;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.widget.animation.*;
@ -251,7 +252,11 @@ public class PageView extends View {
*/
private boolean hasPrevPage() {
mTouchListener.prePage();
return mPageLoader.prev();
boolean hasPrevPage = mPageLoader.prev();
if (!hasPrevPage){
showSnackBar("已经是第一页了");
}
return hasPrevPage;
}
/**
@ -262,6 +267,9 @@ public class PageView extends View {
private boolean hasNextPage() {
boolean hasNextPage = mPageLoader.next();
mTouchListener.nextPage(hasNextPage);
if (!hasNextPage){
showSnackBar("已经是最后一页了");
}
return hasNextPage;
}
@ -270,6 +278,16 @@ public class PageView extends View {
mPageLoader.pageCancel();
}
/**
* 显示tips
*
* @param msg
*/
public void showSnackBar(String msg) {
SnackbarUtils.show(this, msg);
}
@Override
public void computeScroll() {
//进行滑动

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="100%"
android:toYDelta="0"
android:duration="200"/>
</set>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromYDelta="0"
android:toYDelta="100%"
android:duration="200"/>
</set>

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0"
android:fromYDelta="-100%"
android:toXDelta="0"
android:toYDelta="0"
android:duration="200"/>
</set>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="0"
android:toYDelta="-100%"
android:duration="200"/>
</set>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 898 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 834 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="@color/white"
android:pathData="M5.938,4v16h1.414l4.648-2.789L16.649,20h1.411V4H5.938z M16.606,18.278l-4.605-2.763l-4.609,2.764 V5.454h9.215V18.278z" />
android:fillColor="#FFFFFF"
android:pathData="M364.9,460.4c-51.5,0 -93.5,-41.9 -93.5,-93.5 0,-51.5 41.9,-93.5 93.5,-93.5s93.5,41.9 93.5,93.5c-0.1,51.5 -42,93.5 -93.5,93.5zM364.9,319.2c-26.3,0 -47.7,21.4 -47.7,47.7 0,26.3 21.4,47.7 47.7,47.7s47.7,-21.4 47.7,-47.7c0,-26.3 -21.4,-47.7 -47.7,-47.7z"/>
<path
android:fillColor="#FFFFFF"
android:pathData="M951.1,588.1L437.3,73.4c-5.3,-5.4 -12.5,-8.4 -20,-8.4L93,64c-7.7,0 -15.2,3 -20.6,8.5 -5.5,5.4 -8.5,12.8 -8.4,20.5l0.7,321.7c0.1,7.4 3.1,14.6 8.4,19.8l359,360.7c10.6,10.6 27.6,11.2 38.9,1.4 12.6,-10.9 13.3,-30.1 1.6,-41.9L127.8,408.3c-3.9,-3.9 -6.1,-9.1 -6.2,-14.6l0.3,-250.9c-0.2,-11.8 9.4,-21.4 21.1,-21.3l253.4,0.6c5.5,0 10.8,2.2 14.7,6.2l464.5,465.5c8.2,8.2 8.2,21.5 0,29.7L626.2,873c-8.2,8.2 -21.5,8.2 -29.7,0l-19,-19c-10.6,-10.6 -27.6,-11.2 -38.9,-1.4 -12.6,10.9 -13.3,30.1 -1.6,41.9l53.9,53.9c11.3,11.3 29.6,11.3 40.9,0L951.1,629c11.3,-11.3 11.3,-29.6 0,-40.9z"/>
</vector>

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FFFFFF"
android:pathData="M640,320 L192,320C156.61,320 128,348.61 128,384l0,512c0,35.33 28.61,64 64,64l448,0c35.39,0 64,-28.67 64,-64L704,384C704,348.61 675.39,320 640,320zM640,896 L192,896 192,384l448,0L640,896zM832,128 L352,128C334.34,128 320,142.34 320,160S334.34,192 352,192L832,192l0,544c0,17.66 14.34,32 32,32s32,-14.34 32,-32L896,192C896,156.61 867.39,128 832,128z"/>
</vector>

@ -1,14 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FFFFFF"
android:pathData="M18.545,18.304c0,0.131-0.11,0.242-0.241,0.242H5.696c-0.131,0-0.242-0.111-0.242-0.242V5.697 c0-0.131,0.111-0.243,0.242-0.243H12V4H5.696C4.76,4,4,4.762,4,5.697v12.606C4,19.239,4.76,20,5.696,20h12.607 C19.239,20,20,19.239,20,18.304v-6.303h-1.455V18.304z" />
android:pathData="M762.24,506.88a32,32 0,0 1,-46.08 -44.16l55.68,-58.24a125.76,125.76 0,0 0,-12.16 -171.2c-48.32,-50.24 -119.68,-54.72 -160,-12.48L444.16,384a125.76,125.76 0,0 0,12.16 171.2,32 32,0 0,1 0,45.12 32,32 0,0 1,-45.44 0,189.44 189.44,0 0,1 -12.16,-259.52l155.52,-162.24c66.88,-69.76 180.48,-64 252.8,12.8A189.44,189.44 0,0 1,818.24 448z"/>
<path
android:fillColor="#FFFFFF"
android:pathData="M 19.272 4 L 13.843 4 L 13.843 5.454 L 17.517 5.454 L 7.464 15.508 L 8.491 16.536 L 18.545 6.482 L 18.545 10.157 L 20 10.157 L 20 4.727 L 20 4 Z" />
android:pathData="M229.76,517.12a32,32 0,0 1,46.08 44.16l-55.68,58.24a125.76,125.76 0,0 0,12.16 171.2c48.32,50.24 119.68,54.72 160,12.48L547.84,640a125.76,125.76 0,0 0,-12.16 -171.2,32 32,0 0,1 0,-45.12 32,32 0,0 1,45.44 0,189.44 189.44,0 0,1 12.16,259.52L437.76,845.44c-66.88,69.76 -180.48,64 -252.8,-12.8A189.44,189.44 0,0 1,173.76 576z"/>
</vector>

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24.117188dp"
android:height="24dp"
android:viewportWidth="1029"
android:viewportHeight="1024">
<path
android:fillColor="#9fc6ff"
android:pathData="M918.39,829.23 L749.22,660.07c-2.93,-2.93 -6.58,-4.37 -9.99,-6.58 98.28,-138.95 86.08,-332.16 -38.34,-456.58 -139.01,-139.07 -364.46,-139.07 -503.54,0 -139.07,139.01 -139.07,364.4 0,503.54 125.79,125.79 321.64,136.56 460.95,34.81 2.21,3.17 3.59,6.64 6.34,9.45l169.16,169.16c23.45,23.39 61.25,23.39 84.58,0C941.71,890.43 941.71,852.56 918.39,829.23L918.39,829.23zM629.65,629.09c-99.65,99.65 -261.22,99.65 -360.93,0 -99.65,-99.65 -99.65,-261.28 0,-360.93 99.65,-99.65 261.22,-99.65 360.93,0C729.36,367.87 729.36,529.43 629.65,629.09L629.65,629.09z"/>
</vector>

@ -18,7 +18,7 @@
android:fitsSystemWindows="true"
app:titleTextAppearance="@style/toolbar_title_textStyle"
app:subtitleTextAppearance="@style/toolbar_subtitle_textStyle"
android:theme="@style/Theme.ToolBar.Dark.Menu">
android:theme="?attr/actionBarStyle">
<com.google.android.material.tabs.TabLayout
android:id="@+id/catalog_tab"

@ -1,241 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--背景图片-->
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
app:srcCompat="@drawable/bg_login" />
<!--添加虚化层-->
<io.alterac.blurkit.BlurLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:blk_blurRadius="5"
app:blk_fps="0" />
<!-- 猫头鹰-->
<!-- <RelativeLayout-->
<!-- android:layout_width="300dp"-->
<!-- android:layout_height="200dp"-->
<!-- android:layout_centerHorizontal="true"-->
<!-- android:layout_alignTop="@+id/bg"-->
<!-- android:layout_marginTop="-75dp">-->
<!-- &lt;!&ndash;头像&ndash;&gt;-->
<!-- <ImageView-->
<!-- android:id="@+id/iv_head"-->
<!-- android:layout_width="wrap_content"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:src="@drawable/owl_head"-->
<!-- android:layout_centerHorizontal="true"-->
<!-- />-->
<!-- &lt;!&ndash;手掌&ndash;&gt;-->
<!-- <ImageView-->
<!-- android:id="@+id/iv_left_hand"-->
<!-- android:layout_width="50dp"-->
<!-- android:layout_height="60dp"-->
<!-- android:src="@drawable/icon_hand"-->
<!-- android:layout_alignParentLeft="true"-->
<!-- android:layout_alignBottom="@+id/iv_head"-->
<!-- android:layout_marginBottom="-25dp"-->
<!-- android:layout_marginLeft="10dp"/>-->
<!-- <ImageView-->
<!-- android:id="@+id/iv_right_hand"-->
<!-- android:layout_width="50dp"-->
<!-- android:layout_height="60dp"-->
<!-- android:src="@drawable/icon_hand"-->
<!-- android:layout_alignParentRight="true"-->
<!-- android:layout_alignBottom="@+id/iv_head"-->
<!-- android:layout_marginBottom="-25dp"-->
<!-- android:layout_marginRight="10dp"/>-->
<!-- &lt;!&ndash;翅膀&ndash;&gt;-->
<!-- <ImageView-->
<!-- android:id="@+id/iv_left_arm"-->
<!-- android:layout_width="65dp"-->
<!-- android:layout_height="40dp"-->
<!-- android:src="@drawable/arm_left"-->
<!-- android:layout_below="@+id/iv_head"-->
<!-- android:layout_alignParentLeft="true"-->
<!-- android:layout_marginLeft="20dp"/>-->
<!-- <ImageView-->
<!-- android:id="@+id/iv_right_arm"-->
<!-- android:layout_width="65dp"-->
<!-- android:layout_height="40dp"-->
<!-- android:src="@drawable/arm_right"-->
<!-- android:layout_below="@+id/iv_head"-->
<!-- android:layout_alignParentRight="true"-->
<!-- android:layout_marginRight="20dp"/>-->
<!-- </RelativeLayout>-->
<View
android:id="@+id/bg"
android:layout_width="match_parent"
android:layout_height="280dp"
android:layout_centerInParent="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:background="@drawable/input_bg_shape" />
<io.alterac.blurkit.BlurLayout
android:layout_width="match_parent"
android:layout_height="280dp"
android:layout_centerInParent="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
app:blk_blurRadius="20"
app:blk_fps="0" />
<!--添加标题和输入框-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:orientation="vertical"
android:padding="20dp">
<!--标题-->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/text_login"
android:textAlignment="center"
android:textColor="@color/textSecondary"
android:textSize="20dp" />
<!--添加输入框-->
<!--<EditText
android:id="@+id/et_user"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/editview_shape"
android:drawableStart="@drawable/iconfont_user"
android:drawablePadding="10dp"
android:hint="@string/et_account_tip"
android:inputType="text"
android:maxLines="1"
android:paddingStart="10dp"
android:textSize="18dp" />-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_user"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="14">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_account_tip"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>
<!--<EditText
android:id="@+id/et_password"
style="@style/EditTextStyle"
android:background="@drawable/editview_shape"
android:drawableStart="@drawable/iconfont_password"
android:hint="@string/et_password_tip"
android:inputType="textPassword"
android:textSize="18dp" />-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="16"
app:passwordToggleEnabled="true"
app:passwordToggleTintMode="src_in">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_password_tip"
android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<!--<EditText
android:id="@+id/et_captcha"
style="@style/EditTextStyle"
android:layout_width="match_parent"
android:layout_weight="1"
android:background="@drawable/editview_shape"
android:hint="@string/et_rp_captcha"
android:inputType="text"
android:textSize="18dp" />-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_captcha"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="4">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_rp_captcha"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/iv_captcha"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="3"
android:layout_gravity="center_vertical"
android:paddingStart="7dp" />
</LinearLayout>
<!--登录按钮-->
<Button
android:id="@+id/bt_login"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="20dp"
android:background="@drawable/login_btn_selector"
android:enabled="false"
android:text="@string/text_login"
android:textAlignment="center"
android:textColor="@color/textPrimaryInverted"
android:textSize="25dp" />
<TextView
android:id="@+id/tv_register"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="@string/tv_register"
android:textColor="@color/colorAccent" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>

@ -0,0 +1,199 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/read_abl_top_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/read_menu_bg"
android:visibility="gone"
tools:visibility="visible">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
style="@style/NoPaddingToolbar"
android:layout_width="match_parent"
android:layout_height="45dp"
android:minHeight="?attr/actionBarSize"
android:gravity="center_vertical"
android:theme="?attr/actionBarStyle"
android:transitionName="sharedView"
app:title="大主宰"
app:layout_scrollFlags="scroll|enterAlways"
app:popupTheme="@style/AppTheme.PopupOverlay">
</androidx.appcompat.widget.Toolbar>
<View
android:layout_width="fill_parent"
android:layout_height="0.5dp"
android:background="@color/sys_dialog_setting_line" />
<LinearLayout
android:id="@+id/ll_chapter_view"
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="vertical">
<TextView android:id="@+id/tv_chapter_title_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginEnd="14dp"
android:textSize="15sp"
android:text="章节名称"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@color/read_menu_text">
</TextView>
<TextView android:id="@+id/tv_chapter_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="14dp"
android:layout_marginEnd="14dp"
android:ellipsize="end"
android:textSize="13sp"
android:text="章节链接"
android:maxLines="1"
android:textColor="@color/read_menu_text">
</TextView>
</LinearLayout>
</LinearLayout>
</com.google.android.material.appbar.AppBarLayout>
<!--阅读页面-->
<xyz.fycz.myreader.widget.page.PageView
android:id="@+id/read_pv_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
tools:visibility="gone"/>
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp">
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar
android:id="@+id/pb_nextPage"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:splitTrack="false"
app:seekBarRotation="CW270"
android:visibility="invisible"
tools:visibility="visible"/>
</com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper>
<ProgressBar
android:id="@+id/pb_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
tools:visibility="visible"/>
<!--切换页面提示-->
<TextView
android:id="@+id/read_tv_page_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="@+id/read_ll_bottom_menu"
android:layout_centerHorizontal="true"
android:layout_marginBottom="10dp"
android:background="@color/read_menu_bg"
android:gravity="center"
android:paddingLeft="12dp"
android:paddingTop="8dp"
android:paddingRight="12dp"
android:paddingBottom="8dp"
android:text="1/12"
android:textColor="@color/md_white_1000"
android:visibility="gone"/>
<!--底部页面-->
<LinearLayout
android:id="@+id/read_ll_bottom_menu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="@color/read_menu_bg"
android:clickable="true"
android:orientation="vertical"
android:visibility="gone"
tools:visibility="visible">
<!--页面进度栏-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/read_tv_pre_chapter"
style="@style/MAppTheme.TextAppearance.Read.BottomMenu"
android:layout_width="80dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="上一章"/>
<SeekBar
android:id="@+id/read_sb_chapter_progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:max="100"
android:maxHeight="3dp"
android:minHeight="3dp"
android:progressDrawable="@drawable/seekbar_bg"
android:thumb="@mipmap/thumb"
android:thumbOffset="10dp"/>
<TextView
android:id="@+id/read_tv_next_chapter"
style="@style/MAppTheme.TextAppearance.Read.BottomMenu"
android:layout_width="80dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="下一章"/>
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="0.5dp"
android:background="@color/sys_dialog_setting_line" />
<!--菜单栏-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="72dp"
android:orientation="horizontal"
android:paddingTop="12dp"
android:paddingBottom="12dp">
<TextView
android:id="@+id/read_tv_category"
style="@style/AppTheme.TextAppearance.Read.BottomMenu.Button"
android:drawableTop="@mipmap/t3"
android:text="目录"/>
<TextView
android:id="@+id/read_tv_download"
style="@style/AppTheme.TextAppearance.Read.BottomMenu.Button"
android:drawableTop="@mipmap/download"
android:text="下载"/>
<TextView
android:id="@+id/read_tv_night_mode"
style="@style/AppTheme.TextAppearance.Read.BottomMenu.Button"
android:drawableTop="@mipmap/ao"
android:text="夜间"/>
<TextView
android:id="@+id/read_tv_setting"
style="@style/AppTheme.TextAppearance.Read.BottomMenu.Button"
android:drawableTop="@mipmap/t6"
android:text="设置"/>
</LinearLayout>
</LinearLayout>
</RelativeLayout>

@ -1,59 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto"
android:fitsSystemWindows="true"
android:id="@+id/read_activity_new"
tools:context="xyz.fycz.myreader.ui.activity.ReadActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--阅读页面-->
<xyz.fycz.myreader.widget.page.PageView
android:id="@+id/read_pv_page"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
tools:visibility="gone"/>
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp">
<com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar
android:id="@+id/pb_nextPage"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:splitTrack="false"
app:seekBarRotation="CW270"
android:visibility="invisible"/>
</com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBarWrapper>
<ProgressBar
android:id="@+id/pb_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"/>
<TextView
android:id="@+id/tv_end_page_tip"
android:layout_width="match_parent"
android:layout_height="35dp"
android:layout_alignParentBottom="true"
android:background="@color/sys_dialog_setting_line"
android:textColor="@color/sys_dialog_setting_word_bright"
android:text="@string/end_page_tip"
android:gravity="center_vertical|start"
android:paddingStart="15dp"
android:textSize="14sp"
android:visibility="gone"/>
</RelativeLayout>
</androidx.drawerlayout.widget.DrawerLayout>

@ -1,205 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--背景图片-->
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
app:srcCompat="@drawable/bg_login" />
<!--添加虚化层-->
<io.alterac.blurkit.BlurLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:blk_blurRadius="5"
app:blk_fps="0" />
<View
android:id="@+id/bg"
android:layout_width="match_parent"
android:layout_height="280dp"
android:layout_centerInParent="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:background="@drawable/input_bg_shape" />
<io.alterac.blurkit.BlurLayout
android:layout_width="match_parent"
android:layout_height="400dp"
android:layout_centerInParent="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
app:blk_blurRadius="20"
app:blk_fps="0" />
<!--添加标题和输入框-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:orientation="vertical"
android:padding="20dp">
<!--标题-->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/text_register"
android:textAlignment="center"
android:textColor="@color/sys_word_little"
android:textSize="20dp" />
<TextView
android:id="@+id/tv_register_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text=""
android:textColor="@color/textError"
android:visibility="gone" />
<!--添加输入框-->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="14">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_account_tip"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="16"
app:passwordToggleEnabled="true"
app:passwordToggleTintMode="src_in">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_password_tip"
android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_rp_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="16"
app:passwordToggleEnabled="true"
app:passwordToggleTintMode="src_in">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_password_tip"
android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/et_captcha"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawablePadding="10dp"
app:counterEnabled="true"
app:counterMaxLength="4">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/et_rp_captcha"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>
<ImageView
android:id="@+id/iv_captcha"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="3"
android:paddingStart="7dp" />
</LinearLayout>
<!--登录按钮-->
<Button
android:id="@+id/bt_register"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:background="@drawable/login_btn_selector"
android:enabled="false"
android:text="@string/text_register"
android:textAlignment="center"
android:textColor="#ffffff"
android:textSize="25dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<CheckBox
android:id="@+id/cb_agreement"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_agreement_tip"
android:textColor="@color/textSecondaryInverted"
android:theme="@style/MyCheckBox" />
<TextView
android:id="@+id/tv_agreement"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/link_agreement"
android:textColor="@color/colorAccent"
android:textSize="14dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="》"
android:textColor="@color/textSecondaryInverted" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/dialog_tv_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size"
android:text="神墓(辰东)"/>
</LinearLayout>

@ -1,297 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="86dp"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/rl_title_view"
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@color/sys_dialog_setting_bg">
<LinearLayout
android:id="@+id/ll_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/ll_title_back"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingLeft="10dp">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:layout_margin="5dip"
app:srcCompat="@mipmap/larrow3"/>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_book_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal"
android:paddingRight="10dp">
<TextView
android:id="@+id/tv_book_name_top"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:layout_marginLeft="15dp"
android:text="@string/app_name"
android:textSize="20sp"
android:textColor="@color/sys_dialog_setting_word">
</TextView>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:gravity="center"
android:orientation="horizontal"
android:paddingRight="10dp">
<ImageView
android:id="@+id/iv_change_source"
android:layout_width="30dp"
android:layout_height="match_parent"
android:src="@drawable/ic_change_source"
app:tint="@color/little_black_white"/>
<ImageView
android:id="@+id/iv_refresh"
android:layout_marginLeft="15dp"
android:layout_width="30dp"
android:layout_height="match_parent"
android:src="@drawable/ic_refresh"
app:tint="@color/little_black_white"/>
<ImageView
android:id="@+id/iv_book_mark"
android:layout_marginLeft="12dp"
android:layout_width="33dp"
android:layout_height="match_parent"
android:src="@drawable/ic_bookmark"
app:tint="@color/little_black_white"/>
<ImageView
android:id="@+id/iv_voice_read"
android:layout_width="25dp"
android:layout_height="match_parent"
android:layout_marginLeft="20dp"
app:srcCompat="@mipmap/bd"
android:visibility="gone"/>
<ImageView
android:id="@+id/iv_more"
android:layout_width="25dp"
android:layout_height="match_parent"
android:layout_marginLeft="20dp"
app:srcCompat="@mipmap/t8"
android:visibility="gone"/>
</LinearLayout>
</RelativeLayout>
<View
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="@color/sys_dialog_setting_line" />
<LinearLayout
android:id="@+id/ll_chapter_view"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="@color/sys_dialog_setting_bg"
android:orientation="vertical">
<TextView android:id="@+id/tv_chapter_title_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="14dp"
android:textSize="15sp"
android:text="章节名称"
android:maxLines="1"
android:textColor="@color/sys_dialog_setting_word">
</TextView>
<TextView android:id="@+id/tv_chapter_url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:textSize="13sp"
android:text="章节链接"
android:maxLines="1"
android:textColor="@color/sys_dialog_setting_word">
</TextView>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/ll_bottom_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true"
android:background="@color/sys_dialog_setting_bg"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="30dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="30dp">
<TextView
android:id="@+id/tv_last_chapter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="上一章"
android:textColor="@color/sys_dialog_setting_word"
android:textSize="15sp" />
<SeekBar
android:id="@+id/sb_read_chapter_progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_weight="1"
android:maxHeight="1dp"
android:minHeight="1dp"
android:progressDrawable="@drawable/seekbar_bg"
android:thumb="@mipmap/thumb"/>
<TextView
android:id="@+id/tv_next_chapter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下一章"
android:textColor="@color/sys_dialog_setting_word"
android:textSize="15sp"/>
</LinearLayout>
<ImageView
android:layout_width="match_parent"
android:layout_height="1px"
app:srcCompat="@color/sys_dialog_setting_line" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:gravity="center">
<LinearLayout
android:id="@+id/ll_chapter_list"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@mipmap/t3" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="目录"
android:textColor="@color/sys_dialog_setting_word" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_download_cache"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@mipmap/download" />
<TextView
android:id="@+id/tv_download_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="缓存"
android:textColor="@color/sys_dialog_setting_word" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_night_and_day"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_night_and_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@mipmap/ao" />
<TextView
android:id="@+id/tv_night_and_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="夜间"
android:textColor="@color/sys_dialog_setting_word" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_setting"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical"
android:gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@mipmap/t6" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="设置"
android:textColor="@color/sys_dialog_setting_word" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</RelativeLayout>

@ -3,7 +3,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@color/colorBackground"
android:scrollbars="vertical">
@ -14,50 +13,6 @@
android:id="@+id/ll_mine"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="25dp"
android:paddingLeft="5dp"
android:paddingRight="5dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerInParent="true"
android:textColor="@color/textPrimary"
android:text="用户"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/mine_rl_user"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:background="@drawable/selector_common_bg">
<ImageView
android:id="@+id/iv_user"
android:layout_alignParentStart="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
app:tint="@color/textPrimary"
android:src="@drawable/ic_user"
/>
<TextView
android:id="@+id/tv_user"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_user"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_medium_size"
android:text="登录/注册"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
@ -104,35 +59,7 @@
</RelativeLayout>
<RelativeLayout
android:id="@+id/mine_rl_syn"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:background="@drawable/selector_common_bg">
<ImageView
android:id="@+id/iv_syn"
android:layout_alignParentStart="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
app:tint="@color/textPrimary"
android:src="@drawable/ic_online_syn"
/>
<TextView
android:id="@+id/tv_syn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_syn"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size"
android:text="@string/menu_bookcase_syn"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="25dp"
@ -230,36 +157,7 @@
android:text="其他"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/mine_rl_feedback"
android:layout_width="match_parent"
android:layout_height="60dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:background="@drawable/selector_common_bg">
<ImageView
android:layout_marginStart="3dp"
android:id="@+id/iv_feedback"
android:layout_alignParentStart="true"
android:layout_width="18dp"
android:layout_height="18dp"
android:layout_centerVertical="true"
app:tint="@color/textPrimary"
android:src="@drawable/ic_feedback"
/>
<TextView
android:id="@+id/tv_feedback"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="18dp"
android:layout_toEndOf="@id/iv_feedback"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size"
android:text="@string/mine_feedback"/>
</RelativeLayout>
<RelativeLayout
android:id="@+id/mine_rl_about"
android:layout_width="match_parent"

@ -207,42 +207,6 @@
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/vw_qq"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_margin="6dp"
android:clickable="true"
android:focusable="true"
android:foreground="?attr/selectableItemBackground"
app:cardBackgroundColor="@color/background_card">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:src="@drawable/ic_qq_group"
app:tint="@color/textSecondary" />
<TextView
android:id="@+id/tv_qq"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="5dp"
android:gravity="center_vertical"
android:textColor="@color/textSecondary"
android:text="@string/join_qq_group" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="@+id/vw_git"
android:layout_width="match_parent"

@ -10,7 +10,7 @@
android:layout_width="match_parent"
android:layout_height="45dp"
android:background="@color/colorPrimary"
android:theme="@style/Theme.ToolBar.Dark.Menu"
android:theme="?attr/actionBarStyle"
app:titleTextAppearance="@style/toolbar_title_textStyle"
app:subtitleTextAppearance="@style/toolbar_subtitle_textStyle"
app:layout_scrollFlags="scroll|enterAlways">

@ -0,0 +1,34 @@
<?xml version ="1.0" encoding ="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<item
android:id="@+id/action_change_source"
android:icon="@drawable/ic_change_source"
android:title="@string/menu_change_source"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_reload"
android:icon="@drawable/ic_refresh"
android:title="@string/menu_reload"
app:showAsAction="ifRoom"/>
<group android:id="@+id/action_load_finish">
<item
android:id="@+id/action_add_bookmark"
android:icon="@drawable/ic_bookmark"
android:title="@string/menu_add_bookmark"
app:showAsAction="never"/>
<item
android:id="@+id/action_copy_content"
android:icon="@drawable/ic_copy"
android:title="@string/menu_copy_content"
app:showAsAction="never"/>
<item
android:id="@+id/action_open_link"
android:icon="@drawable/ic_link"
android:title="@string/menu_open_link"
app:showAsAction="never"/>
</group>
</menu>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_tip"
android:icon="@drawable/ic_question"
android:title="提示"
app:showAsAction="always"
android:visible="true"/>
</menu>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 487 KiB

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

Loading…
Cancel
Save