diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..6560a98 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,36 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..797acea --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index ef9a0c1..0932045 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -200,6 +200,10 @@ dependencies { //SwipeBackLayout implementation 'me.imid.swipebacklayout.lib:library:1.1.0' + + //JS + //noinspection GradleDependency + implementation 'com.github.gedoor:rhino-android:1.3' } greendao { diff --git a/app/src/main/java/xyz/fycz/myreader/application/App.java b/app/src/main/java/xyz/fycz/myreader/application/App.java index 096fcd4..3536e01 100644 --- a/app/src/main/java/xyz/fycz/myreader/application/App.java +++ b/app/src/main/java/xyz/fycz/myreader/application/App.java @@ -71,11 +71,13 @@ public class App extends Application { private static final Handler handler = new Handler(); private static App application; private ExecutorService mFixedThreadPool; + private static boolean debug; @Override public void onCreate() { super.onCreate(); application = this; + debug = isApkInDebug(this); firstInit(); HttpUtil.trustAllHosts();//信任所有证书 RxJavaPlugins.setErrorHandler(Functions.emptyConsumer()); @@ -159,12 +161,7 @@ public class App extends Application { SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, trustAllCerts, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); - HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }); + HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); } catch (Exception ignored) { } } @@ -288,7 +285,7 @@ public class App extends Application { App.getApplication().newThread(() -> { try { String url = "https://shimo.im/docs/cqkgjPRRydYYhQKt/read"; - if (isApkInDebug(getmContext())) { + if (getApplication().debug) { url = "https://shimo.im/docs/zfzpda7MUGskOC9v/read"; } String html = OkHttpUtils.getHtml(url); @@ -514,4 +511,8 @@ public class App extends Application { } return null; } + + public static boolean isDebug() { + return debug; + } } diff --git a/app/src/main/java/xyz/fycz/myreader/application/TrustAllCerts.java b/app/src/main/java/xyz/fycz/myreader/application/TrustAllCerts.java deleted file mode 100644 index d6d62ad..0000000 --- a/app/src/main/java/xyz/fycz/myreader/application/TrustAllCerts.java +++ /dev/null @@ -1,16 +0,0 @@ -package xyz.fycz.myreader.application; - -import java.security.cert.X509Certificate; - -import javax.net.ssl.X509TrustManager; - -public class TrustAllCerts implements X509TrustManager { - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) {} - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) {} - - @Override - public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];} -} diff --git a/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java b/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java index 776bc5a..171e40f 100644 --- a/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java +++ b/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java @@ -2,10 +2,18 @@ package xyz.fycz.myreader.common; import android.os.Environment; +import com.google.gson.reflect.TypeToken; + import xyz.fycz.myreader.R; import xyz.fycz.myreader.util.utils.FileUtils; import java.io.File; +import java.lang.reflect.Type; +import java.util.Map; +import java.util.regex.Pattern; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; public class APPCONST { @@ -103,4 +111,13 @@ public class APPCONST { public static final String DEFAULT_WEB_DAV_URL = "https://dav.jianguoyun.com/dav/"; + + public static Type MAP_STRING = new TypeToken>() { + }.getType(); + + public static final Pattern JS_PATTERN = Pattern.compile("([\\w\\W]*?|@js:[\\w\\W]*$)", Pattern.CASE_INSENSITIVE); + public static final Pattern EXP_PATTERN = Pattern.compile("\\{\\{([\\w\\W]*?)\\}\\}"); + + public static final ScriptEngine SCRIPT_ENGINE = new ScriptEngineManager().getEngineByName("rhino"); + } diff --git a/app/src/main/java/xyz/fycz/myreader/common/ErrorCode.java b/app/src/main/java/xyz/fycz/myreader/common/ErrorCode.java deleted file mode 100644 index daf9c77..0000000 --- a/app/src/main/java/xyz/fycz/myreader/common/ErrorCode.java +++ /dev/null @@ -1,34 +0,0 @@ -package xyz.fycz.myreader.common; - - - -public class ErrorCode { - - public static final int login_not_user = 1; // 用户不存在 - public static final int login_not_password = 2;// 密码错误 - public static final int internet_waring = 3;// 网络访问错误 - public static final int no_security = 4;// 没有权限(登陆过期) - public static final int no_login_ = 5;// 没有登录 - public static final int meeting_user_no = 6;// 报名用户不存在 - public static final int old_password = 8;// 原密码错误 - public static final int execute_error = 7;//程序运行异常 - public static final int order_timeout = 9;// 订餐超时 - public static final int email_or_emailPwd_empty = 10;//邮箱或邮箱密码为空 - public static final int email_login_error = 11;//邮箱登陆失败 - public static final int email_params_error = 12;//邮箱接口参数不完整 - public static final int suggestion_params_error = 13;//反馈意见接口参数不完整 - public static final int email_addresses_error = 15;//邮箱地址无效 - public static final int uploadfile_extend_error = 16;//上传附件后缀不支持 - public static final int params_overlength = 17;//接口参数过长 - public static final int leave_error = 18;//请假审批人跟请假人不能相同 - public static final int login_simple_pwd = 19;//简单登录密码 - - - public static final int more_depart = 1011;//多部门用户 - public static final int Certificate_sys_no_user = 1012;//认证系统内无此用户 - public static final int Certificate_no_bind_user = 1013;//用户没绑定证书在系统内 - public static final int ukey_duplicate_user = 1014;//ukey序列号系统中不存在或存在多笔数据,请联系管理员 - - - -} diff --git a/app/src/main/java/xyz/fycz/myreader/common/URLCONST.java b/app/src/main/java/xyz/fycz/myreader/common/URLCONST.java index 81515c2..c5e9190 100644 --- a/app/src/main/java/xyz/fycz/myreader/common/URLCONST.java +++ b/app/src/main/java/xyz/fycz/myreader/common/URLCONST.java @@ -9,7 +9,7 @@ public class URLCONST { public static String APP_DIR_UR = "https://www.lanzous.com/b00ngso7e"; - public static String LAN_ZOUS_URL = "https://fycz.lanzous.com"; + public static String LAN_ZOUS_URL = "https://fycz.lanzoui.com"; //字体下载 public static final String FONT_DOWNLOAD_URL = "https://novel.fycz.xyz/app/fonts/"; diff --git a/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/BookSource3Bean.java b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/BookSource3Bean.java new file mode 100644 index 0000000..89ff0a8 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/BookSource3Bean.java @@ -0,0 +1,266 @@ +package xyz.fycz.myreader.entity.thirdsource; + +import com.google.gson.Gson; + +import java.util.ArrayList; +import java.util.List; + +import kotlin.jvm.Transient; + +// 解析阅读3.0的书源规则,toBookSourceBean()方法转换为阅读2.0书源规则 +// https://raw.githubusercontent.com/gedoor/legado/master/app/src/main/java/io/legado/app/data/entities/BookSource.kt +public class BookSource3Bean { + private String bookSourceName = ""; // 名称 + private String bookSourceGroup; // 分组 + private String bookSourceUrl = ""; // 地址,包括 http/https + private int bookSourceType = 0; // 类型,0 文本,1 音频 + private String bookUrlPattern; // 详情页url正则 + private int customOrder = 0; // 手动排序编号 + private Boolean enabled = true; // 是否启用 + private Boolean enabledExplore = true; // 启用发现 + private String header; // 请求头 + private String loginUrl; // 登录地址 + private String bookSourceComment; // 注释 + private Long lastUpdateTime = 0L; // 最后更新时间,用于排序 + private int weight = 0; // 智能排序的权重 + private String exploreUrl; // 发现url + private ExploreRule ruleExplore; // 发现规则 + private String searchUrl; // 搜索url + private SearchRule ruleSearch; // 搜索规则 + private BookInfoRule ruleBookInfo; // 书籍信息页规则 + private TocRule ruleToc; // 目录页规则 + private ContentRule ruleContent; // 正文页规则 + + @Transient + private String userAgent; + @Transient + private String RuleSearchUrl; + + class ContentRule { + String content; + String nextContentUrl; + String webJs; + String sourceRegex; + String replaceRegex; + String imageStyle; //默认大小居中,FULL最大宽度 + } + + class TocRule { + String chapterList; + String chapterName; + String chapterUrl; + String isVip; + String updateTime; + String nextTocUrl; + } + + class BookInfoRule { + String init; + String name; + String author; + String intro; + String kind; + String lastChapter; + String updateTime; + String coverUrl; + String tocUrl; + String wordCount; + } + + class SearchRule { + String bookList; + String name; + String author; + String intro; + String kind; + String lastChapter; + String updateTime; + String bookUrl; + String coverUrl; + String wordCount; + } + + class ExploreRule { + String bookList; + String name; + String author; + String intro; + String kind; + String lastChapter; + String updateTime; + String bookUrl; + String coverUrl; + String wordCount; + } + +/* @Override + public Object clone() { + try { + Gson gson = new Gson(); + String json = gson.toJson(this); + return gson.fromJson(json, BookSource3Bean.class); + } catch (Exception ignored) { + } + return this; + }*/ + + // 给书源增加一个标签 + public BookSource3Bean addGroupTag(String tag) { + if (this.bookSourceGroup != null) { + //为了避免空格、首尾位置的差异造成影响,这里做循环处理 + String[] tags = (this.bookSourceGroup + ";" + tag).split(";"); + + List list = new ArrayList<>(); + list.add(tag); + for (String s : tags) { + if (!list.contains(s)) { + list.add(s); + } + } + bookSourceGroup = tag; + for (int i = 1; i < list.size(); i++) { + bookSourceGroup = bookSourceGroup + ";" + list.get(i); + } + } + return this; + } + + class httpRequest { + String method; + String body; + String headers; + String charset; + } + + private String searchUrl2RuleSearchUrl(String searchUrl) { + String RuleSearchUrl = searchUrl; + if (searchUrl != null) { + String q = ""; + if (searchUrl.replaceAll("\\s", "").matches("^[^,]+,\\{.+\\}")) { + // 正常网址不包含逗号 + String[] strings = searchUrl.split(",", 2); + try { + Gson gson = new Gson(); + httpRequest request = gson.fromJson(strings[1], httpRequest.class); + if (gson.toJson(request).replaceAll("\\s", "").length() > 0) { + // 阅读2.0没有单独的header,只有useragent + if (request.headers != null) { + if (this.header == null) + this.header = request.headers; + else if (request.headers.trim().length() < 1) + this.header = request.headers; + } + + if (request.charset != null) { + if (request.charset.trim().length() > 0) + q = q + "|char=" + request.charset; + } + + if (request.body != null) { + q = request.body + .replace("{{key}}", "searchKey") + .replaceFirst("\\{\\{([^{}]*)page([^{}]*)\\}\\}", "$1searchPage$2") + + q; + + // post请求的关键词一定在第二部分 + if (request.method != null) { + if (request.method.toLowerCase().contains("post")) + q = "@" + q; + else + q = "?" + q; + } + return strings[0] + q; + } + + RuleSearchUrl = strings[0] + q; + } else + RuleSearchUrl = searchUrl; + + } catch (Exception e) { + e.printStackTrace(); + RuleSearchUrl = searchUrl; + } + } + + return RuleSearchUrl.replaceAll("\\s", "") + .replace("{{key}}", "searchKey") + .replaceFirst("\\{\\{([^{}]*)page([^{}]*)\\}\\}", "$1searchPage$2") + ; + } + return null; + } + + public BookSourceBean toBookSourceBean() { + // 带注释的行,表示2.0/3.0书源json的数据命名不同。注释后方为2.0名称 + String bookSourceType = ""; + if (this.bookSourceType != 0) + bookSourceType = "" + this.bookSourceType; + + RuleSearchUrl = searchUrl2RuleSearchUrl(searchUrl); + + if (header != null && userAgent == null) { + if (header.matches("(?!).*(User-Agent).*")) + userAgent = header.replaceFirst("(?!).*(User-Agent)[\\s:]+\"([^\"]+)\".*", "$2"); + } + + String ruleFindUrl=null; + if(exploreUrl!=null){ + ruleFindUrl=exploreUrl.replaceAll("\\{\\{page\\}\\}","searchPage"); + } + + // 暂时只给发现和搜索添加了header + String header=""; + if(this.header!=null){ + if(this.header.trim().length()>0) + header="@Header:"+this.header.replaceAll("\\n"," "); + } + + + return new BookSourceBean( + bookSourceUrl, + bookSourceName, + bookSourceGroup, + bookSourceType, + loginUrl, + lastUpdateTime, + 0, //u serialNumber, + weight, + true, //u enable, + ruleFindUrl+header,//发现规则 ruleFindUrl, + ruleExplore.bookList, // 列表 ruleFindList, + ruleExplore.name,// ruleFindName, + ruleExplore.author,// ruleFindAuthor, + ruleExplore.kind,// ruleFindKind, + ruleExplore.intro,// ruleFindIntroduce, + ruleExplore.lastChapter,// ruleFindLastChapter, + ruleExplore.coverUrl,// ruleFindCoverUrl, + ruleExplore.bookUrl,//??? ruleFindNoteUrl, + RuleSearchUrl+header,// ruleSearchUrl, + ruleSearch.bookList,// ruleSearchList, + ruleSearch.name,// ruleSearchName, + ruleSearch.author,// ruleSearchAuthor, + ruleSearch.kind,// ruleSearchKind, + ruleSearch.intro,// ruleSearchIntroduce, + ruleSearch.lastChapter,// ruleSearchLastChapter, + ruleSearch.coverUrl,//ruleSearchCoverUrl, + ruleSearch.bookUrl,// ruleSearchNoteUrl, + bookUrlPattern, //??? ruleBookUrlPattern, + ruleBookInfo.init,// ruleBookInfoInit, + ruleBookInfo.name,// ruleBookName, + ruleBookInfo.author,// ruleBookAuthor, + ruleBookInfo.coverUrl,// ruleCoverUrl, + ruleBookInfo.intro,// ruleIntroduce, + ruleBookInfo.kind,// ruleBookKind, + ruleBookInfo.lastChapter,// ruleBookLastChapter, + ruleBookInfo.tocUrl,// ruleChapterUrl, + ruleToc.nextTocUrl,// ruleChapterUrlNext, + ruleToc.chapterList,// ruleChapterList, + ruleToc.chapterName,// ruleChapterName, + ruleToc.chapterUrl, // ruleContentUrl, + ruleContent.nextContentUrl, //ruleContentUrlNext, + ruleContent.content, // ruleBookContent, + ruleContent.replaceRegex,// ruleBookContentReplace, + userAgent // httpUserAgent + ); + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/BookSourceBean.java b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/BookSourceBean.java new file mode 100644 index 0000000..a0c7ec1 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/entity/thirdsource/BookSourceBean.java @@ -0,0 +1,709 @@ +package xyz.fycz.myreader.entity.thirdsource; + +import android.text.TextUtils; + +import com.google.gson.Gson; + +import org.greenrobot.greendao.annotation.Entity; +import org.greenrobot.greendao.annotation.Generated; +import org.greenrobot.greendao.annotation.Id; +import org.greenrobot.greendao.annotation.NotNull; +import org.greenrobot.greendao.annotation.OrderBy; +import org.greenrobot.greendao.annotation.Transient; + +import java.util.ArrayList; +import java.util.Objects; + +import xyz.fycz.myreader.util.utils.StringUtils; + +import static android.text.TextUtils.isEmpty; + +/** + * Created by GKF on 2017/12/14. + * 书源信息 + */ +@Entity +public class BookSourceBean implements Cloneable { + @Id + private String bookSourceUrl; + private String bookSourceName; + private String bookSourceGroup; + private String bookSourceType; + private String loginUrl; + private Long lastUpdateTime; + @OrderBy + private int serialNumber; + @OrderBy + @NotNull + private int weight = 0; + private boolean enable; + //发现规则 + private String ruleFindUrl; + private String ruleFindList; + private String ruleFindName; + private String ruleFindAuthor; + private String ruleFindKind; + private String ruleFindIntroduce; + private String ruleFindLastChapter; + private String ruleFindCoverUrl; + private String ruleFindNoteUrl; + //搜索规则 + private String ruleSearchUrl; + private String ruleSearchList; + private String ruleSearchName; + private String ruleSearchAuthor; + private String ruleSearchKind; + private String ruleSearchIntroduce; + private String ruleSearchLastChapter; + private String ruleSearchCoverUrl; + private String ruleSearchNoteUrl; + //详情页规则 + private String ruleBookUrlPattern; + private String ruleBookInfoInit; + private String ruleBookName; + private String ruleBookAuthor; + private String ruleCoverUrl; + private String ruleIntroduce; + private String ruleBookKind; + private String ruleBookLastChapter; + private String ruleChapterUrl; + //目录页规则 + private String ruleChapterUrlNext; + private String ruleChapterList; + private String ruleChapterName; + private String ruleContentUrl; + //正文页规则 + private String ruleContentUrlNext; + private String ruleBookContent; + private String ruleBookContentReplace; + private String httpUserAgent; + + @Transient + private transient ArrayList groupList; + + @Generated(hash = 243497779) + public BookSourceBean(String bookSourceUrl, String bookSourceName, String bookSourceGroup, String bookSourceType, String loginUrl, Long lastUpdateTime, int serialNumber, int weight, boolean enable, String ruleFindUrl, String ruleFindList, + String ruleFindName, String ruleFindAuthor, String ruleFindKind, String ruleFindIntroduce, String ruleFindLastChapter, String ruleFindCoverUrl, String ruleFindNoteUrl, String ruleSearchUrl, String ruleSearchList, + String ruleSearchName, String ruleSearchAuthor, String ruleSearchKind, String ruleSearchIntroduce, String ruleSearchLastChapter, String ruleSearchCoverUrl, String ruleSearchNoteUrl, String ruleBookUrlPattern, + String ruleBookInfoInit, String ruleBookName, String ruleBookAuthor, String ruleCoverUrl, String ruleIntroduce, String ruleBookKind, String ruleBookLastChapter, String ruleChapterUrl, String ruleChapterUrlNext, + String ruleChapterList, String ruleChapterName, String ruleContentUrl, String ruleContentUrlNext, String ruleBookContent, String ruleBookContentReplace, String httpUserAgent) { + this.bookSourceUrl = bookSourceUrl; + this.bookSourceName = bookSourceName; + this.bookSourceGroup = bookSourceGroup; + this.bookSourceType = bookSourceType; + this.loginUrl = loginUrl; + this.lastUpdateTime = lastUpdateTime; + this.serialNumber = serialNumber; + this.weight = weight; + this.enable = enable; + this.ruleFindUrl = ruleFindUrl; + this.ruleFindList = ruleFindList; + this.ruleFindName = ruleFindName; + this.ruleFindAuthor = ruleFindAuthor; + this.ruleFindKind = ruleFindKind; + this.ruleFindIntroduce = ruleFindIntroduce; + this.ruleFindLastChapter = ruleFindLastChapter; + this.ruleFindCoverUrl = ruleFindCoverUrl; + this.ruleFindNoteUrl = ruleFindNoteUrl; + this.ruleSearchUrl = ruleSearchUrl; + this.ruleSearchList = ruleSearchList; + this.ruleSearchName = ruleSearchName; + this.ruleSearchAuthor = ruleSearchAuthor; + this.ruleSearchKind = ruleSearchKind; + this.ruleSearchIntroduce = ruleSearchIntroduce; + this.ruleSearchLastChapter = ruleSearchLastChapter; + this.ruleSearchCoverUrl = ruleSearchCoverUrl; + this.ruleSearchNoteUrl = ruleSearchNoteUrl; + this.ruleBookUrlPattern = ruleBookUrlPattern; + this.ruleBookInfoInit = ruleBookInfoInit; + this.ruleBookName = ruleBookName; + this.ruleBookAuthor = ruleBookAuthor; + this.ruleCoverUrl = ruleCoverUrl; + this.ruleIntroduce = ruleIntroduce; + this.ruleBookKind = ruleBookKind; + this.ruleBookLastChapter = ruleBookLastChapter; + this.ruleChapterUrl = ruleChapterUrl; + this.ruleChapterUrlNext = ruleChapterUrlNext; + this.ruleChapterList = ruleChapterList; + this.ruleChapterName = ruleChapterName; + this.ruleContentUrl = ruleContentUrl; + this.ruleContentUrlNext = ruleContentUrlNext; + this.ruleBookContent = ruleBookContent; + this.ruleBookContentReplace = ruleBookContentReplace; + this.httpUserAgent = httpUserAgent; + } + + public BookSourceBean(BookSourceBean sourceBean) { + this.bookSourceUrl = sourceBean.bookSourceUrl; + this.bookSourceName = sourceBean.bookSourceName; + this.bookSourceGroup = sourceBean.bookSourceGroup; + this.bookSourceType = sourceBean.bookSourceType; + this.loginUrl = sourceBean.loginUrl; + this.lastUpdateTime = sourceBean.lastUpdateTime; + this.serialNumber = sourceBean.serialNumber; + this.weight = sourceBean.weight; + this.enable = sourceBean.enable; + this.ruleFindUrl = sourceBean.ruleFindUrl; + this.ruleFindList = sourceBean.ruleFindList; + this.ruleFindName = sourceBean.ruleFindName; + this.ruleFindAuthor = sourceBean.ruleFindAuthor; + this.ruleFindKind = sourceBean.ruleFindKind; + this.ruleFindIntroduce = sourceBean.ruleFindIntroduce; + this.ruleFindLastChapter = sourceBean.ruleFindLastChapter; + this.ruleFindCoverUrl = sourceBean.ruleFindCoverUrl; + this.ruleFindNoteUrl = sourceBean.ruleFindNoteUrl; + this.ruleSearchUrl = sourceBean.ruleSearchUrl; + this.ruleSearchList = sourceBean.ruleSearchList; + this.ruleSearchName = sourceBean.ruleSearchName; + this.ruleSearchAuthor = sourceBean.ruleSearchAuthor; + this.ruleSearchKind = sourceBean.ruleSearchKind; + this.ruleSearchIntroduce = sourceBean.ruleSearchIntroduce; + this.ruleSearchLastChapter = sourceBean.ruleSearchLastChapter; + this.ruleSearchCoverUrl = sourceBean.ruleSearchCoverUrl; + this.ruleSearchNoteUrl = sourceBean.ruleSearchNoteUrl; + this.ruleBookUrlPattern = sourceBean.ruleBookUrlPattern; + this.ruleBookInfoInit = sourceBean.ruleBookInfoInit; + this.ruleBookName = sourceBean.ruleBookName; + this.ruleBookAuthor = sourceBean.ruleBookAuthor; + this.ruleCoverUrl = sourceBean.ruleCoverUrl; + this.ruleIntroduce = sourceBean.ruleIntroduce; + this.ruleBookKind = sourceBean.ruleBookKind; + this.ruleBookLastChapter = sourceBean.ruleBookLastChapter; + this.ruleChapterUrl = sourceBean.ruleChapterUrl; + this.ruleChapterUrlNext = sourceBean.ruleChapterUrlNext; + this.ruleChapterList = sourceBean.ruleChapterList; + this.ruleChapterName = sourceBean.ruleChapterName; + this.ruleContentUrl = sourceBean.ruleContentUrl; + this.ruleContentUrlNext = sourceBean.ruleContentUrlNext; + this.ruleBookContent = sourceBean.ruleBookContent; + this.ruleBookContentReplace = sourceBean.ruleBookContentReplace; + this.httpUserAgent = sourceBean.httpUserAgent; + } + + public BookSourceBean() { + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof BookSourceBean) { + BookSourceBean bs = (BookSourceBean) obj; + return stringEquals(bookSourceUrl, bs.bookSourceUrl) + && stringEquals(bookSourceName, bs.bookSourceName) + && stringEquals(bookSourceType, bs.bookSourceType) + && stringEquals(loginUrl, bs.loginUrl) + && stringEquals(bookSourceGroup, bs.bookSourceGroup) + && stringEquals(ruleBookName, bs.ruleBookName) + && stringEquals(ruleBookAuthor, bs.ruleBookAuthor) + && stringEquals(ruleChapterUrl, bs.ruleChapterUrl) + && stringEquals(ruleChapterUrlNext, ruleChapterUrlNext) + && stringEquals(ruleCoverUrl, bs.ruleCoverUrl) + && stringEquals(ruleIntroduce, bs.ruleIntroduce) + && stringEquals(ruleChapterList, bs.ruleChapterList) + && stringEquals(ruleChapterName, bs.ruleChapterName) + && stringEquals(ruleContentUrl, bs.ruleContentUrl) + && stringEquals(ruleContentUrlNext, bs.ruleContentUrlNext) + && stringEquals(ruleBookContent, bs.ruleBookContent) + && stringEquals(ruleSearchUrl, bs.ruleSearchUrl) + && stringEquals(ruleSearchList, bs.ruleSearchList) + && stringEquals(ruleSearchName, bs.ruleSearchName) + && stringEquals(ruleSearchAuthor, bs.ruleSearchAuthor) + && stringEquals(ruleSearchKind, bs.ruleSearchKind) + && stringEquals(ruleSearchLastChapter, bs.ruleSearchLastChapter) + && stringEquals(ruleSearchCoverUrl, bs.ruleSearchCoverUrl) + && stringEquals(ruleSearchNoteUrl, bs.ruleSearchNoteUrl) + && stringEquals(httpUserAgent, bs.httpUserAgent) + && stringEquals(ruleBookKind, bs.ruleBookKind) + && stringEquals(ruleBookLastChapter, bs.ruleBookLastChapter) + && stringEquals(ruleBookUrlPattern, bs.ruleBookUrlPattern) + && stringEquals(ruleBookContentReplace, bs.ruleBookContentReplace); + } + return false; + } + + private Boolean stringEquals(String str1, String str2) { + return Objects.equals(str1, str2) || (isEmpty(str1) && isEmpty(str2)); + } + + @Override + public Object clone() { + try { + Gson gson = new Gson(); + String json = gson.toJson(this); + return gson.fromJson(json, BookSourceBean.class); + } catch (Exception ignored) { + } + return this; + } + + public String getBookSourceName() { + return bookSourceName; + } + + public void setBookSourceName(String bookSourceName) { + this.bookSourceName = bookSourceName; + } + + public String getBookSourceUrl() { + return bookSourceUrl; + } + + public void setBookSourceUrl(String bookSourceUrl) { + this.bookSourceUrl = bookSourceUrl; + } + + public int getSerialNumber() { + return this.serialNumber; + } + + public void setSerialNumber(int serialNumber) { + this.serialNumber = serialNumber; + } + + public int getWeight() { + return this.weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + // 换源时选择的源权重+500 + public void increaseWeightBySelection() { + this.weight += 500; + } + + public void increaseWeight(int increase) { + this.weight += increase; + } + + public boolean getEnable() { + return this.enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public String getRuleBookName() { + return this.ruleBookName; + } + + public void setRuleBookName(String ruleBookName) { + this.ruleBookName = ruleBookName; + } + + public String getRuleBookAuthor() { + return this.ruleBookAuthor; + } + + public void setRuleBookAuthor(String ruleBookAuthor) { + this.ruleBookAuthor = ruleBookAuthor; + } + + public String getRuleChapterUrl() { + return this.ruleChapterUrl; + } + + public void setRuleChapterUrl(String ruleChapterUrl) { + this.ruleChapterUrl = ruleChapterUrl; + } + + public String getRuleCoverUrl() { + return this.ruleCoverUrl; + } + + public void setRuleCoverUrl(String ruleCoverUrl) { + this.ruleCoverUrl = ruleCoverUrl; + } + + public String getRuleIntroduce() { + return this.ruleIntroduce; + } + + public void setRuleIntroduce(String ruleIntroduce) { + this.ruleIntroduce = ruleIntroduce; + } + + public String getRuleBookContent() { + return this.ruleBookContent; + } + + public void setRuleBookContent(String ruleBookContent) { + this.ruleBookContent = ruleBookContent; + } + + public String getRuleSearchUrl() { + return this.ruleSearchUrl; + } + + public void setRuleSearchUrl(String ruleSearchUrl) { + this.ruleSearchUrl = ruleSearchUrl; + } + + public String getRuleContentUrl() { + return this.ruleContentUrl; + } + + public void setRuleContentUrl(String ruleContentUrl) { + this.ruleContentUrl = ruleContentUrl; + } + + public String getRuleSearchName() { + return this.ruleSearchName; + } + + public void setRuleSearchName(String ruleSearchName) { + this.ruleSearchName = ruleSearchName; + } + + public String getRuleSearchAuthor() { + return this.ruleSearchAuthor; + } + + public void setRuleSearchAuthor(String ruleSearchAuthor) { + this.ruleSearchAuthor = ruleSearchAuthor; + } + + public String getRuleSearchKind() { + return this.ruleSearchKind; + } + + public void setRuleSearchKind(String ruleSearchKind) { + this.ruleSearchKind = ruleSearchKind; + } + + public String getRuleSearchLastChapter() { + return this.ruleSearchLastChapter; + } + + public void setRuleSearchLastChapter(String ruleSearchLastChapter) { + this.ruleSearchLastChapter = ruleSearchLastChapter; + } + + public String getRuleSearchCoverUrl() { + return this.ruleSearchCoverUrl; + } + + public void setRuleSearchCoverUrl(String ruleSearchCoverUrl) { + this.ruleSearchCoverUrl = ruleSearchCoverUrl; + } + + public String getRuleSearchNoteUrl() { + return this.ruleSearchNoteUrl; + } + + public void setRuleSearchNoteUrl(String ruleSearchNoteUrl) { + this.ruleSearchNoteUrl = ruleSearchNoteUrl; + } + + public String getRuleSearchList() { + return this.ruleSearchList; + } + + public void setRuleSearchList(String ruleSearchList) { + this.ruleSearchList = ruleSearchList; + } + + public String getRuleChapterList() { + return this.ruleChapterList; + } + + public void setRuleChapterList(String ruleChapterList) { + this.ruleChapterList = ruleChapterList; + } + + public String getRuleChapterName() { + return this.ruleChapterName; + } + + public void setRuleChapterName(String ruleChapterName) { + this.ruleChapterName = ruleChapterName; + } + + public String getHttpUserAgent() { + return this.httpUserAgent; + } + + public void setHttpUserAgent(String httpHeaders) { + this.httpUserAgent = httpHeaders; + } + + public String getRuleFindUrl() { + return this.ruleFindUrl; + } + + public void setRuleFindUrl(String ruleFindUrl) { + this.ruleFindUrl = ruleFindUrl; + } + + public String getBookSourceGroup() { + return this.bookSourceGroup; + } + + public void setBookSourceGroup(String bookSourceGroup) { + this.bookSourceGroup = bookSourceGroup; + upGroupList(); + this.bookSourceGroup = TextUtils.join("; ", groupList); + } + + public String getRuleChapterUrlNext() { + return this.ruleChapterUrlNext; + } + + public void setRuleChapterUrlNext(String ruleChapterUrlNext) { + this.ruleChapterUrlNext = ruleChapterUrlNext; + } + + public String getRuleContentUrlNext() { + return this.ruleContentUrlNext; + } + + public void setRuleContentUrlNext(String ruleContentUrlNext) { + this.ruleContentUrlNext = ruleContentUrlNext; + } + + public String getRuleBookUrlPattern() { + return ruleBookUrlPattern; + } + + public void setRuleBookUrlPattern(String ruleBookUrlPattern) { + this.ruleBookUrlPattern = ruleBookUrlPattern; + } + + public String getRuleBookKind() { + return ruleBookKind; + } + + public void setRuleBookKind(String ruleBookKind) { + this.ruleBookKind = ruleBookKind; + } + + public String getRuleBookLastChapter() { + return ruleBookLastChapter; + } + + public void setRuleBookLastChapter(String ruleBookLastChapter) { + this.ruleBookLastChapter = ruleBookLastChapter; + } + + private void upGroupList() { + if (groupList == null) + groupList = new ArrayList<>(); + else + groupList.clear(); + if (!TextUtils.isEmpty(bookSourceGroup)) { + for (String group : bookSourceGroup.split("\\s*[,;,;]\\s*")) { + group = group.trim(); + if (TextUtils.isEmpty(group) || groupList.contains(group)) continue; + groupList.add(group); + } + } + } + + public void addGroup(String group) { + if (groupList == null) + upGroupList(); + if (!groupList.contains(group)) { + groupList.add(group); + updateModTime(); + bookSourceGroup = TextUtils.join("; ", groupList); + } + } + + public void removeGroup(String group) { + if (groupList == null) + upGroupList(); + if (groupList.contains(group)) { + groupList.remove(group); + updateModTime(); + bookSourceGroup = TextUtils.join("; ", groupList); + } + } + + public boolean containsGroup(String group) { + if (groupList == null) { + upGroupList(); + } + return groupList.contains(group); + } + + public Long getLastUpdateTime() { + return lastUpdateTime; + } + + public void setLastUpdateTime(Long lastUpdateTime) { + this.lastUpdateTime = lastUpdateTime; + } + + public void updateModTime() { + this.lastUpdateTime = System.currentTimeMillis(); + } + + public String getLoginUrl() { + return this.loginUrl; + } + + public void setLoginUrl(String loginUrl) { + this.loginUrl = loginUrl; + } + + public String getBookSourceType() { + return bookSourceType == null ? "" : bookSourceType; + } + + public void setBookSourceType(String bookSourceType) { + this.bookSourceType = bookSourceType; + } + + public String getRuleSearchIntroduce() { + return this.ruleSearchIntroduce; + } + + public void setRuleSearchIntroduce(String ruleSearchIntroduce) { + this.ruleSearchIntroduce = ruleSearchIntroduce; + } + + public String getRuleFindList() { + return this.ruleFindList; + } + + public void setRuleFindList(String ruleFindList) { + this.ruleFindList = ruleFindList; + } + + public String getRuleFindName() { + return this.ruleFindName; + } + + public void setRuleFindName(String ruleFindName) { + this.ruleFindName = ruleFindName; + } + + public String getRuleFindAuthor() { + return this.ruleFindAuthor; + } + + public void setRuleFindAuthor(String ruleFindAuthor) { + this.ruleFindAuthor = ruleFindAuthor; + } + + public String getRuleFindKind() { + return this.ruleFindKind; + } + + public void setRuleFindKind(String ruleFindKind) { + this.ruleFindKind = ruleFindKind; + } + + public String getRuleFindIntroduce() { + return this.ruleFindIntroduce; + } + + public void setRuleFindIntroduce(String ruleFindIntroduce) { + this.ruleFindIntroduce = ruleFindIntroduce; + } + + public String getRuleFindLastChapter() { + return this.ruleFindLastChapter; + } + + public void setRuleFindLastChapter(String ruleFindLastChapter) { + this.ruleFindLastChapter = ruleFindLastChapter; + } + + public String getRuleFindCoverUrl() { + return this.ruleFindCoverUrl; + } + + public void setRuleFindCoverUrl(String ruleFindCoverUrl) { + this.ruleFindCoverUrl = ruleFindCoverUrl; + } + + public String getRuleFindNoteUrl() { + return this.ruleFindNoteUrl; + } + + public void setRuleFindNoteUrl(String ruleFindNoteUrl) { + this.ruleFindNoteUrl = ruleFindNoteUrl; + } + + public String getRuleBookInfoInit() { + return this.ruleBookInfoInit; + } + + public void setRuleBookInfoInit(String ruleBookInfoInit) { + this.ruleBookInfoInit = ruleBookInfoInit; + } + + public String getRuleBookContentReplace() { + return ruleBookContentReplace; + } + + public void setRuleBookContentReplace(String ruleBookContentReplace) { + this.ruleBookContentReplace = ruleBookContentReplace; + } + + public String getJson(int maxLength) { + try { + String source = getMinJson(false); + // 优先直接输出 + if (source.getBytes("utf-8").length <= maxLength) + return source; + source = StringUtils.zipString(source + .replaceFirst("^\\{", "") + // 保留末位的括号,用于解压缩后的验证 + .replaceFirst("(\\s|\n)*\\}$", "}") + .trim()); + // 优先输出带发现的书源 + if (source.getBytes("utf-8").length < maxLength) + return "{" + source; + // 去除发现 + return "{" + StringUtils.zipString(getMinJson(true) + .replaceFirst("^\\{", "") + // 保留末位的括号,用于解压缩后的验证 + .replaceFirst("(\\s|\n)*\\}$", "}") + .trim()); + + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + } + + /** + * 把书源bean转为json,并去除不必要的信息。 + * + * @param removeFind false,去除空信息和排序、可用性信息。true,额外去除发现规则 + * @return + */ + public String getMinJson(Boolean removeFind) { + BookSourceBean bean = new BookSourceBean(this); + String json; + + if (removeFind) { + bean.setRuleFindUrl(null); + bean.setRuleFindList(null); + bean.setRuleFindName(null); + bean.setRuleFindAuthor(null); + bean.setRuleFindKind(null); + bean.setRuleFindIntroduce(null); + bean.setRuleFindLastChapter(null); + bean.setRuleFindCoverUrl(null); + bean.setRuleFindNoteUrl(null); + } + + try { + Gson gson = new Gson(); + json = gson.toJson(bean); + + return json.replaceFirst("\n\\s*\"enable\":\\s*\\S+(,)?\\s*", "\n") + .replaceFirst("\n\\s*\"serialNumber\":\\s*\\d+(,)?\\s*", "\n") + .replaceFirst("\n\\s*\"\"weight\":\\s*\\d+(,)?\\s*", "\n") + .replaceAll("\n\\s*\"[a-zA-Z]+\"(:\"\"|: \"\"| :\"\"| : \"\")\\s*,\\s*\n", "\n") + .replaceAll("\\s*\n\\s*", "") + ; + } catch (Exception ignored) { + } + return ""; + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java b/app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java index e302a1e..b096087 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java @@ -326,7 +326,7 @@ public class BookService extends BaseService { return false; }*/ - public boolean matchHistoryChapterPos(Book book, ArrayList mChapters) { + public boolean matchHistoryChapterPos(Book book, List mChapters) { float matchSui = SysManager.getSetting().getMatchChapterSuitability(); int index = getDurChapter(book.getHisttoryChapterNum(), book.getChapterTotalNum(), book.getHistoryChapterId(), mChapters); if (book.getHistoryChapterId() == null) return false; diff --git a/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java b/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java index 7152c3c..f0b94ab 100644 --- a/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java +++ b/app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java @@ -241,7 +241,7 @@ public class ChapterService extends BaseService { * * @param newChapters */ - public void updateAllOldChapterData(ArrayList mChapters, ArrayList newChapters, String bookId) { + public void updateAllOldChapterData(List mChapters, List newChapters, String bookId) { int i; for (i = 0; i < mChapters.size() && i < newChapters.size(); i++) { Chapter oldChapter = mChapters.get(i); diff --git a/app/src/main/java/xyz/fycz/myreader/model/SearchEngine.java b/app/src/main/java/xyz/fycz/myreader/model/SearchEngine.java index 50fcfed..1206666 100644 --- a/app/src/main/java/xyz/fycz/myreader/model/SearchEngine.java +++ b/app/src/main/java/xyz/fycz/myreader/model/SearchEngine.java @@ -168,6 +168,7 @@ public class SearchEngine { public void onError(Throwable e) { searchFinishNum++; searchOnEngine(keyword); + if (App.isDebug()) e.printStackTrace(); } @Override @@ -224,6 +225,7 @@ public class SearchEngine { public void onError(Throwable e) { searchFinishNum++; searchOnEngine(title, author); + if (App.isDebug()) e.printStackTrace(); } @Override diff --git a/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeByJSonPath.java b/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeByJSonPath.java new file mode 100644 index 0000000..eec09c3 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeByJSonPath.java @@ -0,0 +1,205 @@ +package xyz.fycz.myreader.model.analyzeRule; + +import android.text.TextUtils; + +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.ReadContext; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class AnalyzeByJSonPath { + private static final Pattern jsonRulePattern = Pattern.compile("(?<=\\{)\\$\\..+?(?=\\})"); + private ReadContext ctx; + + public AnalyzeByJSonPath parse(Object json) { + if (json instanceof String) { + ctx = JsonPath.parse((String) json); + } else { + ctx = JsonPath.parse(json); + } + return this; + } + + public String getString(String rule) { + if (TextUtils.isEmpty(rule)) return null; + String result = ""; + String[] rules; + String elementsType; + + if (rule.contains("{$.")) { + result = rule; + Matcher matcher = jsonRulePattern.matcher(rule); + while (matcher.find()) { + result = result.replace(String.format("{%s}", matcher.group()), getString(matcher.group().trim())); + } + return result; + } + + if (rule.contains("&&")) { + rules = rule.split("&&"); + elementsType = "&"; + } else { + rules = rule.split("\\|\\|"); + elementsType = "|"; + } + + if (rules.length == 1) { + try { + Object object = ctx.read(rule); + if (object instanceof List) { + StringBuilder builder = new StringBuilder(); + for (Object o : (List) object) { + builder.append(o).append("\n"); + } + result = builder.toString().replaceAll("\\n$", ""); + } else { + result = String.valueOf(object); + } + } catch (Exception e) { + if (!rule.startsWith("$.")) { + return rule; + } + } + return result; + } else { + List textS = new ArrayList<>(); + for (String rl : rules) { + String temp = getString(rl); + if (!TextUtils.isEmpty(temp)) { + textS.add(temp); + if (elementsType.equals("|")) { + break; + } + } + } + return TextUtils.join(",", textS).trim(); + } + } + + List getStringList(String rule) { + List result = new ArrayList<>(); + if (TextUtils.isEmpty(rule)) return result; + String[] rules; + String elementsType; + if (rule.contains("&&")) { + rules = rule.split("&&"); + elementsType = "&"; + } else if (rule.contains("%%")) { + rules = rule.split("%%"); + elementsType = "%"; + } else { + rules = rule.split("\\|\\|"); + elementsType = "|"; + } + if (rules.length == 1) { + if (!rule.contains("{$.")) { + try { + Object object = ctx.read(rule); + if (object == null) return result; + if (object instanceof List) { + for (Object o : ((List) object)) + result.add(String.valueOf(o)); + } else { + result.add(String.valueOf(object)); + } + } catch (Exception ignored) { + } + return result; + } else { + Matcher matcher = jsonRulePattern.matcher(rule); + while (matcher.find()) { + List stringList = getStringList(matcher.group()); + for (String s : stringList) { + result.add(rule.replace(String.format("{%s}", matcher.group()), s)); + } + } + return result; + } + } else { + List> results = new ArrayList<>(); + for (String rl : rules) { + List temp = getStringList(rl); + if (temp != null && !temp.isEmpty()) { + results.add(temp); + if (temp.size() > 0 && elementsType.equals("|")) { + break; + } + } + } + if (results.size() > 0) { + if ("%".equals(elementsType)) { + for (int i = 0; i < results.get(0).size(); i++) { + for (List temp : results) { + if (i < temp.size()) { + result.add(temp.get(i)); + } + } + } + } else { + for (List temp : results) { + result.addAll(temp); + } + } + } + return result; + } + } + + Object getObject(String rule) { + return ctx.read(rule); + } + + List getList(String rule) { + List result = new ArrayList<>(); + if (TextUtils.isEmpty(rule)) return result; + String elementsType; + String[] rules; + if (rule.contains("&&")) { + rules = rule.split("&&"); + elementsType = "&"; + } else if (rule.contains("%%")) { + rules = rule.split("%%"); + elementsType = "%"; + } else { + rules = rule.split("\\|\\|"); + elementsType = "|"; + } + if (rules.length == 1) { + try { + return ctx.read(rules[0]); + } catch (Exception e) { + return null; + } + } else { + List results = new ArrayList<>(); + for (String rl : rules) { + List temp = getList(rl); + if (temp != null && !temp.isEmpty()) { + results.add(temp); + if (temp.size() > 0 && elementsType.equals("|")) { + break; + } + } + } + if (results.size() > 0) { + if ("%".equals(elementsType)) { + for (int i = 0; i < results.get(0).size(); i++) { + for (List temp : results) { + if (i < temp.size()) { + result.add(temp.get(i)); + } + } + } + } else { + for (List temp : results) { + result.addAll(temp); + } + } + } + } + return result; + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeByJSoup.java b/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeByJSoup.java new file mode 100644 index 0000000..1d11342 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeByJSoup.java @@ -0,0 +1,454 @@ +package xyz.fycz.myreader.model.analyzeRule; + +import android.text.TextUtils; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Element; +import org.jsoup.nodes.TextNode; +import org.jsoup.select.Collector; +import org.jsoup.select.Elements; +import org.jsoup.select.Evaluator; +import org.seimicrawler.xpath.JXNode; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import xyz.fycz.myreader.util.utils.StringUtils; + +import static android.text.TextUtils.isEmpty; + +/** + * Created by GKF on 2018/1/25. + * 书源规则解析 + */ + +public class AnalyzeByJSoup { + private Element element; + + public AnalyzeByJSoup parse(Object doc) { + if (doc instanceof Element) { + element = (Element) doc; + } else if (doc instanceof JXNode) { + JXNode jxNode = (JXNode) doc; + if (jxNode.isElement()) { + element = jxNode.asElement(); + } else { + element = Jsoup.parse(jxNode.value().toString()); + } + } else { + element = Jsoup.parse(doc.toString()); + } + return this; + } + + /** + * 获取列表 + */ + Elements getElements(String rule) { + return getElements(element, rule); + } + + /** + * 合并内容列表,得到内容 + */ + String getString(String ruleStr) { + if (isEmpty(ruleStr)) { + return null; + } + List textS = getStringList(ruleStr); + if (textS.size() == 0) { + return null; + } + return TextUtils.join(",", textS).trim(); + } + + /** + * 获取一个字符串 + **/ + String getString0(String ruleStr) { + List urlList = getStringList(ruleStr); + if (!urlList.isEmpty()) { + return urlList.get(0); + } + return ""; + } + + /** + * 获取所有内容列表 + */ + List getStringList(String ruleStr) { + List textS = new ArrayList<>(); + if (isEmpty(ruleStr)) { + return textS; + } + //拆分规则 + SourceRule sourceRule = new SourceRule(ruleStr); + if (isEmpty(sourceRule.elementsRule)) { + textS.add(element.data()); + } else { + String elementsType; + String[] ruleStrS; + if (sourceRule.elementsRule.contains("&")) { + elementsType = "&"; + ruleStrS = sourceRule.elementsRule.split("&+"); + } else if (sourceRule.elementsRule.contains("%%")) { + elementsType = "%"; + ruleStrS = sourceRule.elementsRule.split("%%"); + } else { + elementsType = "|"; + if (sourceRule.isCss) { + ruleStrS = sourceRule.elementsRule.split("\\|\\|"); + } else { + ruleStrS = sourceRule.elementsRule.split("\\|+"); + } + } + List> results = new ArrayList<>(); + for (String ruleStrX : ruleStrS) { + List temp; + if (sourceRule.isCss) { + int lastIndex = ruleStrX.lastIndexOf('@'); + temp = getResultLast(element.select(ruleStrX.substring(0, lastIndex)), ruleStrX.substring(lastIndex + 1)); + } else { + temp = getResultList(ruleStrX); + } + if (temp != null && !temp.isEmpty()) { + results.add(temp); + if (!results.isEmpty() && elementsType.equals("|")) { + break; + } + } + } + if (results.size() > 0) { + if ("%".equals(elementsType)) { + for (int i = 0; i < results.get(0).size(); i++) { + for (List temp : results) { + if (i < temp.size()) { + textS.add(temp.get(i)); + } + } + } + } else { + for (List temp : results) { + textS.addAll(temp); + } + } + } + } + if (!isEmpty(sourceRule.replaceRegex)) { + List tempList = new ArrayList<>(textS); + textS.clear(); + for (String text : tempList) { + text = text.replaceAll(sourceRule.replaceRegex, sourceRule.replacement); + if (text.length() > 0) { + textS.add(text); + } + } + } + return textS; + } + + /** + * 获取Elements + */ + private Elements getElements(Element temp, String rule) { + Elements elements = new Elements(); + if (temp == null || isEmpty(rule)) { + return elements; + } + SourceRule sourceRule = new SourceRule(rule); + String elementsType; + String[] ruleStrS; + if (sourceRule.elementsRule.contains("&")) { + elementsType = "&"; + ruleStrS = sourceRule.elementsRule.split("&+"); + } else if (sourceRule.elementsRule.contains("%")) { + elementsType = "%"; + ruleStrS = sourceRule.elementsRule.split("%+"); + } else { + elementsType = "|"; + if (sourceRule.isCss) { + ruleStrS = sourceRule.elementsRule.split("\\|\\|"); + } else { + ruleStrS = sourceRule.elementsRule.split("\\|+"); + } + } + List elementsList = new ArrayList<>(); + if (sourceRule.isCss) { + for (String ruleStr : ruleStrS) { + Elements tempS = temp.select(ruleStr); + elementsList.add(tempS); + if (tempS.size() > 0 && elementsType.equals("|")) { + break; + } + } + } else { + for (String ruleStr : ruleStrS) { + Elements tempS = getElementsSingle(temp, ruleStr); + elementsList.add(tempS); + if (tempS.size() > 0 && elementsType.equals("|")) { + break; + } + } + } + if (elementsList.size() > 0) { + if ("%".equals(elementsType)) { + for (int i = 0; i < elementsList.get(0).size(); i++) { + for (Elements es : elementsList) { + if (i < es.size()) { + elements.add(es.get(i)); + } + } + } + } else { + for (Elements es : elementsList) { + elements.addAll(es); + } + } + } + return elements; + } + + private Elements filterElements(Elements elements, String[] rules) { + if (rules == null || rules.length < 2) return elements; + Elements selectedEls = new Elements(); + for (Element ele : elements) { + boolean isOk = false; + switch (rules[0]) { + case "class": + isOk = ele.getElementsByClass(rules[1]).size() > 0; + break; + case "id": + isOk = ele.getElementById(rules[1]) != null; + break; + case "tag": + isOk = ele.getElementsByTag(rules[1]).size() > 0; + break; + case "text": + isOk = ele.getElementsContainingOwnText(rules[1]).size() > 0; + break; + } + if (isOk) { + selectedEls.add(ele); + } + } + return selectedEls; + } + + /** + * 获取Elements按照一个规则 + */ + private Elements getElementsSingle(Element temp, String rule) { + Elements elements = new Elements(); + try { + String[] rs = rule.trim().split("@"); + if (rs.length > 1) { + elements.add(temp); + for (String rl : rs) { + Elements es = new Elements(); + for (Element et : elements) { + es.addAll(getElements(et, rl)); + } + elements.clear(); + elements.addAll(es); + } + } else { + String[] rulePcx = rule.split("!"); + String[] rulePc = rulePcx[0].trim().split(">"); + String[] rules = rulePc[0].trim().split("\\."); + String[] filterRules = null; + boolean needFilterElements = rulePc.length > 1 && !isEmpty(rulePc[1].trim()); + if (needFilterElements) { + filterRules = rulePc[1].trim().split("\\."); + filterRules[0] = filterRules[0].trim(); + List validKeys = Arrays.asList("class", "id", "tag", "text"); + if (filterRules.length < 2 || !validKeys.contains(filterRules[0]) || isEmpty(filterRules[1].trim())) { + needFilterElements = false; + } + filterRules[1] = filterRules[1].trim(); + } + switch (rules[0]) { + case "children": + Elements children = temp.children(); + if (needFilterElements) + children = filterElements(children, filterRules); + elements.addAll(children); + break; + case "class": + Elements elementsByClass = temp.getElementsByClass(rules[1]); + if (rules.length == 3) { + int index = Integer.parseInt(rules[2]); + if (index < 0) { + elements.add(elementsByClass.get(elementsByClass.size() + index)); + } else { + elements.add(elementsByClass.get(index)); + } + } else { + if (needFilterElements) + elementsByClass = filterElements(elementsByClass, filterRules); + elements.addAll(elementsByClass); + } + break; + case "tag": + Elements elementsByTag = temp.getElementsByTag(rules[1]); + if (rules.length == 3) { + int index = Integer.parseInt(rules[2]); + if (index < 0) { + elements.add(elementsByTag.get(elementsByTag.size() + index)); + } else { + elements.add(elementsByTag.get(index)); + } + } else { + if (needFilterElements) + elementsByTag = filterElements(elementsByTag, filterRules); + elements.addAll(elementsByTag); + } + break; + case "id": + Elements elementsById = Collector.collect(new Evaluator.Id(rules[1]), temp); + if (rules.length == 3) { + int index = Integer.parseInt(rules[2]); + if (index < 0) { + elements.add(elementsById.get(elementsById.size() + index)); + } else { + elements.add(elementsById.get(index)); + } + } else { + if (needFilterElements) + elementsById = filterElements(elementsById, filterRules); + elements.addAll(elementsById); + } + break; + case "text": + Elements elementsByText = temp.getElementsContainingOwnText(rules[1]); + if (needFilterElements) + elementsByText = filterElements(elementsByText, filterRules); + elements.addAll(elementsByText); + break; + default: + elements.addAll(temp.select(rulePcx[0])); + } + if (rulePcx.length > 1) { + String[] rulePcs = rulePcx[1].split(":"); + for (String pc : rulePcs) { + int pcInt = Integer.parseInt(pc); + if (pcInt < 0 && elements.size() + pcInt >= 0) { + elements.set(elements.size() + pcInt, null); + } else if (Integer.parseInt(pc) < elements.size()) { + elements.set(Integer.parseInt(pc), null); + } + } + Elements es = new Elements(); + es.add(null); + elements.removeAll(es); + } + } + } catch (Exception ignore) { + } + return elements; + } + + /** + * 获取内容列表 + */ + private List getResultList(String ruleStr) { + if (isEmpty(ruleStr)) { + return null; + } + Elements elements = new Elements(); + elements.add(element); + String[] rules = ruleStr.split("@"); + for (int i = 0; i < rules.length - 1; i++) { + Elements es = new Elements(); + for (Element elt : elements) { + es.addAll(getElementsSingle(elt, rules[i])); + } + elements.clear(); + elements = es; + } + if (elements.isEmpty()) { + return null; + } + return getResultLast(elements, rules[rules.length - 1]); + } + + /** + * 根据最后一个规则获取内容 + */ + private List getResultLast(Elements elements, String lastRule) { + List textS = new ArrayList<>(); + List cText = new ArrayList<>(); + try { + switch (lastRule) { + case "text": + for (Element element : elements) { + String text = element.text(); + cText.add(text); + } + textS.add(TextUtils.join("\n", cText)); + break; + case "textNodes": + for (Element element : elements) { + List contentEs = element.textNodes(); + for (int i = 0; i < contentEs.size(); i++) { + String temp = contentEs.get(i).text().trim(); + if (!isEmpty(temp)) { + cText.add(temp); + } + } + } + textS.add(TextUtils.join("\n", cText)); + break; + case "ownText": + for (Element element : elements) { + cText.add(element.ownText()); + } + textS.add(TextUtils.join("\n", cText)); + break; + case "html": + elements.select("script, style").remove(); + String html = elements.html(); + textS.add(html); + break; + case "all": + textS.add(elements.outerHtml()); + break; + default: + for (Element element : elements) { + String url = element.attr(lastRule); + if (!TextUtils.isEmpty(url) && !textS.contains(url)) { + textS.add(url); + } + } + } + } catch (Exception ignore) { + } + return textS; + } + + class SourceRule { + boolean isCss = false; + String elementsRule; + String replaceRegex = ""; + String replacement = ""; + + SourceRule(String ruleStr) { + if (StringUtils.startWithIgnoreCase(ruleStr, "@CSS:")) { + isCss = true; + elementsRule = ruleStr.substring(5).trim(); + return; + } + String[] ruleStrS; + //分离正则表达式 + ruleStrS = ruleStr.trim().split("#"); + elementsRule = ruleStrS[0]; + if (ruleStrS.length > 1) { + replaceRegex = ruleStrS[1]; + } + if (ruleStrS.length > 2) { + replacement = ruleStrS[2]; + } + } + } + +} diff --git a/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeByXPath.java b/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeByXPath.java new file mode 100644 index 0000000..b9e8645 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeByXPath.java @@ -0,0 +1,194 @@ +package xyz.fycz.myreader.model.analyzeRule; + +import android.text.TextUtils; + +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.select.Elements; +import org.seimicrawler.xpath.JXDocument; +import org.seimicrawler.xpath.JXNode; + +import java.util.ArrayList; +import java.util.List; + +public class AnalyzeByXPath { + private JXDocument jxDocument; + private JXNode jxNode; + + public AnalyzeByXPath parse(Object doc) { + if (doc instanceof JXNode) { + jxNode = (JXNode) doc; + if (!jxNode.isElement()) { + jxDocument = strToJXDocument(doc.toString()); + jxNode = null; + } + } else if (doc instanceof Document) { + jxDocument = JXDocument.create((Document) doc); + jxNode = null; + } else if (doc instanceof Element) { + jxDocument = JXDocument.create(new Elements((Element) doc)); + jxNode = null; + } else if (doc instanceof Elements) { + jxDocument = JXDocument.create((Elements) doc); + jxNode = null; + } else { + jxDocument = strToJXDocument(doc.toString()); + jxNode = null; + } + return this; + } + + private JXDocument strToJXDocument(String html) { + if (html.endsWith("")) { + html = String.format("%s", html); + } + if (html.endsWith("") || html.endsWith("")) { + html = String.format("%s
", html); + } + return JXDocument.create(html); + } + + List getElements(String xPath) { + if (TextUtils.isEmpty(xPath)) { + return null; + } + List jxNodes = new ArrayList<>(); + String elementsType; + String[] rules; + if (xPath.contains("&&")) { + rules = xPath.split("&&"); + elementsType = "&"; + } else if (xPath.contains("%%")) { + rules = xPath.split("%%"); + elementsType = "%"; + } else { + rules = xPath.split("\\|\\|"); + elementsType = "|"; + } + if (rules.length == 1) { + if (jxNode != null) { + return jxNode.sel(rules[0]); + } + return jxDocument.selN(rules[0]); + } else { + List> results = new ArrayList<>(); + for (String rl : rules) { + List temp = getElements(rl); + if (temp != null && !temp.isEmpty()) { + results.add(temp); + if (temp.size() > 0 && elementsType.equals("|")) { + break; + } + } + } + if (results.size() > 0) { + if ("%".equals(elementsType)) { + for (int i = 0; i < results.get(0).size(); i++) { + for (List temp : results) { + if (i < temp.size()) { + jxNodes.add(temp.get(i)); + } + } + } + } else { + for (List temp : results) { + jxNodes.addAll(temp); + } + } + } + } + return jxNodes; + } + + List getStringList(String xPath) { + List result = new ArrayList<>(); + String elementsType; + String[] rules; + if (xPath.contains("&&")) { + rules = xPath.split("&&"); + elementsType = "&"; + } else if (xPath.contains("%%")) { + rules = xPath.split("%%"); + elementsType = "%"; + } else { + rules = xPath.split("\\|\\|"); + elementsType = "|"; + } + if (rules.length == 1) { + List jxNodes; + if (jxNode != null) { + jxNodes = jxNode.sel(xPath); + } else { + jxNodes = jxDocument.selN(xPath); + } + for (JXNode jxNode : jxNodes) { + /*if(jxNode.isString()){ + result.add(String.valueOf(jxNode)); + }*/ + result.add(String.valueOf(jxNode)); + } + return result; + } else { + List> results = new ArrayList<>(); + for (String rl : rules) { + List temp = getStringList(rl); + if (temp != null && !temp.isEmpty()) { + results.add(temp); + if (temp.size() > 0 && elementsType.equals("|")) { + break; + } + } + } + if (results.size() > 0) { + if ("%".equals(elementsType)) { + for (int i = 0; i < results.get(0).size(); i++) { + for (List temp : results) { + if (i < temp.size()) { + result.add(temp.get(i)); + } + } + } + } else { + for (List temp : results) { + result.addAll(temp); + } + } + } + } + return result; + } + + public String getString(String rule) { + String[] rules; + String elementsType; + if (rule.contains("&&")) { + rules = rule.split("&&"); + elementsType = "&"; + } else { + rules = rule.split("\\|\\|"); + elementsType = "|"; + } + if (rules.length == 1) { + List jxNodes; + if (jxNode != null) { + jxNodes = jxNode.sel(rule); + } else { + jxNodes = jxDocument.selN(rule); + } + if (jxNodes == null) return null; + return TextUtils.join(",", jxNodes); + } else { + List textS = new ArrayList<>(); + for (String rl : rules) { + String temp = getString(rl); + if (!TextUtils.isEmpty(temp)) { + textS.add(temp); + if (elementsType.equals("|")) { + break; + } + } + } + return TextUtils.join(",", textS).trim(); + } + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeRule.java b/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeRule.java new file mode 100644 index 0000000..8e52049 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeRule.java @@ -0,0 +1,540 @@ +package xyz.fycz.myreader.model.analyzeRule; + +import android.annotation.SuppressLint; + +import androidx.annotation.Keep; + +import com.google.gson.Gson; + +import org.jsoup.nodes.Entities; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.script.SimpleBindings; + +import xyz.fycz.myreader.util.StringHelper; +import xyz.fycz.myreader.util.utils.JsExtensions; +import xyz.fycz.myreader.util.utils.NetworkUtils; +import xyz.fycz.myreader.util.utils.StringUtils; + +import static android.text.TextUtils.isEmpty; +import static xyz.fycz.myreader.common.APPCONST.EXP_PATTERN; +import static xyz.fycz.myreader.common.APPCONST.JS_PATTERN; +import static xyz.fycz.myreader.common.APPCONST.MAP_STRING; +import static xyz.fycz.myreader.common.APPCONST.SCRIPT_ENGINE; +import static xyz.fycz.myreader.util.utils.NetworkUtils.headerPattern; + + +/** + * Created by REFGD. + * 统一解析接口 + */ +@Keep +@SuppressWarnings({"unused", "WeakerAccess"}) +public class AnalyzeRule implements JsExtensions { + /*private static final Pattern putPattern = Pattern.compile("@put:(\\{[^}]+?\\})", Pattern.CASE_INSENSITIVE); + private static final Pattern getPattern = Pattern.compile("@get:\\{([^}]+?)\\}", Pattern.CASE_INSENSITIVE); + + private BaseBookBean book; + private Object object; + private Boolean isJSON = false; + private String baseUrl = null; + + private AnalyzeByXPath analyzeByXPath = null; + private AnalyzeByJSoup analyzeByJSoup = null; + private AnalyzeByJSonPath analyzeByJSonPath = null; + + private boolean objectChangedXP = false; + private boolean objectChangedJS = false; + private boolean objectChangedJP = false; + + public AnalyzeRule(BaseBookBean bookBean) { + book = bookBean; + } + + public void setBook(BaseBookBean book) { + this.book = book; + } + + public AnalyzeRule setContent(Object body) { + return setContent(body, baseUrl); + } + + public Object getContent() { + return object; + } + + public AnalyzeRule setContent(Object body, String baseUrl) { + if (body == null) throw new AssertionError("Content cannot be null"); + isJSON = StringUtils.isJsonType(String.valueOf(body)); + object = body; + if (baseUrl != null) { + this.baseUrl = headerPattern.matcher(baseUrl).replaceAll(""); + } + objectChangedXP = true; + objectChangedJS = true; + objectChangedJP = true; + return this; + } + + public String getBaseUrl() { + return this.baseUrl; + } + + *//** + * 获取XPath解析类 + *//* + private AnalyzeByXPath getAnalyzeByXPath(Object o) { + if (o != object) { + return new AnalyzeByXPath().parse(o); + } + return getAnalyzeByXPath(); + } + + private AnalyzeByXPath getAnalyzeByXPath() { + if (analyzeByXPath == null || objectChangedXP) { + analyzeByXPath = new AnalyzeByXPath(); + analyzeByXPath.parse(object); + objectChangedXP = false; + } + return analyzeByXPath; + } + + *//** + * 获取JSOUP解析类 + *//* + private AnalyzeByJSoup getAnalyzeByJSoup(Object o) { + if (o != object) { + return new AnalyzeByJSoup().parse(o); + } + return getAnalyzeByJSoup(); + } + + private AnalyzeByJSoup getAnalyzeByJSoup() { + if (analyzeByJSoup == null || objectChangedJS) { + analyzeByJSoup = new AnalyzeByJSoup(); + analyzeByJSoup.parse(object); + objectChangedJS = false; + } + return analyzeByJSoup; + } + + *//** + * 获取JSON解析类 + *//* + private AnalyzeByJSonPath getAnalyzeByJSonPath(Object o) { + if (o != object) { + return new AnalyzeByJSonPath().parse(o); + } + return getAnalyzeByJSonPath(); + } + + private AnalyzeByJSonPath getAnalyzeByJSonPath() { + if (analyzeByJSonPath == null || objectChangedJP) { + analyzeByJSonPath = new AnalyzeByJSonPath(); + analyzeByJSonPath.parse(object); + objectChangedJP = false; + } + return analyzeByJSonPath; + } + + *//** + * 获取文本列表 + *//* + public List getStringList(String rule) throws Exception { + return getStringList(rule, false); + } + + public List getStringList(String rule, boolean isUrl) throws Exception { + if (isEmpty(rule)) return null; + List ruleList = splitSourceRule(rule); + return getStringList(ruleList, isUrl); + } + + @SuppressWarnings({"unchecked"}) + public List getStringList(List ruleList, boolean isUrl) throws Exception { + Object result = null; + if (!ruleList.isEmpty()) result = object; + for (SourceRule rule : ruleList) { + if (!isEmpty(rule.rule)) { + switch (rule.mode) { + case Js: + result = evalJS(rule.rule, result); + break; + case JSon: + result = getAnalyzeByJSonPath(result).getStringList(rule.rule); + break; + case XPath: + result = getAnalyzeByXPath(result).getStringList(rule.rule); + break; + default: + result = getAnalyzeByJSoup(result).getStringList(rule.rule); + } + } + if (!isEmpty(rule.replaceRegex) && result instanceof List) { + List newList = new ArrayList<>(); + //noinspection rawtypes + for (Object item : (List) result) { + newList.add(replaceRegex(String.valueOf(item), rule)); + } + result = newList; + } else if (!isEmpty(rule.replaceRegex)) { + result = replaceRegex(String.valueOf(result), rule); + } + } + if (result == null) return new ArrayList<>(); + if (result instanceof String) { + result = Arrays.asList(StringUtils.formatHtml((String) result).split("\n")); + } + if (isUrl && !isEmpty(baseUrl)) { + List urlList = new ArrayList<>(); + //noinspection rawtypes + for (Object url : (List) result) { + String absoluteURL = NetworkUtils.getAbsoluteURL(baseUrl, String.valueOf(url)); + if (!urlList.contains(absoluteURL) && !isEmpty(absoluteURL)) { + urlList.add(absoluteURL); + } + } + return urlList; + } + return (List) result; + } + + *//** + * 获取文本 + *//* + public String getString(String rule) throws Exception { + return getString(rule, false); + } + + public String getString(String ruleStr, boolean isUrl) throws Exception { + if (isEmpty(ruleStr)) return null; + List ruleList = splitSourceRule(ruleStr); + return getString(ruleList, isUrl); + } + + public String getString(List ruleList) throws Exception { + return getString(ruleList, false); + } + + public String getString(List ruleList, boolean isUrl) throws Exception { + Object result = null; + if (!ruleList.isEmpty()) result = object; + for (SourceRule rule : ruleList) { + if (!StringHelper.isEmpty(rule.rule)) { + switch (rule.mode) { + case Js: + result = evalJS(rule.rule, result); + break; + case JSon: + result = getAnalyzeByJSonPath(result).getString(rule.rule); + break; + case XPath: + result = getAnalyzeByXPath(result).getString(rule.rule); + break; + case Default: + if (isUrl && !isEmpty(baseUrl)) { + result = getAnalyzeByJSoup(result).getString0(rule.rule); + } else { + result = getAnalyzeByJSoup(result).getString(rule.rule); + } + } + } + if (!isEmpty(rule.replaceRegex)) { + result = replaceRegex(String.valueOf(result), rule); + } + } + if (result == null) return ""; + if (isUrl && !StringHelper.isEmpty(baseUrl)) { + return NetworkUtils.getAbsoluteURL(baseUrl, Entities.unescape(String.valueOf(result))); + } + try { + return Entities.unescape(String.valueOf(result)); + } catch (Exception e) { + return String.valueOf(result); + } + } + + *//** + * 获取Element + *//* + public Object getElement(String ruleStr) throws Exception { + List ruleList = splitSourceRule(ruleStr); + Object result = object; + for (SourceRule rule : ruleList) { + switch (rule.mode) { + case Js: + result = evalJS(rule.rule, result); + break; + case JSon: + result = getAnalyzeByJSonPath(result).getObject(rule.rule); + break; + case XPath: + result = getAnalyzeByXPath(result).getElements(rule.rule); + break; + default: + result = getAnalyzeByJSoup(result).getElements(rule.rule); + } + if (!isEmpty(rule.replaceRegex) && result instanceof String) { + result = replaceRegex(String.valueOf(result), rule); + } + } + return result; + } + + *//** + * 获取列表 + *//* + @SuppressWarnings("unchecked") + public List getElements(String ruleStr) throws Exception { + List ruleList = splitSourceRule(ruleStr); + Object result = null; + if (!ruleList.isEmpty()) result = object; + for (SourceRule rule : ruleList) { + switch (rule.mode) { + case Js: + result = evalJS(rule.rule, result); + break; + case JSon: + result = getAnalyzeByJSonPath(result).getList(rule.rule); + break; + case XPath: + result = getAnalyzeByXPath(result).getElements(rule.rule); + break; + default: + result = getAnalyzeByJSoup(result).getElements(rule.rule); + } + if (!isEmpty(rule.replaceRegex) && result instanceof String) { + result = replaceRegex(String.valueOf(result), rule); + } + } + if (result == null) { + return new ArrayList<>(); + } + return (List) result; + } + + *//** + * 保存变量 + *//* + private void putRule(Map map) throws Exception { + for (Map.Entry entry : map.entrySet()) { + if (book != null) { + book.putVariable(entry.getKey(), getString(entry.getValue())); + } + } + } + + *//** + * 分离并执行put规则 + *//* + private String splitPutRule(String ruleStr) throws Exception { + Matcher putMatcher = putPattern.matcher(ruleStr); + while (putMatcher.find()) { + ruleStr = ruleStr.replace(putMatcher.group(), ""); + Map map = new Gson().fromJson(putMatcher.group(1), MAP_STRING); + putRule(map); + } + return ruleStr; + } + + *//** + * 替换@get + *//* + public String replaceGet(String ruleStr) { + Matcher getMatcher = getPattern.matcher(ruleStr); + while (getMatcher.find()) { + String value = ""; + if (book != null && book.getVariableMap() != null) { + value = book.getVariableMap().get(getMatcher.group(1)); + if (value == null) value = ""; + } + ruleStr = ruleStr.replace(getMatcher.group(), value); + } + return ruleStr; + } + + *//** + * 正则替换 + *//* + private String replaceRegex(String result, SourceRule rule) { + if (!isEmpty(rule.replaceRegex)) { + if (rule.replaceFirst) { + Pattern pattern = Pattern.compile(rule.replaceRegex); + Matcher matcher = pattern.matcher(String.valueOf(result)); + if (matcher.find()) { + result = matcher.group(0).replaceFirst(rule.replaceRegex, rule.replacement); + } else { + result = ""; + } + } else { + result = String.valueOf(result).replaceAll(rule.replaceRegex, rule.replacement); + } + } + return result; + } + + *//** + * 替换JS + *//* + @SuppressLint("DefaultLocale") + private String replaceJs(String ruleStr) throws Exception { + if (ruleStr.contains("{{") && ruleStr.contains("}}")) { + Object jsEval; + StringBuffer sb = new StringBuffer(ruleStr.length()); + Matcher expMatcher = EXP_PATTERN.matcher(ruleStr); + while (expMatcher.find()) { + jsEval = evalJS(expMatcher.group(1), object); + if (jsEval instanceof String) { + expMatcher.appendReplacement(sb, (String) jsEval); + } else if (jsEval instanceof Double && ((Double) jsEval) % 1.0 == 0) { + expMatcher.appendReplacement(sb, String.format("%.0f", (Double) jsEval)); + } else { + expMatcher.appendReplacement(sb, String.valueOf(jsEval)); + } + } + expMatcher.appendTail(sb); + ruleStr = sb.toString(); + } + return ruleStr; + } + + *//** + * 分解规则生成规则列表 + *//* + public List splitSourceRule(String ruleStr) throws Exception { + List ruleList = new ArrayList<>(); + if (isEmpty(ruleStr)) return ruleList; + //检测Mode + Mode mode; + if (StringUtils.startWithIgnoreCase(ruleStr, "@XPath:")) { + mode = Mode.XPath; + ruleStr = ruleStr.substring(7); + } else if (StringUtils.startWithIgnoreCase(ruleStr, "@JSon:")) { + mode = Mode.JSon; + ruleStr = ruleStr.substring(6); + } else { + if (isJSON) { + mode = Mode.JSon; + } else { + mode = Mode.Default; + } + } + //分离put规则 + ruleStr = splitPutRule(ruleStr); + //替换get值 + ruleStr = replaceGet(ruleStr); + //替换js + ruleStr = replaceJs(ruleStr); + //拆分为列表 + int start = 0; + String tmp; + Matcher jsMatcher = JS_PATTERN.matcher(ruleStr); + while (jsMatcher.find()) { + if (jsMatcher.start() > start) { + tmp = ruleStr.substring(start, jsMatcher.start()).replaceAll("\n", "").trim(); + if (!isEmpty(tmp)) { + ruleList.add(new SourceRule(tmp, mode)); + } + } + ruleList.add(new SourceRule(jsMatcher.group(), Mode.Js)); + start = jsMatcher.end(); + } + if (ruleStr.length() > start) { + tmp = ruleStr.substring(start).replaceAll("\n", "").trim(); + if (!isEmpty(tmp)) { + ruleList.add(new SourceRule(tmp, mode)); + } + } + return ruleList; + } + + *//** + * 规则类 + *//* + public static class SourceRule { + Mode mode; + String rule; + String replaceRegex = ""; + String replacement = ""; + boolean replaceFirst = false; + + SourceRule(String ruleStr, Mode mainMode) { + this.mode = mainMode; + if (mode == Mode.Js) { + if (ruleStr.startsWith("")) { + rule = ruleStr.substring(4, ruleStr.lastIndexOf("<")); + } else { + rule = ruleStr.substring(4); + } + } else { + if (StringUtils.startWithIgnoreCase(ruleStr, "@XPath:")) { + mode = Mode.XPath; + rule = ruleStr.substring(7); + } else if (StringUtils.startWithIgnoreCase(ruleStr, "//")) {//XPath特征很明显,无需配置单独的识别标头 + mode = Mode.XPath; + rule = ruleStr; + } else if (StringUtils.startWithIgnoreCase(ruleStr, "@JSon:")) { + mode = Mode.JSon; + rule = ruleStr.substring(6); + } else if (ruleStr.startsWith("$.")) { + mode = Mode.JSon; + rule = ruleStr; + } else { + rule = ruleStr; + } + //分离正则表达式 + String[] ruleStrS = rule.trim().split("##"); + rule = ruleStrS[0]; + if (ruleStrS.length > 1) { + replaceRegex = ruleStrS[1]; + } + if (ruleStrS.length > 2) { + replacement = ruleStrS[2]; + } + if (ruleStrS.length > 3) { + replaceFirst = true; + } + } + } + + } + + private enum Mode { + XPath, JSon, Default, Js + } + + public String put(String key, String value) { + if (book != null) { + book.putVariable(key, value); + } + return value; + } + + public String get(String key) { + if (book == null) { + return null; + } + if (book.getVariableMap() == null) { + return null; + } + return book.getVariableMap().get(key); + } + + *//** + * 执行JS + *//* + private Object evalJS(String jsStr, Object result) throws Exception { + SimpleBindings bindings = new SimpleBindings(); + bindings.put("java", this); + bindings.put("result", result); + bindings.put("baseUrl", baseUrl); + return SCRIPT_ENGINE.eval(jsStr, bindings); + }*/ + +} diff --git a/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeUrl.java b/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeUrl.java new file mode 100644 index 0000000..eedf161 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeUrl.java @@ -0,0 +1,316 @@ +package xyz.fycz.myreader.model.analyzeRule; + +import android.annotation.SuppressLint; +import android.text.TextUtils; + +import androidx.annotation.Keep; + +import com.google.gson.Gson; + +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.script.SimpleBindings; + +import xyz.fycz.myreader.util.StringHelper; +import xyz.fycz.myreader.util.utils.JsExtensions; +import xyz.fycz.myreader.util.utils.NetworkUtils; +import xyz.fycz.myreader.util.utils.StringUtils; +import xyz.fycz.myreader.util.utils.UrlEncoderUtils; + +import static xyz.fycz.myreader.common.APPCONST.EXP_PATTERN; +import static xyz.fycz.myreader.common.APPCONST.JS_PATTERN; +import static xyz.fycz.myreader.common.APPCONST.MAP_STRING; +import static xyz.fycz.myreader.common.APPCONST.SCRIPT_ENGINE; +import static xyz.fycz.myreader.util.utils.NetworkUtils.headerPattern; + + +/** + * Created by GKF on 2018/1/24. + * 搜索URL规则解析 + */ +@Keep +public class AnalyzeUrl implements JsExtensions { + private static final Pattern pagePattern = Pattern.compile("\\{(.*?)\\}"); + private String baseUrl; + private String url; + private String host; + private String urlPath; + private String queryStr; + private Map queryMap = new LinkedHashMap<>(); + private Map headerMap = new HashMap<>(); + private String charCode = null; + private UrlMode urlMode = UrlMode.DEFAULT; + + public AnalyzeUrl(String urlRule) throws Exception { + this(urlRule, null, null, null, null); + } + + public AnalyzeUrl(String urlRule, Map headerMapF, String baseUrl) throws Exception { + this(urlRule, null, null, headerMapF, baseUrl); + } + + @SuppressLint("DefaultLocale") + public AnalyzeUrl(String ruleUrl, final String key, final Integer page, Map headerMapF, String baseUrl) throws Exception { + if (!TextUtils.isEmpty(baseUrl)) { + this.baseUrl = headerPattern.matcher(baseUrl).replaceAll(""); + } + //替换关键字 + if (!StringHelper.isEmpty(key)) { + // 处理searchKey=searchKey的情况 + if (ruleUrl.matches("=[\\s{(]*searchKey")) + ruleUrl = ruleUrl.replaceFirst("=[\\s{(]*searchKey", "=" + key); + else + ruleUrl = ruleUrl.replace("searchKey", key); + } + //判断是否有下一页 + if (page != null && page > 1 && !ruleUrl.contains("searchPage")) + throw new Exception("没有下一页"); + //替换js + ruleUrl = replaceJs(ruleUrl, baseUrl, page, key); + //解析Header + ruleUrl = analyzeHeader(ruleUrl, headerMapF); + //分离编码规则 + ruleUrl = splitCharCode(ruleUrl); + //设置页数 + ruleUrl = analyzePage(ruleUrl, page); + //执行规则列表 + List ruleList = splitRule(ruleUrl); + for (String rule : ruleList) { + if (rule.startsWith("")) { + rule = rule.substring(4, rule.lastIndexOf("<")); + ruleUrl = (String) evalJS(rule, ruleUrl); + } else if (rule.startsWith("@js:")) { + rule = rule.substring(4); + ruleUrl = (String) evalJS(rule, ruleUrl); + } else { + ruleUrl = rule.replace("@result", ruleUrl); + } + } + //分离post参数 + String[] ruleUrlS = ruleUrl.split("@"); + if (ruleUrlS.length > 1) { + urlMode = UrlMode.POST; + } else { + //分离get参数 + ruleUrlS = ruleUrlS[0].split("\\?"); + if (ruleUrlS.length > 1) { + urlMode = UrlMode.GET; + } + } + generateUrlPath(ruleUrlS[0]); + if (urlMode != UrlMode.DEFAULT) { + analyzeQuery(ruleUrlS[1]); + } + } + + /** + * 解析Header + */ + private String analyzeHeader(String ruleUrl, Map headerMapF) { + if (headerMapF != null) { + headerMap.putAll(headerMapF); + } + Matcher matcher = headerPattern.matcher(ruleUrl); + if (matcher.find()) { + String find = matcher.group(0); + ruleUrl = ruleUrl.replace(find, ""); + find = find.substring(8); + try { + Map map = new Gson().fromJson(find, MAP_STRING); + headerMap.putAll(map); + } catch (Exception ignored) { + } + } + return ruleUrl; + } + + /** + * 分离编码规则 + */ + private String splitCharCode(String rule) { + String[] ruleUrlS = rule.split("\\|"); + if (ruleUrlS.length > 1) { + if (!TextUtils.isEmpty(ruleUrlS[1])) { + String[] qtS = ruleUrlS[1].split("&"); + for (String qt : qtS) { + String[] gz = qt.split("="); + if (gz[0].equals("char")) { + charCode = gz[1]; + } + } + } + } + return ruleUrlS[0]; + } + + /** + * 解析页数 + */ + private String analyzePage(String ruleUrl, final Integer searchPage) { + if (searchPage == null) return ruleUrl; + Matcher matcher = pagePattern.matcher(ruleUrl); + while (matcher.find()) { + String[] pages = matcher.group(1).split(","); + if (searchPage <= pages.length) { + ruleUrl = ruleUrl.replace(matcher.group(), pages[searchPage - 1].trim()); + } else { + ruleUrl = ruleUrl.replace(matcher.group(), pages[pages.length - 1].trim()); + } + } + return ruleUrl.replace("searchPage-1", String.valueOf(searchPage - 1)) + .replace("searchPage+1", String.valueOf(searchPage + 1)) + .replace("searchPage", String.valueOf(searchPage)); + } + + /** + * 替换js + */ + @SuppressLint("DefaultLocale") + private String replaceJs(String ruleUrl, String baseUrl, Integer searchPage, String searchKey) throws Exception { + if (ruleUrl.contains("{{") && ruleUrl.contains("}}")) { + Object jsEval; + StringBuffer sb = new StringBuffer(ruleUrl.length()); + SimpleBindings simpleBindings = new SimpleBindings() {{ + this.put("baseUrl", baseUrl); + this.put("searchPage", searchPage); + this.put("searchKey", searchKey); + }}; + Matcher expMatcher = EXP_PATTERN.matcher(ruleUrl); + while (expMatcher.find()) { + jsEval = SCRIPT_ENGINE.eval(expMatcher.group(1), simpleBindings); + if (jsEval instanceof String) { + expMatcher.appendReplacement(sb, (String) jsEval); + } else if (jsEval instanceof Double && ((Double) jsEval) % 1.0 == 0) { + expMatcher.appendReplacement(sb, String.format("%.0f", (Double) jsEval)); + } else { + expMatcher.appendReplacement(sb, String.valueOf(jsEval)); + } + } + expMatcher.appendTail(sb); + ruleUrl = sb.toString(); + } + return ruleUrl; + } + + /** + * 解析QueryMap + */ + private void analyzeQuery(String allQuery) throws Exception { + queryStr = allQuery; + String[] queryS = allQuery.split("&"); + for (String query : queryS) { + String[] queryM = query.split("="); + String value = queryM.length > 1 ? queryM[1] : ""; + if (TextUtils.isEmpty(charCode)) { + if (UrlEncoderUtils.hasUrlEncoded(value)) { + queryMap.put(queryM[0], value); + } else { + queryMap.put(queryM[0], URLEncoder.encode(value, "UTF-8")); + } + } else if (charCode.equals("escape")) { + queryMap.put(queryM[0], StringUtils.escape(value)); + } else { + queryMap.put(queryM[0], URLEncoder.encode(value, charCode)); + } + } + } + + /** + * 拆分规则 + */ + private List splitRule(String ruleStr) { + List ruleList = new ArrayList<>(); + Matcher jsMatcher = JS_PATTERN.matcher(ruleStr); + int start = 0; + String tmp; + while (jsMatcher.find()) { + if (jsMatcher.start() > start) { + tmp = ruleStr.substring(start, jsMatcher.start()).replaceAll("\n", "").trim(); + if (!TextUtils.isEmpty(tmp)) { + ruleList.add(tmp); + } + } + ruleList.add(jsMatcher.group()); + start = jsMatcher.end(); + } + if (ruleStr.length() > start) { + tmp = ruleStr.substring(start).replaceAll("\n", "").trim(); + if (!TextUtils.isEmpty(tmp)) { + ruleList.add(tmp); + } + } + return ruleList; + } + + /** + * 分解URL + */ + private void generateUrlPath(String ruleUrl) { + url = NetworkUtils.getAbsoluteURL(baseUrl, ruleUrl); + host = StringUtils.getBaseUrl(url); + urlPath = url.substring(host.length()); + } + + /** + * 执行JS + */ + private Object evalJS(String jsStr, Object result) throws Exception { + SimpleBindings bindings = new SimpleBindings(); + bindings.put("result", result); + return SCRIPT_ENGINE.eval(jsStr, bindings); + } + + public String getCharCode() { + return charCode; + } + + public String getHost() { + return host; + } + + public String getPath() { + return urlPath; + } + + public String getUrl() { + return url; + } + + public Map getQueryMap() { + return queryMap; + } + + public Map getHeaderMap() { + return headerMap; + } + + public String getQueryStr() { + return queryStr; + } + + public byte[] getPostData() { + StringBuilder builder = new StringBuilder(); + Set keys = queryMap.keySet(); + for (String key : keys) { + builder.append(String.format("%s=%s&", key, queryMap.get(key))); + } + builder.deleteCharAt(builder.lastIndexOf("&")); + return builder.toString().getBytes(); + } + + public UrlMode getUrlMode() { + return urlMode; + } + + public enum UrlMode { + GET, POST, DEFAULT + } +} diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java index b0f86a2..691872f 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java @@ -28,21 +28,28 @@ import com.google.zxing.EncodeHintType; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import com.weaction.ddsdk.ad.DdSdkFlowAd; +import org.jetbrains.annotations.NotNull; + import java.io.File; import java.io.FileOutputStream; import java.util.ArrayList; import java.util.List; - import cn.bingoogolapple.qrcode.zxing.QRCodeEncoder; +import io.reactivex.Observable; +import io.reactivex.ObservableEmitter; +import io.reactivex.ObservableOnSubscribe; +import io.reactivex.ObservableSource; import io.reactivex.Single; import io.reactivex.SingleOnSubscribe; import io.reactivex.annotations.NonNull; +import io.reactivex.disposables.Disposable; +import io.reactivex.functions.Function; import xyz.fycz.myreader.R; -import xyz.fycz.myreader.ai.BookWordCountPre; import xyz.fycz.myreader.application.App; import xyz.fycz.myreader.application.SysManager; import xyz.fycz.myreader.base.BaseActivity; +import xyz.fycz.myreader.base.observer.MyObserver; import xyz.fycz.myreader.base.observer.MySingleObserver; import xyz.fycz.myreader.common.APPCONST; import xyz.fycz.myreader.common.URLCONST; @@ -52,8 +59,8 @@ import xyz.fycz.myreader.greendao.entity.Book; import xyz.fycz.myreader.greendao.entity.Chapter; import xyz.fycz.myreader.greendao.entity.rule.BookSource; import xyz.fycz.myreader.greendao.service.BookService; -import xyz.fycz.myreader.model.source.BookSourceManager; import xyz.fycz.myreader.greendao.service.ChapterService; +import xyz.fycz.myreader.model.source.BookSourceManager; import xyz.fycz.myreader.ui.adapter.BookTagAdapter; import xyz.fycz.myreader.ui.adapter.DetailCatalogAdapter; import xyz.fycz.myreader.ui.dialog.BookGroupDialog; @@ -93,13 +100,14 @@ public class BookDetailedActivity extends BaseActivity { private ChapterService mChapterService; private ReadCrawler mReadCrawler; private DetailCatalogAdapter mCatalogAdapter; - private ArrayList mChapters = new ArrayList<>(); - private ArrayList mNewestChapters = new ArrayList<>(); + private List mChapters = new ArrayList<>(); + private List mNewestChapters = new ArrayList<>(); private boolean isCollected; private SourceExchangeDialog mSourceDialog; private int sourceIndex; private BookGroupDialog mBookGroupDia; private List tagList = new ArrayList<>(); + private Disposable chaptersDis; @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { @@ -136,6 +144,12 @@ public class BookDetailedActivity extends BaseActivity { setContentView(binding.getRoot()); } + @Override + protected void onDestroy() { + if (chaptersDis != null) chaptersDis.dispose(); + super.onDestroy(); + } + @Override protected void initData(Bundle savedInstanceState) { super.initData(savedInstanceState); @@ -154,7 +168,7 @@ public class BookDetailedActivity extends BaseActivity { } //Dialog mSourceDialog = new SourceExchangeDialog(this, mBook); - if (isBookSourceNotExist()){ + if (isBookSourceNotExist()) { DialogCreator.createCommonDialog(this, "未知书源", "当前书籍的书源不存在,是否搜索以切换书源?", false, (dialog, which) -> { mSourceDialog.show(); @@ -229,6 +243,7 @@ public class BookDetailedActivity extends BaseActivity { initAd(); } + /** * type 信息流类型,只能填 1、4、6、3 (样式请看下表) * count 请求返回的信息流广告条目数,最小为 1,最大为 5,通常情况下建议一次返回一条 @@ -238,7 +253,7 @@ public class BookDetailedActivity extends BaseActivity { AdUtils.checkHasAd().subscribe(new MySingleObserver() { @Override public void onSuccess(@NonNull Boolean aBoolean) { - if (aBoolean){ + if (aBoolean) { AdUtils.initAd(); new DdSdkFlowAd().getFlowViews(BookDetailedActivity.this, 6, 1, new DdSdkFlowAd.FlowCallback() { // 信息流广告拉取完毕后返回的 views @@ -251,7 +266,7 @@ public class BookDetailedActivity extends BaseActivity { // 信息流广告展示后调用 @Override public void show() { - AdUtils.adRecord("flow","adShow"); + AdUtils.adRecord("flow", "adShow"); Log.i(TAG, "信息流广告展示成功"); } @@ -370,7 +385,7 @@ public class BookDetailedActivity extends BaseActivity { } } - private boolean isBookSourceNotExist(){ + private boolean isBookSourceNotExist() { BookSource source = BookSourceManager.getBookSourceByStr(mBook.getSource()); return source.getSourceEName() != null && "fynovel".equals(source.getSourceEName()); } @@ -391,19 +406,21 @@ public class BookDetailedActivity extends BaseActivity { if (rc instanceof BookInfoCrawler && StringHelper.isEmpty(mBook.getImgUrl())) { binding.pbLoading.setVisibility(View.VISIBLE); BookInfoCrawler bic = (BookInfoCrawler) rc; - CommonApi.getBookInfo(mBook, bic, new ResultCallback() { + CommonApi.getBookInfo(mBook, bic).compose(RxUtils::toSimpleSingle).subscribe(new MyObserver() { @Override - public void onFinish(Object o, int code) { + public void onNext(@NotNull Book book) { if (!App.isDestroy(BookDetailedActivity.this)) { mHandler.sendMessage(mHandler.obtainMessage(4)); } } @Override - public void onError(Exception e) { + public void onError(Throwable e) { ToastUtils.showError("书籍详情加载失败!"); + e.printStackTrace(); } }); + } else { initOtherInfo(); //predictBookCount(); @@ -435,51 +452,6 @@ public class BookDetailedActivity extends BaseActivity { mHandler.sendMessage(mHandler.obtainMessage(3)); return; } - /*pbLoading.setVisibility(View.GONE); - CharSequence[] sources = new CharSequence[aBooks.size()]; - int checkedItem = 0; - for (int i = 0; i < sources.length; i++) { - sources[i] = LocalBookSource.fromString(aBooks.get(i).getSource()).text - + "\n" + aBooks.get(i).getNewestChapterTitle(); - if (sources[i].equals(LocalBookSource.fromString(mBook.getSource()).text - + "\n" + aBooks.get(i).getNewestChapterTitle())) { - checkedItem = i; - } - } - final int finalCheckedItem = checkedItem; - AlertDialog dialog = MyAlertDialog.build(this) - .setTitle("切换书源") - .setCancelable(true) - .setSingleChoiceItems(sources, checkedItem, (dialog1, which) -> { - if (finalCheckedItem == which) { - dialog1.dismiss(); - return; - } - Book book = aBooks.get(which); - Book bookTem = new Book(mBook); - bookTem.setChapterUrl(book.getChapterUrl()); - bookTem.setImgUrl(book.getImgUrl()); - bookTem.setType(book.getType()); - bookTem.setDesc(book.getDesc()); - bookTem.setSource(book.getSource()); - if (isCollected) { - mBookService.updateBook(mBook, bookTem); - } - mBook = bookTem; - mHandler.sendMessage(mHandler.obtainMessage(1)); - if (isCollected) { - String tip = null; - if (SysManager.getSetting().isMatchChapter()) { - tip = getString(R.string.change_source_tip1); - } else { - tip = getString(R.string.change_source_tip2); - } - DialogCreator.createTipDialog(this, tip); - } - dialog1.dismiss(); - }).create(); - dialog.show();*/ - } /** @@ -488,40 +460,27 @@ public class BookDetailedActivity extends BaseActivity { private void initChapters(boolean isChangeSource) { if (mChapters.size() == 0 && !"本地书籍".equals(mBook.getType())) { if (isCollected) { - mChapters = (ArrayList) mChapterService.findBookAllChapterByBookId(mBook.getId()); + mChapters = mChapterService.findBookAllChapterByBookId(mBook.getId()); } - CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler, isChangeSource, new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - ArrayList chapters = (ArrayList) o; - mBook.setNewestChapterTitle(chapters.get(chapters.size() - 1).getTitle()); - if (isCollected) { - int noReadNum = chapters.size() - mBook.getChapterTotalNum(); - mBook.setNoReadNum(Math.max(noReadNum, 0)); - mChapterService.updateAllOldChapterData(mChapters, chapters, mBook.getId()); - mBookService.updateEntity(mBook); - if (isChangeSource && SysManager.getSetting().isMatchChapter()) { - if (mBookService.matchHistoryChapterPos(mBook, chapters)) { - ToastUtils.showSuccess("历史阅读章节匹配成功!"); - } else { - ToastUtils.showError("历史阅读章节匹配失败!"); - } + if (chaptersDis != null) chaptersDis.dispose(); + CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler) + .flatMap((Function, ObservableSource>) chapters -> saveChapters(chapters, isChangeSource)).compose(RxUtils::toSimpleSingle) + .subscribe(new MyObserver() { + @Override + public void onSubscribe(Disposable d) { + chaptersDis = d; } - } - mChapters = chapters; - int end = Math.max(0, mChapters.size() - 6); - for (int i = mChapters.size() - 1; i >= end; i--) { - mNewestChapters.add(mChapters.get(i)); - } - App.runOnUiThread(() -> mCatalogAdapter.refreshItems(mNewestChapters)); - } - @Override - public void onError(Exception e) { - e.printStackTrace(); - ToastUtils.showError("最新章节加载失败!"); - } - }); + @Override + public void onNext(@NotNull Boolean aBoolean) { + mCatalogAdapter.refreshItems(mNewestChapters); + } + + @Override + public void onError(Throwable e) { + if (App.isDebug()) e.printStackTrace(); + } + }); } else { int end = Math.max(0, mChapters.size() - 6); for (int i = mChapters.size() - 1; i >= end; i--) { @@ -531,6 +490,32 @@ public class BookDetailedActivity extends BaseActivity { } } + private Observable saveChapters(List chapters, boolean isChangeSource){ + return Observable.create(emitter -> { + mBook.setNewestChapterTitle(chapters.get(chapters.size() - 1).getTitle()); + if (isCollected) { + int noReadNum = chapters.size() - mBook.getChapterTotalNum(); + mBook.setNoReadNum(Math.max(noReadNum, 0)); + mChapterService.updateAllOldChapterData(mChapters, chapters, mBook.getId()); + mBookService.updateEntity(mBook); + if (isChangeSource && SysManager.getSetting().isMatchChapter()) { + if (mBookService.matchHistoryChapterPos(mBook, chapters)) { + ToastUtils.showSuccess("历史阅读章节匹配成功!"); + } else { + ToastUtils.showError("历史阅读章节匹配失败!"); + } + } + } + mChapters = chapters; + int end = Math.max(0, mChapters.size() - 6); + for (int i = mChapters.size() - 1; i >= end; i--) { + mNewestChapters.add(mChapters.get(i)); + } + emitter.onNext(true); + emitter.onComplete(); + }); + } + /** * 前往阅读界面 */ @@ -600,24 +585,6 @@ public class BookDetailedActivity extends BaseActivity { ToastUtils.showWarring("无网络连接!"); return true; } - /*pbLoading.setVisibility(View.VISIBLE); - if (aBooks == null) { - ChangeSourceDialog csd = new ChangeSourceDialog(this, mBook); - csd.init(new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - aBooks = (ArrayList) o; - mHandler.sendMessage(mHandler.obtainMessage(2)); - } - - @Override - public void onError(Exception e) { - mHandler.sendMessage(mHandler.obtainMessage(3)); - } - }); - } else { - createChangeSourceDia(); - }*/ mSourceDialog.show(); break; case R.id.action_share: @@ -895,17 +862,4 @@ public class BookDetailedActivity extends BaseActivity { return lines; } - - private void predictBookCount(){ - if (isCollected) { - BookWordCountPre bwcp = new BookWordCountPre(mBook); - App.getApplication().newThread(() ->{ - if (bwcp.train()){ - int count = bwcp.predict(); - mBook.setWordCount(String.valueOf(count)); - App.runOnUiThread(this::initTagList); - } - }); - } - } } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/BookstoreActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/BookstoreActivity.java index 8298651..94b1833 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/BookstoreActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/BookstoreActivity.java @@ -165,24 +165,6 @@ public class BookstoreActivity extends BaseActivity { mSourceDia.dismiss(); }); mSourceDia.show(); - /*mHandler.sendMessage(mHandler.obtainMessage(3)); - ChangeSourceDialog csd = new ChangeSourceDialog(this, book); - csd.initOneBook(new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - Book searchBook = (Book) o; - book.setChapterUrl(searchBook.getChapterUrl()); - book.setSource(searchBook.getSource()); - goToBookDetail(book); - mHandler.sendMessage(mHandler.obtainMessage(4)); - } - - @Override - public void onError(Exception e) { - DialogCreator.createTipDialog(BookstoreActivity.this, "未搜索到该书籍,无法进入书籍详情!"); - mHandler.sendMessage(mHandler.obtainMessage(4)); - } - });*/ } }); } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java index 0eaefbd..73e595e 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java @@ -39,16 +39,25 @@ import androidx.core.content.ContextCompat; import com.gyf.immersionbar.ImmersionBar; import com.jaredrummler.android.colorpicker.ColorPickerDialogListener; +import org.jetbrains.annotations.NotNull; + import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import io.reactivex.Observable; +import io.reactivex.ObservableEmitter; +import io.reactivex.ObservableOnSubscribe; +import io.reactivex.ObservableSource; +import io.reactivex.Observer; +import io.reactivex.functions.Function; import xyz.fycz.myreader.ActivityManage; import xyz.fycz.myreader.R; import xyz.fycz.myreader.application.App; import xyz.fycz.myreader.application.SysManager; import xyz.fycz.myreader.base.BaseActivity; +import xyz.fycz.myreader.base.observer.MyObserver; import xyz.fycz.myreader.common.APPCONST; import xyz.fycz.myreader.common.URLCONST; import xyz.fycz.myreader.databinding.ActivityReadBinding; @@ -87,6 +96,7 @@ import xyz.fycz.myreader.util.notification.NotificationClickReceiver; import xyz.fycz.myreader.util.notification.NotificationUtil; import xyz.fycz.myreader.util.utils.ColorUtil; import xyz.fycz.myreader.util.utils.NetworkUtils; +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.util.utils.SystemBarUtils; @@ -1001,20 +1011,25 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe }); } else { mPageLoader.setmStatus(PageLoader.STATUS_LOADING_CHAPTER); - CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler, false, new ResultCallback() { + CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler).flatMap(chapters -> Observable.create(emitter -> { + updateAllOldChapterData(chapters); + initChapters(); + emitter.onComplete(); + })).compose(RxUtils::toSimpleSingle).subscribe(new MyObserver() { @Override - public void onFinish(Object o, int code) { + public void onComplete() { mPageLoader.setmStatus(PageLoader.STATUS_LOADING); - ArrayList chapters = (ArrayList) o; - updateAllOldChapterData(chapters); - initChapters(); } @Override - public void onError(Exception e) { -// settingChange = true; - initChapters(); + public void onNext(@NotNull Object o) { + + } + + @Override + public void onError(Throwable e) { mHandler.sendMessage(mHandler.obtainMessage(1)); + if (App.isDebug()) e.printStackTrace(); } }); } @@ -1028,7 +1043,7 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe * * @param newChapters */ - private void updateAllOldChapterData(ArrayList newChapters) { + private void updateAllOldChapterData(List newChapters) { for (Chapter newChapter : newChapters) { newChapter.setId(StringHelper.getStringRandom(25)); newChapter.setBookId(mBook.getId()); @@ -1576,19 +1591,21 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe if (StringHelper.isEmpty(chapter.getBookId())) { chapter.setId(mBook.getId()); } - CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler, new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - downloadingChapter = chapter.getTitle(); - mChapterService.saveOrUpdateChapter(chapter, (String) o); - curCacheChapterNum++; - } + CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler) + .subscribe(new MyObserver() { + @Override + public void onNext(@NotNull String s) { + downloadingChapter = chapter.getTitle(); + mChapterService.saveOrUpdateChapter(chapter, s); + curCacheChapterNum++; + } - @Override - public void onError(Exception e) { - curCacheChapterNum++; - } - }); + @Override + public void onError(Throwable e) { + curCacheChapterNum++; + if (App.isDebug()) e.printStackTrace(); + } + }); try { Thread.sleep(downloadInterval); } catch (InterruptedException e) { diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java index f1f7573..8563ca5 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java @@ -9,31 +9,31 @@ import android.os.Handler; import android.os.Message; import android.text.Editable; import android.text.TextWatcher; -import android.view.Gravity; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; -import android.widget.PopupMenu; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.Toolbar; import androidx.recyclerview.widget.LinearLayoutManager; +import org.jetbrains.annotations.NotNull; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; -import me.gujun.android.taggroup.TagGroup; +import io.reactivex.Single; +import io.reactivex.SingleOnSubscribe; import xyz.fycz.myreader.R; import xyz.fycz.myreader.application.App; import xyz.fycz.myreader.application.SysManager; @@ -46,10 +46,10 @@ import xyz.fycz.myreader.entity.Setting; import xyz.fycz.myreader.greendao.entity.Book; import xyz.fycz.myreader.greendao.entity.SearchHistory; import xyz.fycz.myreader.greendao.entity.rule.BookSource; -import xyz.fycz.myreader.model.source.BookSourceManager; import xyz.fycz.myreader.greendao.service.SearchHistoryService; import xyz.fycz.myreader.model.SearchEngine; import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; +import xyz.fycz.myreader.model.source.BookSourceManager; import xyz.fycz.myreader.ui.adapter.SearchBookAdapter; import xyz.fycz.myreader.ui.adapter.SearchHistoryAdapter; import xyz.fycz.myreader.ui.dialog.DialogCreator; @@ -57,9 +57,8 @@ import xyz.fycz.myreader.ui.dialog.MultiChoiceDialog; import xyz.fycz.myreader.util.SharedPreUtils; import xyz.fycz.myreader.util.StringHelper; import xyz.fycz.myreader.util.ToastUtils; -import xyz.fycz.myreader.webapi.BaseApi; -import xyz.fycz.myreader.webapi.CommonApi; -import xyz.fycz.myreader.webapi.callback.ResultCallback; +import xyz.fycz.myreader.util.utils.OkHttpUtils; +import xyz.fycz.myreader.util.utils.RxUtils; import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil; import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; @@ -138,7 +137,7 @@ public class SearchBookActivity extends BaseActivity { super.initData(savedInstanceState); mSetting = SysManager.getSetting(); mSearchHistoryService = SearchHistoryService.getInstance(); - showHot = !App.isApkInDebug(this); + showHot = !App.isDebug(); searchEngine = new SearchEngine(); searchEngine.setOnSearchListener(new SearchEngine.OnSearchListener() { @Override @@ -309,9 +308,9 @@ public class SearchBookActivity extends BaseActivity { if (item.getItemId() == R.id.action_hot) { showHot = !showHot; initSuggestionList(); - }else if (item.getItemId() == R.id.action_disable_source) { + } else if (item.getItemId() == R.id.action_disable_source) { showDisableSourceDia(); - }else if (item.getItemId() == R.id.action_source_man) { + } else if (item.getItemId() == R.id.action_source_man) { startActivityForResult(new Intent(this, BookSourceActivity.class), APPCONST.REQUEST_BOOK_SOURCE); } else { @@ -383,7 +382,7 @@ public class SearchBookActivity extends BaseActivity { .subscribe(new MySingleObserver() { @Override public void onSuccess(@NonNull Boolean aBoolean) { - if (aBoolean){ + if (aBoolean) { ToastUtils.showSuccess("保存成功"); } } @@ -411,27 +410,31 @@ public class SearchBookActivity extends BaseActivity { binding.tgSuggestBook.setTags(suggestion); } else { SharedPreUtils spu = SharedPreUtils.getInstance(); - String cookie = spu.getString(getString(R.string.qdCookie), ""); - String url = "https://m.qidian.com/majax/search/auto?kw=&"; - if (cookie.equals("")) { - url += "_csrfToken=eXRDlZxmRDLvFAmdgzqvwWAASrxxp2WkVlH4ZM7e"; - } else { + Single.create((SingleOnSubscribe) emitter -> { + String cookie = spu.getString(getString(R.string.qdCookie), ""); + String url = "https://m.qidian.com/majax/search/auto?kw=&"; + if (cookie.equals("")) { + cookie = "_csrfToken=eXRDlZxmRDLvFAmdgzqvwWAASrxxp2WkVlH4ZM7e; newstatisticUUID=1595991935_2026387981"; + } url += cookie.split(";")[0]; - } - BaseApi.getCommonReturnHtmlStringApi(url, null, "utf-8", true, new ResultCallback() { + Map headers = new HashMap<>(); + headers.put("Cookie", cookie); + emitter.onSuccess(OkHttpUtils.getHtml(url, null, + "utf-8", headers)); + }).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver() { @Override - public void onFinish(Object o, int code) { - parseSuggestionList((String) o); + public void onSuccess(@NotNull String s) { + parseSuggestionList(s); if (mSuggestions.size() > 0) { - App.runOnUiThread(() -> binding.tgSuggestBook.setTags(mSuggestions.subList(0, 5))); + binding.tgSuggestBook.setTags(mSuggestions.subList(0, 5)); } else { - App.runOnUiThread(() -> binding.llSuggestBooksView.setVisibility(View.GONE)); + binding.llSuggestBooksView.setVisibility(View.GONE); } } @Override - public void onError(Exception e) { - App.runOnUiThread(() -> binding.llSuggestBooksView.setVisibility(View.GONE)); + public void onError(Throwable e) { + binding.llSuggestBooksView.setVisibility(View.GONE); } }); } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/adapter/ChapterTitleAdapter.java b/app/src/main/java/xyz/fycz/myreader/ui/adapter/ChapterTitleAdapter.java index c07512e..3b22341 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/adapter/ChapterTitleAdapter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/adapter/ChapterTitleAdapter.java @@ -32,7 +32,7 @@ public class ChapterTitleAdapter extends ArrayAdapter { private List mList; private Book mBook; - public ChapterTitleAdapter(Context context, int resourceId, ArrayList datas, Book book) { + public ChapterTitleAdapter(Context context, int resourceId, List datas, Book book) { super(context, resourceId, new ArrayList<>(datas)); mResourceId = resourceId; setting = SysManager.getSetting(); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/dialog/ChangeSourceDialog.java b/app/src/main/java/xyz/fycz/myreader/ui/dialog/ChangeSourceDialog.java deleted file mode 100644 index 6fb452d..0000000 --- a/app/src/main/java/xyz/fycz/myreader/ui/dialog/ChangeSourceDialog.java +++ /dev/null @@ -1,186 +0,0 @@ -package xyz.fycz.myreader.ui.dialog; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.Handler; -import android.os.Message; - -import xyz.fycz.myreader.application.App; -import xyz.fycz.myreader.webapi.callback.ResultCallback; -import xyz.fycz.myreader.util.ToastUtils; -import xyz.fycz.myreader.webapi.crawler.*; -import xyz.fycz.myreader.entity.SearchBookBean; -import xyz.fycz.myreader.greendao.entity.Book; -import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap; -import xyz.fycz.myreader.webapi.CommonApi; -import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler; -import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; - -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; - - -/** - * @author fengyue - * @date 2020/5/19 17:35 - */ -public class ChangeSourceDialog { - private Context context; - private Book mBook; - private ConcurrentMultiValueMap mBooks = new ConcurrentMultiValueMap<>(); - private ArrayList aBooks; - private SearchBookBean sbb; - private boolean isSearchSuccess; - private int threadCount; - private ResultCallback rc; - private boolean isGetOneBook = false; - private Book oneBook; - @SuppressLint("HandlerLeak") - private Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case 1: - if (isGetOneBook){ - rc.onFinish(oneBook, 0); - }else { - if (threadCount == 0) { - createaBooks(); - } - } - break; - case 2: - if (!isSearchSuccess && threadCount == 0) { - if (!isGetOneBook|| oneBook == null) { - rc.onError(new Exception()); - } - }else if (threadCount == 0){ - if (isGetOneBook){ - rc.onFinish(oneBook, 0); - }else { - createaBooks(); - } - } - break; - } - } - }; - - public ChangeSourceDialog(Context context, Book mBook) { - this.context = context; - this.mBook = mBook; - sbb = new SearchBookBean(mBook.getName(), mBook.getAuthor()); - } - - public void init(ResultCallback rc) { - this.rc = rc; - getData(); - } - - public void initOneBook(ResultCallback rc){ - this.rc = rc; - getData(); - isGetOneBook = true; - } - - /** - * 获取搜索数据 - */ - private void getData() { - mBooks.clear(); - ArrayList readCrawlers = ReadCrawlerUtil.getReadCrawlers(); - threadCount = readCrawlers.size(); - if (threadCount == 0){ - if (isGetOneBook){ - ToastUtils.showWarring("当前书源已全部禁用,无法搜索!"); - }else { - ToastUtils.showWarring("当前书源已全部禁用,无法换源!"); - } - rc.onError(new Exception()); - return; - } - isSearchSuccess = false; - for (ReadCrawler readCrawler : readCrawlers){ - searchBookByCrawler(readCrawler, readCrawler.getSearchCharset()); - } - } - - private void searchBookByCrawler(final ReadCrawler rc, String charset) { - String searchKey = mBook.getName(); - if (charset.toLowerCase().equals("gbk")) { - try { - searchKey = URLEncoder.encode(mBook.getName(), charset); - } catch (UnsupportedEncodingException e) { - e.printStackTrace(); - } - } - CommonApi.search(searchKey, rc, new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - final ConcurrentMultiValueMap cmvm = - (ConcurrentMultiValueMap) o; - if (isGetOneBook){ - if (oneBook != null){ - return; - } - oneBook = cmvm.getValue(sbb, 0); - threadCount--; - mHandler.sendMessage(mHandler.obtainMessage(1)); - App.getApplication().shutdownThreadPool(); - return; - } - if (rc instanceof BookInfoCrawler) { - BookInfoCrawler bic = (BookInfoCrawler) rc; - final List aBooks = cmvm.getValues(sbb); - if (aBooks != null) { - for (int i = 0; i < aBooks.size(); i++) { - Book book = aBooks.get(i); - final int finalI = i; - CommonApi.getBookInfo(book, bic, new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - if (finalI == aBooks.size() - 1) { - mBooks.addAll(cmvm); - isSearchSuccess = true; - threadCount--; - mHandler.sendMessage(mHandler.obtainMessage(1)); - } - } - - @Override - public void onError(Exception e) { - threadCount--; - mHandler.sendMessage(mHandler.obtainMessage(2)); - } - }); - - } - } else { - isSearchSuccess = true; - threadCount--; - mHandler.sendMessage(mHandler.obtainMessage(1)); - } - } else { - mBooks.addAll(cmvm); - isSearchSuccess = true; - threadCount--; - mHandler.sendMessage(mHandler.obtainMessage(1)); - } - } - - @Override - public void onError(Exception e) { - threadCount--; - mHandler.sendMessage(mHandler.obtainMessage(2)); - } - }); - } - - private void createaBooks() { - aBooks = (ArrayList) mBooks.getValues(sbb); - rc.onFinish(aBooks, 1); - } - -} diff --git a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java index b1133c7..ef8ae9b 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java @@ -17,6 +17,8 @@ import android.widget.PopupMenu; import androidx.core.app.ActivityCompat; +import org.jetbrains.annotations.NotNull; + import java.text.Collator; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -26,11 +28,14 @@ import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import io.reactivex.Observable; import xyz.fycz.myreader.R; import xyz.fycz.myreader.application.App; import xyz.fycz.myreader.application.SysManager; import xyz.fycz.myreader.base.BasePresenter; +import xyz.fycz.myreader.base.observer.MyObserver; import xyz.fycz.myreader.ui.dialog.BookGroupDialog; +import xyz.fycz.myreader.util.utils.RxUtils; import xyz.fycz.myreader.webapi.callback.ResultCallback; import xyz.fycz.myreader.common.APPCONST; import xyz.fycz.myreader.ui.dialog.MyAlertDialog; @@ -125,7 +130,7 @@ public class BookcasePresenter implements BasePresenter { break; case 4: showErrorLoadingBooks(); - if (App.isApkInDebug(mMainActivity)) { + if (App.isDebug()) { if (isFirstRefresh) { initBook(); isFirstRefresh = false; @@ -234,10 +239,10 @@ public class BookcasePresenter implements BasePresenter { init(); mBookcaseAdapter.setCheckedAll(false); }, null); - }else { + } else { DialogCreator.createCommonDialog(mMainActivity, "批量删除/移除书籍", "您是希望是要删除这些书籍及其所有缓存还是从分组中移除(不会删除书籍)呢?", true, - "删除书籍", "从分组中移除" ,(dialog, which) -> { + "删除书籍", "从分组中移除", (dialog, which) -> { for (Book book : mBookcaseAdapter.getSelectBooks()) { mBookService.deleteBook(book); } @@ -258,7 +263,7 @@ public class BookcasePresenter implements BasePresenter { //加入分组监听器 mBookcaseFragment.getmBtnAddGroup().setOnClickListener(v -> { - mBookGroupDia.addGroup(mBookcaseAdapter.getSelectBooks(), new BookGroupDialog.OnGroup(){ + mBookGroupDia.addGroup(mBookcaseAdapter.getSelectBooks(), new BookGroupDialog.OnGroup() { @Override public void change() { init(); @@ -312,9 +317,9 @@ public class BookcasePresenter implements BasePresenter { mBookcaseFragment.getGvBook().setOnDragSelectListener(new DragSortGridView.OnDragSelectListener() { @Override public void onDragSelect(View mirror) { - if (mSetting.getBookcaseStyle() == BookcaseStyle.listMode){ + if (mSetting.getBookcaseStyle() == BookcaseStyle.listMode) { mirror.setBackgroundColor(mMainActivity.getResources().getColor(R.color.colorBackground)); - }else { + } else { mirror.setScaleX(1.05f); } mirror.setScaleY(1.05f); @@ -354,14 +359,14 @@ public class BookcasePresenter implements BasePresenter { if (mSetting.getSortStyle() == 1) { Collections.sort(mBooks, (o1, o2) -> { - if (o1.getLastReadTime() < o2.getLastReadTime()){ + if (o1.getLastReadTime() < o2.getLastReadTime()) { return 1; - }else if (o1.getLastReadTime() > o2.getLastReadTime()){ + } else if (o1.getLastReadTime() > o2.getLastReadTime()) { return -1; } return 0; }); - }else if (mSetting.getSortStyle() == 2){ + } else if (mSetting.getSortStyle() == 2) { Collections.sort(mBooks, (o1, o2) -> { Collator cmp = Collator.getInstance(java.util.Locale.CHINA); return cmp.compare(o1.getName(), o2.getName()); @@ -373,7 +378,7 @@ public class BookcasePresenter implements BasePresenter { if (sort != i + 1) { if (!isGroup) { mBooks.get(i).setSortCode(i + 1); - }else { + } else { mBooks.get(i).setGroupSort(i + 1); } mBookService.updateEntity(mBooks.get(i)); @@ -400,24 +405,31 @@ public class BookcasePresenter implements BasePresenter { Thread update = new Thread(() -> { final ArrayList mChapters = (ArrayList) mChapterService.findBookAllChapterByBookId(book.getId()); final ReadCrawler mReadCrawler = ReadCrawlerUtil.getReadCrawler(book.getSource()); - CommonApi.getBookChapters(book.getChapterUrl(), mReadCrawler, true, new ResultCallback() { + CommonApi.getBookChapters(book.getChapterUrl(), mReadCrawler).flatMap(chapters -> Observable.create(emitter -> { + int noReadNum = chapters.size() - book.getChapterTotalNum(); + book.setNoReadNum(Math.max(noReadNum, 0)); + book.setNewestChapterTitle(chapters.get(chapters.size() - 1).getTitle()); + mBookcaseAdapter.getIsLoading().put(book.getId(), false); + mChapterService.updateAllOldChapterData(mChapters, chapters, book.getId()); + mBookService.updateEntity(book); + emitter.onComplete(); + })).compose(RxUtils::toSimpleSingle).subscribe(new MyObserver() { @Override - public void onFinish(Object o, int code) { - ArrayList chapters = (ArrayList) o; - int noReadNum = chapters.size() - book.getChapterTotalNum(); - book.setNoReadNum(Math.max(noReadNum, 0)); - book.setNewestChapterTitle(chapters.get(chapters.size() - 1).getTitle()); - mBookcaseAdapter.getIsLoading().put(book.getId(), false); - mChapterService.updateAllOldChapterData(mChapters, chapters, book.getId()); + public void onNext(@NotNull Object o) { + + } + + @Override + public void onComplete() { mHandler.sendMessage(mHandler.obtainMessage(1)); - mBookService.updateEntity(book); } @Override - public void onError(Exception e) { + public void onError(Throwable e) { mBookcaseAdapter.getIsLoading().put(book.getId(), false); errorLoadingBooks.add(book); mHandler.sendMessage(mHandler.obtainMessage(1)); + if (App.isDebug()) e.printStackTrace(); } }); }); @@ -509,7 +521,6 @@ public class BookcasePresenter implements BasePresenter { /** * 显示书籍分组菜单 - * */ public void showBookGroupMenu(View view) { mBookGroupDia.initBookGroups(false); @@ -570,7 +581,7 @@ public class BookcasePresenter implements BasePresenter { } } - public boolean canEditBookcase(){ + public boolean canEditBookcase() { return mBooks.size() > 0; } @@ -583,55 +594,55 @@ public class BookcasePresenter implements BasePresenter { .setItems(mMainActivity.getResources().getStringArray(R.array.group_man) , (dialog, which) -> { mBookGroupDia.initBookGroups(false); - switch (which){ - case 0: - mBookGroupDia.showAddOrRenameGroupDia(false, false, 0, new BookGroupDialog.OnGroup() { - @Override - public void change() { - ogcl.onChange(); - } - - @Override - public void addGroup() { - mBookcaseFragment.getmBtnAddGroup().performClick(); - } - }); - break; - case 1: - mBookGroupDia.showSelectGroupDia((dialog1, which1) -> { - mBookGroupDia.showAddOrRenameGroupDia(true,false, which1, new BookGroupDialog.OnGroup() { - @Override - public void change() { - ogcl.onChange(); - } - - @Override - public void addGroup() { - mBookcaseFragment.getmBtnAddGroup().performClick(); - } - }); - }); - break; - case 2: - mBookGroupDia.showDeleteGroupDia(new BookGroupDialog.OnGroup() { - @Override - public void change() { - ogcl.onChange(); - init(); - } - }); - break; - } - }).show(); + switch (which) { + case 0: + mBookGroupDia.showAddOrRenameGroupDia(false, false, 0, new BookGroupDialog.OnGroup() { + @Override + public void change() { + ogcl.onChange(); + } + + @Override + public void addGroup() { + mBookcaseFragment.getmBtnAddGroup().performClick(); + } + }); + break; + case 1: + mBookGroupDia.showSelectGroupDia((dialog1, which1) -> { + mBookGroupDia.showAddOrRenameGroupDia(true, false, which1, new BookGroupDialog.OnGroup() { + @Override + public void change() { + ogcl.onChange(); + } + + @Override + public void addGroup() { + mBookcaseFragment.getmBtnAddGroup().performClick(); + } + }); + }); + break; + case 2: + mBookGroupDia.showDeleteGroupDia(new BookGroupDialog.OnGroup() { + @Override + public void change() { + ogcl.onChange(); + init(); + } + }); + break; + } + }).show(); } //分组切换监听器 - public void addOnGroupChangeListener(MainActivity.OnGroupChangeListener ogcl){ + public void addOnGroupChangeListener(MainActivity.OnGroupChangeListener ogcl) { this.ogcl = ogcl; } //是否有分组切换监听器 - public boolean hasOnGroupChangeListener(){ + public boolean hasOnGroupChangeListener() { return this.ogcl != null; } @@ -768,19 +779,26 @@ public class BookcasePresenter implements BasePresenter { } mHandler.postDelayed(sendDownloadNotification, 2 * downloadInterval); for (Chapter chapter : needDownloadChapters) { - getChapterContent(book, chapter, new ResultCallback() { + if (StringHelper.isEmpty(chapter.getBookId())) { + chapter.setBookId(book.getId()); + } + ReadCrawler mReadCrawler = ReadCrawlerUtil.getReadCrawler(book.getSource()); + CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler).flatMap(s -> Observable.create(emitter -> { + downloadingChapter = chapter.getTitle(); + mChapterService.saveOrUpdateChapter(chapter, s); + successCathe++; + curCacheChapterNum++; + })).subscribe(new MyObserver() { @Override - public void onFinish(Object o, int code) { - downloadingChapter = chapter.getTitle(); - mChapterService.saveOrUpdateChapter(chapter, (String) o); - successCathe++; - curCacheChapterNum++; + public void onNext(@NotNull Object o) { + } @Override - public void onError(Exception e) { + public void onError(Throwable e) { curCacheChapterNum++; errorCathe++; + if (App.isDebug()) e.printStackTrace(); } }); try { @@ -806,19 +824,6 @@ public class BookcasePresenter implements BasePresenter { } - /** - * 获取章节内容 - * - * @param - * @param - */ - private void getChapterContent(Book mBook, final Chapter chapter, final ResultCallback resultCallback) { - if (StringHelper.isEmpty(chapter.getBookId())) { - chapter.setBookId(mBook.getId()); - } - ReadCrawler mReadCrawler = ReadCrawlerUtil.getReadCrawler(mBook.getSource()); - CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler, resultCallback); - } /** * 发送通知 @@ -892,28 +897,28 @@ public class BookcasePresenter implements BasePresenter { 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, "同步失败,请重试!"); - } + 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, "成功将书架同步至网络!"); } - } - - @Override - public void onError(Exception e) { + } else { if (!isAutoSyn) { - DialogCreator.createTipDialog(mMainActivity, "同步失败,请重试!\n" + e.getLocalizedMessage()); + DialogCreator.createTipDialog(mMainActivity, "同步失败,请重试!"); } } - }); + } + + @Override + public void onError(Exception e) { + if (!isAutoSyn) { + DialogCreator.createTipDialog(mMainActivity, "同步失败,请重试!\n" + e.getLocalizedMessage()); + } + } + }); } } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/presenter/CatalogPresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/presenter/CatalogPresenter.java index cf24c6c..2438a75 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/presenter/CatalogPresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/presenter/CatalogPresenter.java @@ -3,9 +3,14 @@ package xyz.fycz.myreader.ui.presenter; import android.app.Activity; import android.content.Intent; import android.view.View; + +import org.jetbrains.annotations.NotNull; + import xyz.fycz.myreader.R; import xyz.fycz.myreader.application.App; import xyz.fycz.myreader.base.BasePresenter; +import xyz.fycz.myreader.base.observer.MyObserver; +import xyz.fycz.myreader.util.utils.RxUtils; import xyz.fycz.myreader.webapi.callback.ResultCallback; import xyz.fycz.myreader.common.APPCONST; import xyz.fycz.myreader.ui.activity.CatalogActivity; @@ -20,6 +25,7 @@ import xyz.fycz.myreader.webapi.CommonApi; import java.util.ArrayList; import java.util.Collections; +import java.util.List; /** * @author fengyue @@ -29,8 +35,8 @@ public class CatalogPresenter implements BasePresenter { private static final String TAG = CatalogPresenter.class.getSimpleName(); private CatalogFragment mCatalogFragment; private ChapterService mChapterService; - private ArrayList mChapters = new ArrayList<>(); - private ArrayList mConvertChapters = new ArrayList<>(); + private List mChapters = new ArrayList<>(); + private List mConvertChapters = new ArrayList<>(); private int curSortflag = 0; //0正序 1倒序 private ChapterTitleAdapter mChapterTitleAdapter; private Book mBook; @@ -62,26 +68,23 @@ public class CatalogPresenter implements BasePresenter { return; } mCatalogFragment.getPbLoading().setVisibility(View.VISIBLE); - CommonApi.getBookChapters(mBook.getChapterUrl(), ReadCrawlerUtil.getReadCrawler(mBook.getSource()),false, - new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - mChapters = (ArrayList) o; - - App.runOnUiThread(() -> { - mCatalogFragment.getPbLoading().setVisibility(View.GONE); - initChapterTitleList(); - }); - - } + CommonApi.getBookChapters(mBook.getChapterUrl(), ReadCrawlerUtil.getReadCrawler(mBook.getSource())) + .compose(RxUtils::toSimpleSingle).subscribe(new MyObserver>() { + @Override + public void onNext(@NotNull List chapters) { + mChapters = chapters; + mCatalogFragment.getPbLoading().setVisibility(View.GONE); + initChapterTitleList(); + } - @Override - public void onError(Exception e) { - e.printStackTrace(); - ToastUtils.showError("章节目录加载失败!\n" + e.getLocalizedMessage()); - App.runOnUiThread(() -> mCatalogFragment.getPbLoading().setVisibility(View.GONE)); - } - }); + @Override + public void onError(Throwable e) { + e.printStackTrace(); + ToastUtils.showError("章节目录加载失败!\n" + e.getLocalizedMessage()); + mCatalogFragment.getPbLoading().setVisibility(View.GONE); + if (App.isDebug()) e.printStackTrace(); + } + }); } mCatalogFragment.getLvChapterList().setOnItemClickListener((adapterView, view, i, l) -> { Chapter chapter = mChapterTitleAdapter.getItem(i); diff --git a/app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java b/app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java index ce4850f..a5c9550 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java +++ b/app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java @@ -9,7 +9,6 @@ import com.google.gson.Gson; import okhttp3.*; import xyz.fycz.myreader.R; import xyz.fycz.myreader.application.App; -import xyz.fycz.myreader.application.TrustAllCerts; import xyz.fycz.myreader.webapi.callback.HttpCallback; import xyz.fycz.myreader.webapi.callback.JsonCallback; import xyz.fycz.myreader.webapi.callback.URLConnectionCallback; @@ -43,7 +42,7 @@ public class HttpUtil { //因为每个OkHttpClient 实例都有自己的连接池和线程池,重用这个实例能降低延时,减少内存消耗,而重复创建新实例则会浪费资源。 private static OkHttpClient mClient; - private static SSLSocketFactory createSSLSocketFactory() { + public static SSLSocketFactory createSSLSocketFactory() { SSLSocketFactory ssfFactory = null; try { SSLContext sc = SSLContext.getInstance("TLS"); @@ -54,6 +53,16 @@ public class HttpUtil { return ssfFactory; } + static class TrustAllCerts implements X509TrustManager { + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) {} + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) {} + + @Override + public X509Certificate[] getAcceptedIssuers() {return new X509Certificate[0];} + } public static X509TrustManager createTrustAllManager() { X509TrustManager tm = null; @@ -248,62 +257,8 @@ public class HttpUtil { public static void sendGetRequest_okHttp(final String address, boolean isRefresh, final HttpCallback callback) { App.getApplication().newThread(() -> { - /* HttpURLConnection connection = null; - try { - URL url = new URL(address); - connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); - connection.setRequestProperty("Content-type", "text/html"); - connection.setRequestProperty("Accept-Charset", "gbk"); - connection.setRequestProperty("contentType", "gbk"); - connection.setConnectTimeout(5 * 1000); - connection.setReadTimeout(5 * 1000); - connection.setDoInput(true); - connection.setDoOutput(true); - if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { - Log.e("Http", "网络错误异常!!!!"); - } - InputStream in = connection.getInputStream(); - Log.d("Http", "connection success"); - if (callback != null) { - callback.onFinish(in); - } - } catch (Exception e) { - e.printStackTrace(); - Log.e("Http", e.toString()); - if (callback != null) { - callback.onError(e); - } - } finally { - if (connection != null) { - connection.disconnect(); - } - }*/ try { OkHttpClient client = getOkHttpClient(); - /*HashMap> cookieStore = new HashMap<>(); - OkHttpClient client = new OkHttpClient.Builder() - .cookieJar(new CookieJar() { - @Override - public void saveFromResponse(HttpUrl httpUrl, List list) { - StringBuilder sb = new StringBuilder(); - for (Cookie cookie : list){ - sb.append(cookie.name()).append("=").append(cookie.value()).append("; "); - } - if (list.size() > 0){ - sb.deleteCharAt(sb.lastIndexOf("; ")); - } - Log.i("CookieStr", sb.toString()); - cookieStore.put(httpUrl.host(), list); - } - - @Override - public List loadForRequest(HttpUrl httpUrl) { - List cookies = cookieStore.get(httpUrl.host()); - return cookies != null ? cookies : new ArrayList<>(); - } - }) - .build();*/ Request.Builder requestBuilder = new Request.Builder() .addHeader("Accept", "*/*") .addHeader("Connection", "Keep-Alive") @@ -422,92 +377,6 @@ public class HttpUtil { }).start(); } - public static void sendPostRequest_okHttp(final String address, final String output, final HttpCallback callback) { - new Thread(new Runnable() { - HttpURLConnection connection = null; - - @Override - public void run() { - try { - MediaType contentType = MediaType.parse("charset=utf-8"); - OkHttpClient client = new OkHttpClient(); - RequestBody body = RequestBody.create(contentType, output); - Request request = new Request.Builder() - .url(address) - .post(body) - .build(); - Response response = client.newCall(request).execute(); - callback.onFinish(response.body().byteStream()); - } catch (Exception e) { - if (callback != null) { - callback.onError(e); - } - } finally { - if (connection != null) { - connection.disconnect(); - } - } - } - }).start(); - } - - /** - * 多文件上传请求 - * - * @param url - * @param files - * @param params - * @param callback - */ - public static void uploadFile(String url, ArrayList files, Map params, final HttpCallback callback) { - OkHttpClient client = new OkHttpClient(); - // form 表单形式上传 - MultipartBody.Builder requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM); - for (File file : files) { - if (file != null) { - // MediaType.parse() 里面是上传的文件类型。 - RequestBody body = RequestBody.create(MediaType.parse("*/*"), file); - String filename = file.getName(); - // 参数分别为, 请求key ,文件名称 , RequestBody - requestBody.addFormDataPart(file.getName(), file.getName(), body); - } - } - - if (params != null) { - // map 里面是请求中所需要的 key 和 value - for (Map.Entry entry : params.entrySet()) { - requestBody.addFormDataPart(valueOf(entry.getKey()), valueOf(entry.getValue())); - } - } - Request request = new Request.Builder().url(url).post(requestBody.build()).tag(App.getApplication()).build(); - // readTimeout("请求超时时间" , 时间单位); - client.newBuilder().readTimeout(5000, TimeUnit.MILLISECONDS).build().newCall(request).enqueue(new Callback() { - @Override - public void onFailure(Call call, IOException e) { - callback.onError(e); - Log.i("Http", "onFailure"); - } - - @Override - public void onResponse(Call call, Response response) throws IOException { - if (response.isSuccessful()) { - String str = response.body().string(); - callback.onFinish(str); - Log.i("Http", response.message() + " , body " + str); - } else { - JsonModel jsonModel = new JsonModel(); - jsonModel.setSuccess(false); -// jsonModel.setResult(response.body().string()); - callback.onFinish(new Gson().toJson(jsonModel)); - Log.i("Http", response.message() + " error : body " + response.body().string()); - } - } - - }); - - } - - /** * 生成URL * @@ -550,35 +419,6 @@ public class HttpUtil { return url.toString().replace("?&", "?"); } - /** - * 生成URL(不加密) - * - * @param p_url - * @param params - * @return - */ - public static String makeURLNoRSA(String p_url, Map params) { - if (params == null) return ""; - StringBuilder url = new StringBuilder(p_url); - Log.d("http", p_url); - if (url.indexOf("?") < 0) - url.append('?'); - for (String name : params.keySet()) { - Log.d("http", name + "=" + params.get(name)); - url.append('&'); - url.append(name); - url.append('='); - url.append(valueOf(params.get(name))); - //不做URLEncoder处理 -// try { -// url.append(URLEncoder.encode(String.valueOf(params.get(name)), UTF_8)); -// } catch (UnsupportedEncodingException e) { -// // TODO Auto-generated catch block -// e.printStackTrace(); -// } - } - return url.toString().replace("?&", "?"); - } /** * 生成post输出参数串 @@ -622,189 +462,6 @@ public class HttpUtil { return output.toString(); } - public static void uploadFileRequest(final String actionUrl, - final String[] filePaths, - final Map params, - final JsonCallback callback) { - - new Thread(new Runnable() { - @Override - public void run() { - uploadFile(actionUrl, params, filePaths, callback); - } - - }).start(); - - } - - - /** - * 测试URL可连接性 - * - * @param url - * @param connectionCallback - */ - public static void isURLConnection(String url, final URLConnectionCallback connectionCallback) { - sendTestGetRequest(url, new HttpCallback() { - @Override - public void onFinish(String response) { - connectionCallback.onFinish(true); - } - - @Override - public void onFinish(InputStream in) { - connectionCallback.onFinish(true); - } - - @Override - public void onFinish(Bitmap bm) { - connectionCallback.onFinish(true); - } - - @Override - public void onError(Exception e) { - connectionCallback.onFinish(false); - - } - }); - } - - - /** - * 多文件上传的方法 - * - * @param actionUrl:上传的路径 - * @param uploadFilePaths:需要上传的文件路径,数组 - * @return - */ - private static void uploadFile(String actionUrl, Map params, String[] uploadFilePaths, JsonCallback callback) { - String end = "\r\n"; - String twoHyphens = "--"; - String boundary = "*****"; - String CHARSET = "utf-8"; //设置编码 - DataOutputStream ds = null; - InputStream inputStream = null; - InputStreamReader inputStreamReader = null; - BufferedReader reader = null; - StringBuffer resultBuffer = new StringBuffer(); - String tempLine = null; - try { - Log.i("http", "开始上传文件"); - // 统一资源 - URL url = new URL(makeURL(actionUrl, params)); - // 连接类的父类,抽象类 - URLConnection urlConnection = url.openConnection(); - urlConnection.setConnectTimeout(60000); - urlConnection.setReadTimeout(60000); - // http的连接类 - HttpURLConnection httpURLConnection = (HttpURLConnection) urlConnection; - // 设置是否从httpUrlConnection读入,默认情况下是true; - httpURLConnection.setDoInput(true); - // 设置是否向httpUrlConnection输出 - httpURLConnection.setDoOutput(true); - // Post 请求不能使用缓存 - httpURLConnection.setUseCaches(false); - // 设定请求的方法,默认是GET - httpURLConnection.setRequestMethod("POST"); - // 设置字符编码连接参数 - httpURLConnection.setRequestProperty("Connection", "Keep-Alive"); - // 设置字符编码 - httpURLConnection.setRequestProperty("Charset", "UTF-8"); - // 设置请求内容类型 - httpURLConnection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary); - // 设置DataOutputStream - ds = new DataOutputStream(httpURLConnection.getOutputStream()); - for (int i = 0; i < uploadFilePaths.length; i++) { - String uploadFile = uploadFilePaths[i]; - String filename = uploadFile.substring(uploadFile.lastIndexOf("/") + 1); - //设置参数 - StringBuffer sb = new StringBuffer(); - sb.append(end); - sb.append(twoHyphens + boundary + end); - sb.append("Content-Disposition: form-data; " + "name=\"file" + i + "\";filename=\"" + filename + "\"" + end); - sb.append("Content-Type: application/octet-stream; charset=" + CHARSET + end); - sb.append(end); - Log.i("http", "参数:" + sb.toString()); - //写入文件数据 - ds.write(sb.toString().getBytes()); - FileInputStream fStream = new FileInputStream(uploadFile); - int bufferSize = 1024; - byte[] buffer = new byte[bufferSize]; - int length = -1; - int total = 0; - while ((length = fStream.read(buffer)) != -1) { - ds.write(buffer, 0, length); - total += length; - } - Log.i("http", "文件的大小:" + total); - ds.writeBytes(end); - /* close streams */ - fStream.close(); - } - ds.writeBytes(twoHyphens + boundary + twoHyphens + end); - /* close streams */ - ds.flush(); - if (httpURLConnection.getResponseCode() >= 300) { - callback.onError(new Exception( - "HTTP Request is not success, Response code is " + httpURLConnection.getResponseCode())); -// throw new Exception( -// "HTTP Request is not success, Response code is " + httpURLConnection.getResponseCode()); - } - if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) { - inputStream = httpURLConnection.getInputStream(); - inputStreamReader = new InputStreamReader(inputStream); - reader = new BufferedReader(inputStreamReader); - tempLine = null; - resultBuffer = new StringBuffer(); - while ((tempLine = reader.readLine()) != null) { - resultBuffer.append(tempLine); - resultBuffer.append("\n"); - } - Log.i("http", resultBuffer.toString()); - if (callback != null) { - Gson gson = new Gson(); - JsonModel jsonModel = gson.fromJson(resultBuffer.toString(), JsonModel.class); - if (URLCONST.isRSA && !StringHelper.isEmpty(jsonModel.getResult())) { - jsonModel.setResult(StringHelper.decode(new String(RSAUtilV2.decryptByPrivateKey(Base64.decode(jsonModel.getResult().replace("\n", ""), Base64.DEFAULT), APPCONST.privateKey)))); - } - callback.onFinish(jsonModel); - } - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (ds != null) { - try { - ds.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if (inputStreamReader != null) { - try { - inputStreamReader.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - if (inputStream != null) { - try { - inputStream.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - } - - /** * Trust every server - dont check for any certificate */ @@ -836,5 +493,4 @@ public class HttpUtil { } } - } diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/AdUtils.java b/app/src/main/java/xyz/fycz/myreader/util/utils/AdUtils.java index 876dc38..25e8d93 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/utils/AdUtils.java +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/AdUtils.java @@ -2,7 +2,6 @@ package xyz.fycz.myreader.util.utils; import android.util.Log; - import com.weaction.ddsdk.base.DdSdkHelper; import org.json.JSONArray; @@ -10,7 +9,6 @@ import org.json.JSONException; import org.json.JSONObject; import io.reactivex.Single; -import io.reactivex.SingleEmitter; import io.reactivex.SingleOnSubscribe; import io.reactivex.annotations.NonNull; import okhttp3.MediaType; @@ -125,9 +123,9 @@ public class AdUtils { public static void initAd() { if (!hasInitAd) { hasInitAd = true; - DdSdkHelper.init("", "", "", - "", "", - "", "", + DdSdkHelper.init("1234", "216", "51716a16fbdf50905704b6575b1b3b60", + "142364", "35ce0efe5f3cc960b116db227498e238", + "8167", "85bd159309c3da1b", App.getApplication(), true); } } diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/JsExtensions.java b/app/src/main/java/xyz/fycz/myreader/util/utils/JsExtensions.java new file mode 100644 index 0000000..e60ec59 --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/JsExtensions.java @@ -0,0 +1,82 @@ +package xyz.fycz.myreader.util.utils; + + +import org.jsoup.Connection; +import org.jsoup.Jsoup; + +import java.io.IOException; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import xyz.fycz.myreader.model.analyzeRule.AnalyzeUrl; +import xyz.fycz.myreader.util.HttpUtil; + +@SuppressWarnings({"unused", "WeakerAccess"}) +public interface JsExtensions { + + /** + * js实现跨域访问,不能删 + */ + /*default String ajax(String urlStr) { + try { + AnalyzeUrl analyzeUrl = new AnalyzeUrl(urlStr); + Response response = BaseModelImpl.getInstance().getResponseO(analyzeUrl) + .blockingFirst(); + return response.body(); + } catch (Exception e) { + return e.getLocalizedMessage(); + } + }*/ + + /** + * js实现解码,不能删 + */ + default String base64Decoder(String base64) { + return StringUtils.base64Decode(base64); + } + + /** + * 章节数转数字 + */ + default String toNumChapter(String s) { + if (s == null) { + return null; + } + Pattern pattern = Pattern.compile("(第)(.+?)(章)"); + Matcher matcher = pattern.matcher(s); + if (matcher.find()) { + return matcher.group(1) + StringUtils.stringToInt(matcher.group(2)) + matcher.group(3); + } + return s; + } + + /** + * js实现重定向拦截,不能删 + */ + default Connection.Response get(String urlStr, Map headers) throws IOException { + return Jsoup.connect(urlStr) + .sslSocketFactory(HttpUtil.createSSLSocketFactory()) + .ignoreContentType(true) + .followRedirects(false) + .headers(headers) + .method(Connection.Method.GET) + .execute(); + } + + /** + * js实现重定向拦截,不能删 + */ + default Connection.Response post(String urlStr, String body, Map headers) throws IOException { + return Jsoup.connect(urlStr) + .sslSocketFactory(HttpUtil.createSSLSocketFactory()) + .ignoreContentType(true) + .followRedirects(false) + .requestBody(body) + .headers(headers) + .method(Connection.Method.POST) + .execute(); + } + + +} diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/NetworkUtils.java b/app/src/main/java/xyz/fycz/myreader/util/utils/NetworkUtils.java index d60d83b..8391561 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/utils/NetworkUtils.java +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/NetworkUtils.java @@ -20,6 +20,8 @@ import java.util.Map; import java.util.regex.Pattern; public class NetworkUtils { + public static final Pattern headerPattern = Pattern.compile("@Header:\\{.+?\\}", Pattern.CASE_INSENSITIVE); + public static final int SUCCESS = 10000; public static final int ERROR_CODE_NONET = 10001; public static final int ERROR_CODE_OUTTIME = 10002; diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java b/app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java index a4a0fd7..16d99f7 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java @@ -1,6 +1,5 @@ package xyz.fycz.myreader.util.utils; -import android.content.Context; import android.util.Log; import org.json.JSONException; @@ -10,18 +9,12 @@ import org.jsoup.nodes.Document; import okhttp3.*; import xyz.fycz.myreader.application.App; -import xyz.fycz.myreader.application.TrustAllCerts; import xyz.fycz.myreader.util.HttpUtil; -import xyz.fycz.myreader.util.StringHelper; - -import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLSocketFactory; -import javax.net.ssl.TrustManager; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.security.SecureRandom; +import java.util.Map; public class OkHttpUtils { @@ -42,7 +35,7 @@ public class OkHttpUtils { return getHtml(url, requestBody, encodeType, null); } - public static String getHtml(String url, RequestBody requestBody, String encodeType, String cookie) throws IOException { + public static String getHtml(String url, RequestBody requestBody, String encodeType, Map headers) throws IOException { Request.Builder builder = new Request.Builder() .addHeader("Accept", "*/*") @@ -50,8 +43,10 @@ public class OkHttpUtils { //.addHeader("Charsert", "utf-8") .addHeader("Cache-Control", "no-cache") .addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36"); - if (cookie != null) { - builder.addHeader("Cookie", cookie); + if (headers != null) { + for (String name : headers.keySet()){ + builder.addHeader(name, headers.get(name)); + } } if (requestBody != null) { builder.post(requestBody); @@ -75,18 +70,6 @@ public class OkHttpUtils { } } - private static SSLSocketFactory createSSLSocketFactory() { - SSLSocketFactory ssfFactory = null; - try { - SSLContext sc = SSLContext.getInstance("TLS"); - sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom()); - ssfFactory = sc.getSocketFactory(); - } catch (Exception e) { - } - - return ssfFactory; - } - public static InputStream getInputStream(String url) throws IOException { Request.Builder builder = new Request.Builder() .addHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4168.3 Safari/537.36"); @@ -104,7 +87,7 @@ public class OkHttpUtils { public static String getUpdateInfo() throws IOException, JSONException { String key = "ryvwiq"; - if (App.isApkInDebug(App.getmContext())) { + if (App.isDebug()) { key = "sgak2h"; } String url = "https://www.yuque.com/api/docs/" + key + "?book_id=19981967&include_contributors=true&include_hits=true&include_like=true&include_pager=true&include_suggests=true"; diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/RxUtils.java b/app/src/main/java/xyz/fycz/myreader/util/utils/RxUtils.java index c65c52b..e4320c4 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/utils/RxUtils.java +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/RxUtils.java @@ -1,6 +1,8 @@ package xyz.fycz.myreader.util.utils; +import org.jetbrains.annotations.NotNull; + import io.reactivex.Observable; import io.reactivex.ObservableSource; import io.reactivex.Single; diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/StringUtils.java b/app/src/main/java/xyz/fycz/myreader/util/utils/StringUtils.java index 2e66642..99d43e2 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/utils/StringUtils.java +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/StringUtils.java @@ -579,4 +579,60 @@ public class StringUtils { return null; } } + + public static String base64Decode(String str) { + byte[] bytes = Base64.decode(str, Base64.DEFAULT); + try { + return new String(bytes, StandardCharsets.UTF_8); + } catch (Exception e) { + return new String(bytes); + } + } + + public static String escape(String src) { + int i; + char j; + StringBuilder tmp = new StringBuilder(); + tmp.ensureCapacity(src.length() * 6); + for (i = 0; i < src.length(); i++) { + j = src.charAt(i); + if (Character.isDigit(j) || Character.isLowerCase(j) + || Character.isUpperCase(j)) + tmp.append(j); + else if (j < 256) { + tmp.append("%"); + if (j < 16) + tmp.append("0"); + tmp.append(Integer.toString(j, 16)); + } else { + tmp.append("%u"); + tmp.append(Integer.toString(j, 16)); + } + } + return tmp.toString(); + } + + public static String getBaseUrl(String url) { + if (url == null || !url.startsWith("http")) return null; + int index = url.indexOf("/", 9); + if (index == -1) { + return url; + } + return url.substring(0, index); + } + + // 移除字符串首尾空字符的高效方法(利用ASCII值判断,包括全角空格) + public static String trim(String s) { + if (isEmpty(s)) return ""; + int start = 0, len = s.length(); + int end = len - 1; + while ((start < end) && ((s.charAt(start) <= 0x20) || (s.charAt(start) == ' '))) { + ++start; + } + while ((start < end) && ((s.charAt(end) <= 0x20) || (s.charAt(end) == ' '))) { + --end; + } + if (end < len) ++end; + return ((start > 0) || (end < len)) ? s.substring(start, end) : s; + } } diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/UrlEncoderUtils.java b/app/src/main/java/xyz/fycz/myreader/util/utils/UrlEncoderUtils.java new file mode 100644 index 0000000..983257b --- /dev/null +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/UrlEncoderUtils.java @@ -0,0 +1,80 @@ +package xyz.fycz.myreader.util.utils; + +import java.util.BitSet; + +/** + * 这里会有误差,比如输入一个字符串 123+456,它到底是原文就是123+456还是123 456做了urlEncode后的内容呢?
+ * 其实问题是一样的,比如遇到123%2B456,它到底是原文即使如此,还是123+456 urlEncode后的呢?
+ * 在这里,我认为只要符合urlEncode规范的,就当作已经urlEncode过了
+ * 毕竟这个方法的初衷就是判断string是否urlEncode过
+ */ +public class UrlEncoderUtils { + private static BitSet dontNeedEncoding; + + static { + dontNeedEncoding = new BitSet(256); + int i; + for (i = 'a'; i <= 'z'; i++) { + dontNeedEncoding.set(i); + } + for (i = 'A'; i <= 'Z'; i++) { + dontNeedEncoding.set(i); + } + for (i = '0'; i <= '9'; i++) { + dontNeedEncoding.set(i); + } + dontNeedEncoding.set('+'); + dontNeedEncoding.set('-'); + dontNeedEncoding.set('_'); + dontNeedEncoding.set('.'); + dontNeedEncoding.set('$'); + dontNeedEncoding.set(':'); + dontNeedEncoding.set('('); + dontNeedEncoding.set(')'); + dontNeedEncoding.set('!'); + dontNeedEncoding.set('*'); + dontNeedEncoding.set('@'); + dontNeedEncoding.set('&'); + dontNeedEncoding.set('#'); + dontNeedEncoding.set(','); + dontNeedEncoding.set('['); + dontNeedEncoding.set(']'); + } + + /** + * 支持JAVA的URLEncoder.encode出来的string做判断。 即: 将' '转成'+' + * 0-9a-zA-Z保留
+ * ! * ' ( ) ; : @ & = + $ , / ? # [ ] 保留 + * 其他字符转成%XX的格式,X是16进制的大写字符,范围是[0-9A-F] + */ + public static boolean hasUrlEncoded(String str) { + boolean needEncode = false; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (dontNeedEncoding.get((int) c)) { + continue; + } + if (c == '%' && (i + 2) < str.length()) { + // 判断是否符合urlEncode规范 + char c1 = str.charAt(++i); + char c2 = str.charAt(++i); + if (isDigit16Char(c1) && isDigit16Char(c2)) { + continue; + } + } + // 其他字符,肯定需要urlEncode + needEncode = true; + break; + } + + return !needEncode; + } + + /** + * 判断c是否是16进制的字符 + */ + private static boolean isDigit16Char(char c) { + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'); + } + +} diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/BaseApi.java b/app/src/main/java/xyz/fycz/myreader/webapi/BaseApi.java deleted file mode 100644 index 4d8c566..0000000 --- a/app/src/main/java/xyz/fycz/myreader/webapi/BaseApi.java +++ /dev/null @@ -1,231 +0,0 @@ -package xyz.fycz.myreader.webapi; - -import xyz.fycz.myreader.common.ErrorCode; -import xyz.fycz.myreader.entity.JsonModel; -import xyz.fycz.myreader.util.HttpUtil; -import com.google.gson.Gson; - -import xyz.fycz.myreader.util.utils.GsonExtensionsKt; -import xyz.fycz.myreader.webapi.callback.JsonCallback; -import xyz.fycz.myreader.webapi.callback.ResultCallback; -import xyz.fycz.myreader.util.ToastUtils; - -import java.util.Map; - -public class BaseApi { - - /** - * post通用返回实体api - * @param url - * @param params - * @param c 返回的实体类型 - * @param callback - */ - public static void postCommonApi(String url, Map params, final Class c, final ResultCallback callback) { - HttpDataSource.httpPost(url, HttpUtil.makePostOutput(params), new JsonCallback() { - @Override - public void onFinish(JsonModel jsonModel) { - if (jsonModel.isSuccess()) { - callback.onFinish(new Gson().fromJson(jsonModel.getResult(),c), jsonModel.getError()); - } else { - noSuccess(jsonModel,callback); - } - } - @Override - public void onError(Exception e) { - error(e,callback); - } - }); - } - - /** - * post通用返回字符串api - * @param url - * @param params - * @param callback - */ - public static void postCommonReturnStringApi(String url, Map params, final ResultCallback callback) { - HttpDataSource.httpPost(url, HttpUtil.makePostOutput(params), new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - callback.onFinish(o, code); - } - - @Override - public void onError(Exception e) { - callback.onError(e); - } - }); - } - - /** - * get通用返回字符串api - * 通过api获取蓝奏云直链 - * @param url - * @param params - * @param callback - */ - public static void postLanzousApi(String url, Map params, final ResultCallback callback, final String referer) { - HttpDataSource.httpPost(url, HttpUtil.makePostOutput(params), new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - callback.onFinish(o, code); - } - - @Override - public void onError(Exception e) { - callback.onError(e); - } - }, referer); - } - /** - * get通用返回实体api - * @param url - * @param params - * @param c 返回的实体类型 - * @param callback - */ - public static void getCommonApi(String url, Map params, final Class c, final ResultCallback callback) { - HttpDataSource.httpGet(HttpUtil.makeURL(url,params), new JsonCallback() { - @Override - public void onFinish(JsonModel jsonModel) { - if (jsonModel.isSuccess()) { - callback.onFinish(new Gson().fromJson(jsonModel.getResult(),c), jsonModel.getError()); - } else { - noSuccess(jsonModel,callback); - } - } - - @Override - public void onError(Exception e) { - error(e,callback); - } - }); - } - - /** - * get通用返回字符串api - * @param url - * @param params - * @param callback - */ - public static void getCommonReturnStringApi(String url, Map params, final ResultCallback callback) { - HttpDataSource.httpGet(HttpUtil.makeURL(url,params), new JsonCallback() { - @Override - public void onFinish(JsonModel jsonModel) { - if (jsonModel.isSuccess()) { - callback.onFinish(jsonModel.getResult(), jsonModel.getError()); - } else { - noSuccess(jsonModel, callback); - } - } - @Override - public void onError(Exception e) { - error(e,callback); - } - }); - } - /** - * get通用返回字符串api - * @param url - * @param params - * @param callback - */ - public static void getCommonReturnStringApi2(String url, Map params, final ResultCallback callback) { - HttpDataSource.httpGet(HttpUtil.makeURL(url, params), new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - callback.onFinish(o,code); - } - - @Override - public void onError(Exception e) { - callback.onError(e); - } - }); - } - - /** - * get通用返回Html字符串api - * @param url - * @param params - * @param callback - */ - public static void getCommonReturnHtmlStringApi(String url, Map params, String charsetName, boolean isRefresh, final ResultCallback callback) { - HttpDataSource.httpGet_html(HttpUtil.makeURL(url, params), charsetName, isRefresh ,new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - callback.onFinish(o,code); - } - - @Override - public void onError(Exception e) { - callback.onError(e); -// error(e,callback); - } - }); - } - - - /** - * get通用获取实体列表api - * @param url - * @param params - * @param c 返回是列表实体类型 - * @param callback - */ - protected static void getCommonListApi(String url, Map params, final Class c, final ResultCallback callback) { - HttpDataSource.httpGet(HttpUtil.makeURL(url,params), new JsonCallback() { - @Override - public void onFinish(JsonModel jsonModel) { - if (jsonModel.isSuccess()) { - try { - callback.onFinish(GsonExtensionsKt.getGSON().fromJson(jsonModel.getResult(), c), jsonModel.getError()); - }catch (Exception e){ - callback.onError(e); - e.printStackTrace(); - } - } else { - noSuccess(jsonModel,callback); - } - } - - @Override - public void onError(Exception e) { - error(e,callback); - } - }); - } - - /** - * api异常处理 - * @param e - * @param callback - */ - private static void error(Exception e, final ResultCallback callback){ - /* if (e.toString().contains("SocketTimeoutException") || e.toString().contains("UnknownHostException")) { - TextHelper.showText("网络连接超时,请检查网络"); - }*/ - e.printStackTrace(); - callback.onError(e); - } - - /** - * api请求失败处理 - * @param jsonModel - * @param callback - */ - private static void noSuccess(JsonModel jsonModel, ResultCallback callback){ - if (!jsonModel.isSuccess()) { - if (jsonModel.getError() == ErrorCode.no_security) { - ToastUtils.showWarring("登录过期,请重新登录"); - } else { - if (jsonModel.getError() == 0) { - callback.onFinish(jsonModel.getResult(), -1); - } else { - callback.onFinish(jsonModel.getResult(), jsonModel.getError()); - } - } - } - } -} diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/BookStoreApi.java b/app/src/main/java/xyz/fycz/myreader/webapi/BookStoreApi.java index 85dbc4d..ecd41d7 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/BookStoreApi.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/BookStoreApi.java @@ -1,11 +1,22 @@ package xyz.fycz.myreader.webapi; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import io.reactivex.Single; +import io.reactivex.SingleEmitter; +import io.reactivex.SingleOnSubscribe; +import xyz.fycz.myreader.base.observer.MySingleObserver; import xyz.fycz.myreader.entity.bookstore.BookType; +import xyz.fycz.myreader.greendao.entity.Book; +import xyz.fycz.myreader.util.utils.OkHttpUtils; +import xyz.fycz.myreader.util.utils.RxUtils; import xyz.fycz.myreader.webapi.callback.ResultCallback; import xyz.fycz.myreader.webapi.crawler.base.FindCrawler; -public class BookStoreApi extends BaseApi{ +public class BookStoreApi { /** @@ -14,17 +25,18 @@ public class BookStoreApi extends BaseApi{ * @param callback */ public static void getBookTypeList(FindCrawler findCrawler, final ResultCallback callback){ - - getCommonReturnHtmlStringApi(findCrawler.getFindUrl(), null, findCrawler.getCharset(), true, new ResultCallback() { + Single.create((SingleOnSubscribe>) emitter -> { + String html = OkHttpUtils.getHtml(findCrawler.getFindUrl(), findCrawler.getCharset()); + emitter.onSuccess(findCrawler.getBookTypes(html)); + }).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver>() { @Override - public void onFinish(Object o, int code) { - callback.onFinish(findCrawler.getBookTypes((String) o),0); + public void onSuccess(@NotNull List bookTypes) { + callback.onFinish(bookTypes, 0); } @Override - public void onError(Exception e) { - callback.onError(e); - + public void onError(Throwable e) { + callback.onError((Exception) e); } }); } @@ -36,20 +48,20 @@ public class BookStoreApi extends BaseApi{ * @param callback */ public static void getBookRankList(BookType bookType, FindCrawler findCrawler, final ResultCallback callback){ - - getCommonReturnHtmlStringApi(bookType.getUrl(), null, findCrawler.getCharset(),true, new ResultCallback() { + Single.create((SingleOnSubscribe>) emitter -> { + String html = OkHttpUtils.getHtml(bookType.getUrl(), findCrawler.getCharset()); + emitter.onSuccess(findCrawler.getFindBooks(html, bookType)); + }).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver>() { @Override - public void onFinish(Object o, int code) { - callback.onFinish(findCrawler.getFindBooks((String) o, bookType),0); + public void onSuccess(@NotNull List books) { + callback.onFinish(books, 0); } @Override - public void onError(Exception e) { - callback.onError(e); - + public void onError(Throwable e) { + callback.onError((Exception) e); } }); } - } diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/CommonApi.java b/app/src/main/java/xyz/fycz/myreader/webapi/CommonApi.java index 99d1fe4..361a4cf 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/CommonApi.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/CommonApi.java @@ -1,6 +1,8 @@ package xyz.fycz.myreader.webapi; +import java.util.HashMap; import java.util.List; +import java.util.Map; import io.reactivex.Observable; import okhttp3.FormBody; @@ -21,29 +23,7 @@ import xyz.fycz.myreader.webapi.crawler.read.FYReadCrawler; import xyz.fycz.myreader.webapi.crawler.read.TianLaiReadCrawler; -public class CommonApi extends BaseApi { - - /** - * 获取章节列表 - * - * @param url - * @param callback - */ - public static void getBookChapters(String url, final ReadCrawler rc, boolean isRefresh, final ResultCallback callback) { - String charset = rc.getCharset(); - url = NetworkUtils.getAbsoluteURL(rc.getNameSpace(), url); - getCommonReturnHtmlStringApi(url, null, charset, isRefresh, new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - callback.onFinish(rc.getChaptersFromHtml((String) o), 0); - } - - @Override - public void onError(Exception e) { - callback.onError(e); - } - }); - } +public class CommonApi { /** @@ -56,51 +36,11 @@ public class CommonApi extends BaseApi { url = NetworkUtils.getAbsoluteURL(rc.getNameSpace(), url); String finalUrl = url; return Observable.create(emitter -> { - try { - emitter.onNext(rc.getChaptersFromHtml(OkHttpUtils.getHtml(finalUrl, charset))); - } catch (Exception e) { - e.printStackTrace(); - emitter.onError(e); - } + emitter.onNext(rc.getChaptersFromHtml(OkHttpUtils.getHtml(finalUrl, charset))); emitter.onComplete(); }); } - /** - * 获取章节正文 - * - * @param url - * @param callback - */ - - public static void getChapterContent(String url, final ReadCrawler rc, final ResultCallback callback) { - int tem = url.indexOf("\""); - if (tem != -1) { - url = url.substring(0, tem); - } - String charset = rc.getCharset(); - if (rc instanceof FYReadCrawler) { - if (url.contains("47.105.152.62")) { - url.replace("47.105.152.62", "novel.fycz.xyz"); - } - if (!url.contains("novel.fycz.xyz")) { - url = URLCONST.nameSpace_FY + url; - } - } - url = NetworkUtils.getAbsoluteURL(rc.getNameSpace(), url); - getCommonReturnHtmlStringApi(url, null, charset, true, new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - callback.onFinish(rc.getContentFormHtml((String) o), 0); - } - - @Override - public void onError(Exception e) { - callback.onError(e); - } - }); - } - /** * 获取章节正文 * @@ -122,32 +62,6 @@ public class CommonApi extends BaseApi { }); } - /** - * 搜索小说 - * - * @param key - * @param callback - */ - - public static void search(String key, final ReadCrawler rc, final ResultCallback callback) { - String charset = "utf-8"; - if (rc instanceof TianLaiReadCrawler) { - charset = "utf-8"; - } else { - charset = rc.getCharset(); - } - getCommonReturnHtmlStringApi(makeSearchUrl(rc.getSearchLink(), key), null, charset, false, new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - callback.onFinish(rc.getBooksFromSearchHtml((String) o), code); - } - - @Override - public void onError(Exception e) { - callback.onError(e); - } - }); - } /** * 搜索小说 @@ -185,7 +99,9 @@ public class CommonApi extends BaseApi { rc.getNameSpace().contains("soshuw.com")) { String cookie = "Hm_lvt_46329db612a10d9ae3a668a40c152e0e=1612793811,1612795781,1613200980,1613218588; " + "__cfduid=d0ebd0275436b7b0c3ccf4c9eb7394abd1619231977 "; - emitter.onNext(rc.getBooksFromSearchHtml(OkHttpUtils.getHtml(url, requestBody, finalCharset, cookie))); + Map headers = new HashMap<>(); + headers.put("Cookie", cookie); + emitter.onNext(rc.getBooksFromSearchHtml(OkHttpUtils.getHtml(url, requestBody, finalCharset, headers))); } else { emitter.onNext(rc.getBooksFromSearchHtml(OkHttpUtils.getHtml(url, requestBody, finalCharset))); } @@ -229,32 +145,6 @@ public class CommonApi extends BaseApi { }); } - /** - * 获取小说详细信息 - * - * @param book - * @param callback - */ - public static void getBookInfo(final Book book, final BookInfoCrawler bic, final ResultCallback callback) { - String url = book.getInfoUrl(); - if (StringHelper.isEmpty(url)) { - url = book.getChapterUrl(); - } - url = NetworkUtils.getAbsoluteURL(bic.getNameSpace(), url); - getCommonReturnHtmlStringApi(url, null, bic.getCharset(), false, new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - callback.onFinish(bic.getBookInfo((String) o, book), 0); - } - - @Override - public void onError(Exception e) { - callback.onError(e); - } - }); - } - - /** * 通过api获取蓝奏云可下载直链 * diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/HttpDataSource.java b/app/src/main/java/xyz/fycz/myreader/webapi/HttpDataSource.java index b319a8c..623e800 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/HttpDataSource.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/HttpDataSource.java @@ -76,203 +76,6 @@ public class HttpDataSource { - - /** - * http请求 (get) - * @param url - * @param callback - */ - public static void httpGet(String url, final JsonCallback callback) { - Log.d("HttpGet URl", url); - HttpUtil.sendGetRequest(url, new HttpCallback() { - @Override - public void onFinish(Bitmap bm) { - - } - - @Override - public void onFinish(InputStream in) { - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); - StringBuilder response = new StringBuilder(); - String line = reader.readLine(); - while (line != null) { - response.append(line); - line = reader.readLine(); - } - if (callback != null) { - Log.d("Http", "read finish:" + response.toString()); - // setResponse(response.toString()); - JsonModel jsonModel = new Gson().fromJson(response.toString(), JsonModel.class); -// jsonModel.setResult(jsonModel.getResult().replace("\n","")); -// test(jsonModel.getResult()); -// String str = new String(RSAUtilV2.decryptByPrivateKey(Base64.decode(jsonModel.getResult().replace("\n",""),Base64.DEFAULT),APPCONST.privateKey)); - if (URLCONST.isRSA && !StringHelper.isEmpty(jsonModel.getResult())) { - jsonModel.setResult(StringHelper.decode(new String(RSAUtilV2.decryptByPrivateKey(Base64.decode(jsonModel.getResult().replace("\n", ""), Base64.DEFAULT), APPCONST.privateKey)))); - } - callback.onFinish(jsonModel); - Log.d("Http", "RSA finish:" + new Gson().toJson(jsonModel)); - } - } catch (Exception e) { - callback.onError(e); - } - } - - @Override - public void onFinish(String response) { - - } - - @Override - public void onError(Exception e) { - if (callback != null) { - callback.onError(e); - } - } - - }); - } - public static void httpGet(String url, final ResultCallback callback) { - Log.d("HttpGet URl", url); - HttpUtil.sendGetRequest(url, new HttpCallback() { - @Override - public void onFinish(Bitmap bm) { - - } - - @Override - public void onFinish(InputStream in) { - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); - StringBuilder response = new StringBuilder(); - String line = reader.readLine(); - while (line != null) { - response.append(line); - line = reader.readLine(); - } - if (callback != null) { - Log.d("Http", "read finish:" + response.toString()); - callback.onFinish(response.toString(), 1); - } - } catch (Exception e) { - callback.onError(e); - } - } - - @Override - public void onFinish(String response) { - - } - - @Override - public void onError(Exception e) { - if (callback != null) { - callback.onError(e); - } - } - - }); - } - - /** - * http请求 (post) - * @param url - * @param output - * @param callback - */ - public static void httpPost(String url, String output, final JsonCallback callback) { - Log.d("HttpPost:", url + "&" + output); - HttpUtil.sendPostRequest(url, output, new HttpCallback() { - @Override - public void onFinish(Bitmap bm) { - - } - - @Override - public void onFinish(InputStream in) { - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); - StringBuilder response = new StringBuilder(); - String line = reader.readLine(); - while (line != null) { - response.append(line); - line = reader.readLine(); - } - if (callback != null) { - Log.d("Http", "read finish:" + response); - // setResponse(response.toString()); - JsonModel jsonModel = new Gson().fromJson(response.toString(), JsonModel.class); - if (URLCONST.isRSA && !StringHelper.isEmpty(jsonModel.getResult())) { - jsonModel.setResult(StringHelper.decode(new String(RSAUtilV2.decryptByPrivateKey(Base64.decode(jsonModel.getResult().replace("\n", ""), Base64.DEFAULT), APPCONST.privateKey)))); - } - callback.onFinish(jsonModel); - Log.d("Http", "RSA finish:" + new Gson().toJson(jsonModel)); - } - } catch (Exception e) { - callback.onError(e); - } - } - - @Override - public void onFinish(String response) { - Log.e("http", response); - } - - @Override - public void onError(Exception e) { - if (callback != null) { - callback.onError(e); - } - } - }); - } - - /** - * http请求 (post) - * @param url - * @param output - * @param callback - */ - public static void httpPost(String url, String output, final ResultCallback callback) { - Log.d("HttpPost:", url + "&" + output); - HttpUtil.sendPostRequest(url, output, new HttpCallback() { - @Override - public void onFinish(Bitmap bm) { - - } - @Override - public void onFinish(InputStream in) { - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); - StringBuilder response = new StringBuilder(); - String line = reader.readLine(); - while (line != null) { - response.append(line); - line = reader.readLine(); - } - if (callback != null) { - Log.d("Http", "read finish:" + response); - callback.onFinish(response.toString(), 1); - } - } catch (Exception e) { - callback.onError(e); - e.printStackTrace(); - } - } - - @Override - public void onFinish(String response) { - Log.e("http", response); - } - - @Override - public void onError(Exception e) { - if (callback != null) { - callback.onError(e); - } - } - }); - } /** * http请求 (post) 获取蓝奏云直链 * @param url @@ -319,207 +122,4 @@ public class HttpDataSource { }, referer); } - /** - * http非加密请求(get) - * @param url - * @param callback - */ - public static void httpGetNoRSA(String url, final JsonCallback callback) { - Log.d("HttpGet URl", url); - HttpUtil.sendGetRequest(url, new HttpCallback() { - @Override - public void onFinish(Bitmap bm) { - - } - - @Override - public void onFinish(InputStream in) { - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); - StringBuilder response = new StringBuilder(); - String line = reader.readLine(); - while (line != null) { - response.append(line); - line = reader.readLine(); - } - if (callback != null) { - Log.i("Http", "read finish:" + response.toString()); - // setResponse(response.toString()); - JsonModel jsonModel = new Gson().fromJson(response.toString(), JsonModel.class); - callback.onFinish(jsonModel); - Log.d("Http", "RSA finish:" + new Gson().toJson(jsonModel)); - } - } catch (Exception e) { - callback.onError(e); - } - } - - @Override - public void onFinish(String response) { - - } - - @Override - public void onError(Exception e) { - if (callback != null) { - callback.onError(e); - } - } - }); - } - - - public static void httpGetBitmap(String url, final HttpCallback callback) { - Log.d("Http", "success1"); - HttpUtil.sendBitmapGetRequest(url, new HttpCallback() { - @Override - public void onFinish(Bitmap bm) { - - } - - @Override - public void onFinish(InputStream in) { - if (callback != null) { - Bitmap bm = BitmapFactory.decodeStream(in); -// setBitmap(bm); - callback.onFinish(bm); - } - } - - @Override - public void onFinish(String response) { - - } - - @Override - public void onError(Exception e) { - if (callback != null) { - callback.onError(e); - } - } - }); - } - - /** - * 多文件上传 - * @param url - * @param files - * @param params - * @param callback - */ - public static void uploadFile(String url, String[] files, Map params, final JsonCallback callback) { - HttpUtil.uploadFileRequest(url, files, params, callback); - } - - /** - * 多文件上传2 - * @param url - * @param files - * @param params - * @param callback - */ - public static void uploadFile(String url, ArrayList files, Map params, final JsonCallback callback) { - String[] filePaths = new String[files.size()]; - int i = 0; - for (File file : files) { - filePaths[i++] = file.getAbsolutePath(); - } - HttpUtil.uploadFileRequest(url, filePaths, params, callback); - } - - - /** - * 多文件上传(okhttp) - * @param url - * @param files - * @param params - * @param callback - */ - public static void uploadFile_okhttp(String url, ArrayList files, Map params, final JsonCallback callback) { - HttpUtil.uploadFile(url, files, params, new HttpCallback() { - @Override - public void onFinish(String response) { - try { - if (callback != null) { - Log.d("Http", "read finish:" + response); - // setResponse(response.toString()); - JsonModel jsonModel = new Gson().fromJson(response.toString(), JsonModel.class); - if (URLCONST.isRSA && !StringHelper.isEmpty(jsonModel.getResult())) { - jsonModel.setResult(StringHelper.decode(new String(RSAUtilV2.decryptByPrivateKey(Base64.decode(jsonModel.getResult().replace("\n", ""), Base64.DEFAULT), APPCONST.privateKey)))); - } - callback.onFinish(jsonModel); - Log.d("Http", "RSA finish:" + new Gson().toJson(jsonModel)); - } - - } catch (Exception e) { - callback.onError(e); - e.printStackTrace(); - } - - } - - @Override - public void onFinish(InputStream in) { - - } - - @Override - public void onFinish(Bitmap bm) { - - } - - @Override - public void onError(Exception e) { - - } - }); - } - - /** - * 单文件上传 - * @param url - * @param file - * @param params - * @param callback - */ - public static void uploadFile(String url, File file, Map params, final JsonCallback callback) { - String[] filePaths = {file.getAbsolutePath()}; - HttpUtil.uploadFileRequest(url, filePaths, params, callback); - } - - /** - * 文件下载 - * @param url - * @param callback - */ - public static void httpGetFile(String url, final HttpCallback callback) { - Log.d("Http", "success1"); - HttpUtil.sendGetRequest(url, new HttpCallback() { - @Override - public void onFinish(Bitmap bm) { - - } - - @Override - public void onFinish(InputStream in) { - callback.onFinish(in); - - } - - @Override - public void onFinish(String response) { - - } - - @Override - public void onError(Exception e) { - if (callback != null) { - callback.onError(e); - } - } - }); - } - - - } diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/LanZousApi.java b/app/src/main/java/xyz/fycz/myreader/webapi/LanZousApi.java index e3a0082..3ed6ed7 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/LanZousApi.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/LanZousApi.java @@ -1,20 +1,25 @@ package xyz.fycz.myreader.webapi; +import org.jetbrains.annotations.NotNull; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; -import xyz.fycz.myreader.R; -import xyz.fycz.myreader.application.App; -import xyz.fycz.myreader.webapi.callback.ResultCallback; -import xyz.fycz.myreader.common.URLCONST; -import xyz.fycz.myreader.util.SharedPreUtils; -import xyz.fycz.myreader.util.StringHelper; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashMap; -import static xyz.fycz.myreader.webapi.BaseApi.*; +import io.reactivex.Single; +import io.reactivex.SingleOnSubscribe; +import okhttp3.MediaType; +import okhttp3.RequestBody; +import xyz.fycz.myreader.application.App; +import xyz.fycz.myreader.base.observer.MySingleObserver; +import xyz.fycz.myreader.common.URLCONST; +import xyz.fycz.myreader.util.StringHelper; +import xyz.fycz.myreader.util.utils.OkHttpUtils; +import xyz.fycz.myreader.util.utils.RxUtils; +import xyz.fycz.myreader.webapi.callback.ResultCallback; /** * @author fengyue @@ -29,17 +34,20 @@ public class LanZousApi { * @param callback */ public static void getUrl1(String lanZouUrl, final ResultCallback callback) { - getCommonReturnHtmlStringApi(lanZouUrl, null, "utf-8", true, new ResultCallback() { + Single.create((SingleOnSubscribe) emitter -> { + emitter.onSuccess(getUrl1(OkHttpUtils.getHtml(lanZouUrl))); + }).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver() { @Override - public void onFinish(Object o, int code) { - callback.onFinish(getUrl1((String) o), code); + public void onSuccess(@NotNull String s) { + callback.onFinish(s, 0); } @Override - public void onError(Exception e) { - callback.onError(e); + public void onError(Throwable e) { + callback.onError((Exception) e); } }); + } /** @@ -49,15 +57,17 @@ public class LanZousApi { * @param callback */ public static void getKey(String url, final ResultCallback callback) { - getCommonReturnHtmlStringApi(url, null, "utf-8", true, new ResultCallback() { + Single.create((SingleOnSubscribe) emitter -> { + emitter.onSuccess(getKey(OkHttpUtils.getHtml(url))); + }).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver() { @Override - public void onFinish(Object o, int code) { - callback.onFinish(getKey((String) o), code); + public void onSuccess(@NotNull String s) { + callback.onFinish(s, 0); } @Override - public void onError(Exception e) { - callback.onError(e); + public void onError(Throwable e) { + callback.onError((Exception) e); } }); } @@ -72,17 +82,28 @@ public class LanZousApi { params.put("action", "downprocess"); params.put("sign", key); params.put("ves", 1); - postLanzousApi(URLCONST.LAN_ZOUS_URL + "/ajaxm.php", params, new ResultCallback() { + Single.create((SingleOnSubscribe) emitter -> { + MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded"); + String body = "action=downprocess&sign=" + key + "&ves=1"; + RequestBody requestBody = RequestBody.create(mediaType, body); + + HashMap headers = new HashMap<>(); + headers.put("Referer", referer); + + String html = OkHttpUtils.getHtml(URLCONST.LAN_ZOUS_URL + "/ajaxm.php", requestBody, + "UTF-8", headers); + emitter.onSuccess(getUrl2(html)); + }).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver() { @Override - public void onFinish(Object o, int code) { - callback.onFinish(getUrl2((String) o), code); + public void onSuccess(@NotNull String s) { + callback.onFinish(s, 1); } @Override - public void onError(Exception e) { - callback.onError(e); + public void onError(Throwable e) { + callback.onError((Exception) e); } - }, referer); + }); } private static String getUrl1(String html) { @@ -94,9 +115,9 @@ public class LanZousApi { //SharedPreUtils spu = SharedPreUtils.getInstance(); String lanzousKeyStart = "var pposturl = '"; String keyName = StringHelper.getSubString(html, "'sign':", ","); - if (keyName.endsWith("'")){ + if (keyName.endsWith("'")) { lanzousKeyStart = "'sign':'"; - }else { + } else { lanzousKeyStart = "var " + keyName + " = '"; } //lanzousKeyStart = spu.getString(App.getmContext().getString(R.string.lanzousKeyStart)); @@ -120,7 +141,6 @@ public class LanZousApi { * 获取重定向地址 * * @param path - * */ public static void getRedirectUrl(final String path, final ResultCallback callback) { App.getApplication().newThread(() -> { @@ -140,7 +160,7 @@ public class LanZousApi { callback.onFinish(redirectUrl, 1); } catch (IOException e) { callback.onError(e); - }finally { + } finally { if (conn != null) { conn.disconnect(); } diff --git a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/find/QiDianMobileRank.java b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/find/QiDianMobileRank.java index e50355b..51592c4 100644 --- a/app/src/main/java/xyz/fycz/myreader/webapi/crawler/find/QiDianMobileRank.java +++ b/app/src/main/java/xyz/fycz/myreader/webapi/crawler/find/QiDianMobileRank.java @@ -2,11 +2,18 @@ package xyz.fycz.myreader.webapi.crawler.find; import android.content.Context; +import org.jetbrains.annotations.NotNull; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; + +import io.reactivex.Single; +import io.reactivex.SingleOnSubscribe; import xyz.fycz.myreader.R; import xyz.fycz.myreader.application.App; +import xyz.fycz.myreader.base.observer.MySingleObserver; +import xyz.fycz.myreader.util.utils.OkHttpUtils; +import xyz.fycz.myreader.util.utils.RxUtils; import xyz.fycz.myreader.webapi.callback.ResultCallback; import xyz.fycz.myreader.entity.bookstore.BookType; import xyz.fycz.myreader.entity.bookstore.QDBook; @@ -157,18 +164,19 @@ public class QiDianMobileRank extends FindCrawler { } public void getRankBooks(BookType bookType, ResultCallback rc) { - CommonApi.getCommonReturnHtmlStringApi(bookType.getUrl(), null, "UTF-8", true, - new ResultCallback() { - @Override - public void onFinish(Object o, int code) { - rc.onFinish(getBooksFromJson((String) o, bookType), 1); - } + Single.create((SingleOnSubscribe>) emitter -> { + emitter.onSuccess(getBooksFromJson(OkHttpUtils.getHtml(bookType.getUrl()), bookType)); + }).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver>() { + @Override + public void onSuccess(@NotNull List books) { + rc.onFinish(books, 0); + } - @Override - public void onError(Exception e) { - rc.onError(e); - } - }); + @Override + public void onError(Throwable e) { + rc.onError((Exception) e); + } + }); } private List getBooksFromJson(String json, BookType bookType) { diff --git a/app/src/main/java/xyz/fycz/myreader/widget/page/NetPageLoader.java b/app/src/main/java/xyz/fycz/myreader/widget/page/NetPageLoader.java index 792da27..211e128 100644 --- a/app/src/main/java/xyz/fycz/myreader/widget/page/NetPageLoader.java +++ b/app/src/main/java/xyz/fycz/myreader/widget/page/NetPageLoader.java @@ -1,6 +1,8 @@ package xyz.fycz.myreader.widget.page; +import org.jetbrains.annotations.NotNull; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; @@ -9,7 +11,9 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; +import io.reactivex.Observable; import xyz.fycz.myreader.application.App; +import xyz.fycz.myreader.base.observer.MyObserver; import xyz.fycz.myreader.common.APPCONST; import xyz.fycz.myreader.entity.Setting; import xyz.fycz.myreader.greendao.entity.Book; @@ -17,6 +21,7 @@ import xyz.fycz.myreader.greendao.entity.Chapter; import xyz.fycz.myreader.greendao.service.ChapterService; import xyz.fycz.myreader.util.StringHelper; import xyz.fycz.myreader.util.utils.FileUtils; +import xyz.fycz.myreader.util.utils.RxUtils; import xyz.fycz.myreader.webapi.CommonApi; import xyz.fycz.myreader.webapi.callback.ResultCallback; import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; @@ -222,32 +227,36 @@ public class NetPageLoader extends PageLoader { * @param chapter */ public void getChapterContent(Chapter chapter) { - CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler, new ResultCallback() { + CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler).flatMap(s -> Observable.create(emitter -> { + loadingChapters.remove(chapter); + String content = StringHelper.isEmpty(s) ? "章节内容为空" : s; + mChapterService.saveOrUpdateChapter(chapter, content); + emitter.onComplete(); + })).compose(RxUtils::toSimpleSingle).subscribe(new MyObserver() { @Override - public void onFinish(final Object o, int code) { - loadingChapters.remove(chapter); - String content = (String) o; - if (StringHelper.isEmpty(content)) content = "章节内容为空"; - mChapterService.saveOrUpdateChapter(chapter, content); + public void onNext(@NotNull Object o) { + + } + + @Override + public void onComplete() { if (isClose()) return; if (getPageStatus() == PageLoader.STATUS_LOADING && mCurChapterPos == chapter.getNumber()) { - App.runOnUiThread(() -> { - if (isPrev) { - openChapterInLastPage(); - } else { - openChapter(); - } - }); + if (isPrev) { + openChapterInLastPage(); + } else { + openChapter(); + } } } @Override - public void onError(Exception e) { + public void onError(Throwable e) { loadingChapters.remove(chapter); if (isClose()) return; if (mCurChapterPos == chapter.getNumber()) - App.runOnUiThread(() -> chapterError("请尝试重新加载或换源\n" + e.getLocalizedMessage())); - e.printStackTrace(); + chapterError("请尝试重新加载或换源\n" + e.getLocalizedMessage()); + if (App.isDebug()) e.printStackTrace(); } }); }