pull/5/head
fengyuecanzhu 4 years ago
parent f61af8d6d1
commit 71d38f1bc4
  1. 36
      .idea/inspectionProfiles/Project_Default.xml
  2. 10
      .idea/runConfigurations.xml
  3. 4
      app/build.gradle
  4. 15
      app/src/main/java/xyz/fycz/myreader/application/App.java
  5. 16
      app/src/main/java/xyz/fycz/myreader/application/TrustAllCerts.java
  6. 17
      app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
  7. 34
      app/src/main/java/xyz/fycz/myreader/common/ErrorCode.java
  8. 2
      app/src/main/java/xyz/fycz/myreader/common/URLCONST.java
  9. 266
      app/src/main/java/xyz/fycz/myreader/entity/thirdsource/BookSource3Bean.java
  10. 709
      app/src/main/java/xyz/fycz/myreader/entity/thirdsource/BookSourceBean.java
  11. 2
      app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java
  12. 2
      app/src/main/java/xyz/fycz/myreader/greendao/service/ChapterService.java
  13. 2
      app/src/main/java/xyz/fycz/myreader/model/SearchEngine.java
  14. 205
      app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeByJSonPath.java
  15. 454
      app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeByJSoup.java
  16. 194
      app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeByXPath.java
  17. 540
      app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeRule.java
  18. 316
      app/src/main/java/xyz/fycz/myreader/model/analyzeRule/AnalyzeUrl.java
  19. 160
      app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
  20. 18
      app/src/main/java/xyz/fycz/myreader/ui/activity/BookstoreActivity.java
  21. 43
      app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
  22. 43
      app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java
  23. 2
      app/src/main/java/xyz/fycz/myreader/ui/adapter/ChapterTitleAdapter.java
  24. 186
      app/src/main/java/xyz/fycz/myreader/ui/dialog/ChangeSourceDialog.java
  25. 57
      app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java
  26. 27
      app/src/main/java/xyz/fycz/myreader/ui/presenter/CatalogPresenter.java
  27. 366
      app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java
  28. 8
      app/src/main/java/xyz/fycz/myreader/util/utils/AdUtils.java
  29. 82
      app/src/main/java/xyz/fycz/myreader/util/utils/JsExtensions.java
  30. 2
      app/src/main/java/xyz/fycz/myreader/util/utils/NetworkUtils.java
  31. 31
      app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java
  32. 2
      app/src/main/java/xyz/fycz/myreader/util/utils/RxUtils.java
  33. 56
      app/src/main/java/xyz/fycz/myreader/util/utils/StringUtils.java
  34. 80
      app/src/main/java/xyz/fycz/myreader/util/utils/UrlEncoderUtils.java
  35. 231
      app/src/main/java/xyz/fycz/myreader/webapi/BaseApi.java
  36. 44
      app/src/main/java/xyz/fycz/myreader/webapi/BookStoreApi.java
  37. 122
      app/src/main/java/xyz/fycz/myreader/webapi/CommonApi.java
  38. 400
      app/src/main/java/xyz/fycz/myreader/webapi/HttpDataSource.java
  39. 68
      app/src/main/java/xyz/fycz/myreader/webapi/LanZousApi.java
  40. 20
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/find/QiDianMobileRank.java
  41. 29
      app/src/main/java/xyz/fycz/myreader/widget/page/NetPageLoader.java

@ -0,0 +1,36 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true">
<option name="TOP_LEVEL_CLASS_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="INNER_CLASS_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="METHOD_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
</value>
</option>
<option name="FIELD_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="IGNORE_DEPRECATED" value="false" />
<option name="IGNORE_JAVADOC_PERIOD" value="true" />
<option name="IGNORE_DUPLICATED_THROWS" value="false" />
<option name="IGNORE_POINT_TO_ITSELF" value="false" />
<option name="myAdditionalJavadocTags" value="date" />
</inspection_tool>
</profile>
</component>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.android.tools.idea.compose.preview.runconfiguration.ComposePreviewRunConfigurationProducer" />
</set>
</option>
</component>
</project>

@ -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 {

@ -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;
}
}

@ -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];}
}

@ -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<Map<String, String>>() {
}.getType();
public static final Pattern JS_PATTERN = Pattern.compile("(<js>[\\w\\W]*?</js>|@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");
}

@ -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序列号系统中不存在或存在多笔数据,请联系管理员
}

@ -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/";

@ -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<String> 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
);
}
}

@ -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<String> 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 "";
}
}

@ -326,7 +326,7 @@ public class BookService extends BaseService {
return false;
}*/
public boolean matchHistoryChapterPos(Book book, ArrayList<Chapter> mChapters) {
public boolean matchHistoryChapterPos(Book book, List<Chapter> mChapters) {
float matchSui = SysManager.getSetting().getMatchChapterSuitability();
int index = getDurChapter(book.getHisttoryChapterNum(), book.getChapterTotalNum(), book.getHistoryChapterId(), mChapters);
if (book.getHistoryChapterId() == null) return false;

@ -241,7 +241,7 @@ public class ChapterService extends BaseService {
*
* @param newChapters
*/
public void updateAllOldChapterData(ArrayList<Chapter> mChapters, ArrayList<Chapter> newChapters, String bookId) {
public void updateAllOldChapterData(List<Chapter> mChapters, List<Chapter> newChapters, String bookId) {
int i;
for (i = 0; i < mChapters.size() && i < newChapters.size(); i++) {
Chapter oldChapter = mChapters.get(i);

@ -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

@ -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<String> 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<String> getStringList(String rule) {
List<String> 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<String> stringList = getStringList(matcher.group());
for (String s : stringList) {
result.add(rule.replace(String.format("{%s}", matcher.group()), s));
}
}
return result;
}
} else {
List<List<String>> results = new ArrayList<>();
for (String rl : rules) {
List<String> 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<String> temp : results) {
if (i < temp.size()) {
result.add(temp.get(i));
}
}
}
} else {
for (List<String> temp : results) {
result.addAll(temp);
}
}
}
return result;
}
}
Object getObject(String rule) {
return ctx.read(rule);
}
List<Object> getList(String rule) {
List<Object> 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<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;
}
}

@ -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<String> textS = getStringList(ruleStr);
if (textS.size() == 0) {
return null;
}
return TextUtils.join(",", textS).trim();
}
/**
* 获取一个字符串
**/
String getString0(String ruleStr) {
List<String> urlList = getStringList(ruleStr);
if (!urlList.isEmpty()) {
return urlList.get(0);
}
return "";
}
/**
* 获取所有内容列表
*/
List<String> getStringList(String ruleStr) {
List<String> 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<List<String>> results = new ArrayList<>();
for (String ruleStrX : ruleStrS) {
List<String> 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<String> temp : results) {
if (i < temp.size()) {
textS.add(temp.get(i));
}
}
}
} else {
for (List<String> temp : results) {
textS.addAll(temp);
}
}
}
}
if (!isEmpty(sourceRule.replaceRegex)) {
List<String> 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<Elements> 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<String> 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<String> 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<String> getResultLast(Elements elements, String lastRule) {
List<String> textS = new ArrayList<>();
List<String> 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<TextNode> 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];
}
}
}
}

@ -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("</td>")) {
html = String.format("<tr>%s</tr>", html);
}
if (html.endsWith("</tr>") || html.endsWith("</tbody>")) {
html = String.format("<table>%s</table>", html);
}
return JXDocument.create(html);
}
List<JXNode> getElements(String xPath) {
if (TextUtils.isEmpty(xPath)) {
return null;
}
List<JXNode> 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<List<JXNode>> results = new ArrayList<>();
for (String rl : rules) {
List<JXNode> 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<JXNode> temp : results) {
if (i < temp.size()) {
jxNodes.add(temp.get(i));
}
}
}
} else {
for (List<JXNode> temp : results) {
jxNodes.addAll(temp);
}
}
}
}
return jxNodes;
}
List<String> getStringList(String xPath) {
List<String> 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<JXNode> 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<List<String>> results = new ArrayList<>();
for (String rl : rules) {
List<String> 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<String> temp : results) {
if (i < temp.size()) {
result.add(temp.get(i));
}
}
}
} else {
for (List<String> 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<JXNode> jxNodes;
if (jxNode != null) {
jxNodes = jxNode.sel(rule);
} else {
jxNodes = jxDocument.selN(rule);
}
if (jxNodes == null) return null;
return TextUtils.join(",", jxNodes);
} else {
List<String> 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();
}
}
}

@ -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<String> getStringList(String rule) throws Exception {
return getStringList(rule, false);
}
public List<String> getStringList(String rule, boolean isUrl) throws Exception {
if (isEmpty(rule)) return null;
List<SourceRule> ruleList = splitSourceRule(rule);
return getStringList(ruleList, isUrl);
}
@SuppressWarnings({"unchecked"})
public List<String> getStringList(List<SourceRule> 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<String> 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<String> 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<String>) 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<SourceRule> ruleList = splitSourceRule(ruleStr);
return getString(ruleList, isUrl);
}
public String getString(List<SourceRule> ruleList) throws Exception {
return getString(ruleList, false);
}
public String getString(List<SourceRule> 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<SourceRule> 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<Object> getElements(String ruleStr) throws Exception {
List<SourceRule> 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<Object>) result;
}
*//**
* 保存变量
*//*
private void putRule(Map<String, String> map) throws Exception {
for (Map.Entry<String, String> 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<String, String> 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<SourceRule> splitSourceRule(String ruleStr) throws Exception {
List<SourceRule> 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("<js>")) {
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);
}*/
}

@ -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<String, String> queryMap = new LinkedHashMap<>();
private Map<String, String> 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<String, String> headerMapF, String baseUrl) throws Exception {
this(urlRule, null, null, headerMapF, baseUrl);
}
@SuppressLint("DefaultLocale")
public AnalyzeUrl(String ruleUrl, final String key, final Integer page, Map<String, String> 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<String> ruleList = splitRule(ruleUrl);
for (String rule : ruleList) {
if (rule.startsWith("<js>")) {
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<String, String> 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<String, String> 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<String> splitRule(String ruleStr) {
List<String> 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<String, String> getQueryMap() {
return queryMap;
}
public Map<String, String> getHeaderMap() {
return headerMap;
}
public String getQueryStr() {
return queryStr;
}
public byte[] getPostData() {
StringBuilder builder = new StringBuilder();
Set<String> 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
}
}

@ -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<Chapter> mChapters = new ArrayList<>();
private ArrayList<Chapter> mNewestChapters = new ArrayList<>();
private List<Chapter> mChapters = new ArrayList<>();
private List<Chapter> mNewestChapters = new ArrayList<>();
private boolean isCollected;
private SourceExchangeDialog mSourceDialog;
private int sourceIndex;
private BookGroupDialog mBookGroupDia;
private List<String> 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);
@ -229,6 +243,7 @@ public class BookDetailedActivity extends BaseActivity {
initAd();
}
/**
* type 信息流类型只能填 1463 (样式请看下表)
* count 请求返回的信息流广告条目数最小为 1最大为 5通常情况下建议一次返回一条
@ -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<Book>() {
@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,12 +460,38 @@ public class BookDetailedActivity extends BaseActivity {
private void initChapters(boolean isChangeSource) {
if (mChapters.size() == 0 && !"本地书籍".equals(mBook.getType())) {
if (isCollected) {
mChapters = (ArrayList<Chapter>) mChapterService.findBookAllChapterByBookId(mBook.getId());
mChapters = mChapterService.findBookAllChapterByBookId(mBook.getId());
}
CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler, isChangeSource, new ResultCallback() {
if (chaptersDis != null) chaptersDis.dispose();
CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler)
.flatMap((Function<List<Chapter>, ObservableSource<Boolean>>) chapters -> saveChapters(chapters, isChangeSource)).compose(RxUtils::toSimpleSingle)
.subscribe(new MyObserver<Boolean>() {
@Override
public void onFinish(Object o, int code) {
ArrayList<Chapter> chapters = (ArrayList<Chapter>) o;
public void onSubscribe(Disposable d) {
chaptersDis = d;
}
@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--) {
mNewestChapters.add(mChapters.get(i));
mCatalogAdapter.refreshItems(mNewestChapters);
}
}
}
private Observable<Boolean> saveChapters(List<Chapter> chapters, boolean isChangeSource){
return Observable.create(emitter -> {
mBook.setNewestChapterTitle(chapters.get(chapters.size() - 1).getTitle());
if (isCollected) {
int noReadNum = chapters.size() - mBook.getChapterTotalNum();
@ -513,22 +511,9 @@ public class BookDetailedActivity extends BaseActivity {
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("最新章节加载失败!");
}
emitter.onNext(true);
emitter.onComplete();
});
} else {
int end = Math.max(0, mChapters.size() - 6);
for (int i = mChapters.size() - 1; i >= end; i--) {
mNewestChapters.add(mChapters.get(i));
mCatalogAdapter.refreshItems(mNewestChapters);
}
}
}
/**
@ -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<Book>) 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);
}
});
}
}
}

@ -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));
}
});*/
}
});
}

@ -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() {
@Override
public void onFinish(Object o, int code) {
mPageLoader.setmStatus(PageLoader.STATUS_LOADING);
ArrayList<Chapter> chapters = (ArrayList<Chapter>) o;
CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler).flatMap(chapters -> Observable.create(emitter -> {
updateAllOldChapterData(chapters);
initChapters();
emitter.onComplete();
})).compose(RxUtils::toSimpleSingle).subscribe(new MyObserver<Object>() {
@Override
public void onComplete() {
mPageLoader.setmStatus(PageLoader.STATUS_LOADING);
}
@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<Chapter> newChapters) {
private void updateAllOldChapterData(List<Chapter> newChapters) {
for (Chapter newChapter : newChapters) {
newChapter.setId(StringHelper.getStringRandom(25));
newChapter.setBookId(mBook.getId());
@ -1576,17 +1591,19 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
if (StringHelper.isEmpty(chapter.getBookId())) {
chapter.setId(mBook.getId());
}
CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler, new ResultCallback() {
CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler)
.subscribe(new MyObserver<String>() {
@Override
public void onFinish(Object o, int code) {
public void onNext(@NotNull String s) {
downloadingChapter = chapter.getTitle();
mChapterService.saveOrUpdateChapter(chapter, (String) o);
mChapterService.saveOrUpdateChapter(chapter, s);
curCacheChapterNum++;
}
@Override
public void onError(Exception e) {
public void onError(Throwable e) {
curCacheChapterNum++;
if (App.isDebug()) e.printStackTrace();
}
});
try {

@ -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
@ -411,27 +410,31 @@ public class SearchBookActivity extends BaseActivity {
binding.tgSuggestBook.setTags(suggestion);
} else {
SharedPreUtils spu = SharedPreUtils.getInstance();
Single.create((SingleOnSubscribe<String>) emitter -> {
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 {
url += cookie.split(";")[0];
cookie = "_csrfToken=eXRDlZxmRDLvFAmdgzqvwWAASrxxp2WkVlH4ZM7e; newstatisticUUID=1595991935_2026387981";
}
BaseApi.getCommonReturnHtmlStringApi(url, null, "utf-8", true, new ResultCallback() {
url += cookie.split(";")[0];
Map<String, String> headers = new HashMap<>();
headers.put("Cookie", cookie);
emitter.onSuccess(OkHttpUtils.getHtml(url, null,
"utf-8", headers));
}).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver<String>() {
@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);
}
});
}

@ -32,7 +32,7 @@ public class ChapterTitleAdapter extends ArrayAdapter<Chapter> {
private List<Chapter> mList;
private Book mBook;
public ChapterTitleAdapter(Context context, int resourceId, ArrayList<Chapter> datas, Book book) {
public ChapterTitleAdapter(Context context, int resourceId, List<Chapter> datas, Book book) {
super(context, resourceId, new ArrayList<>(datas));
mResourceId = resourceId;
setting = SysManager.getSetting();

@ -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<SearchBookBean, Book> mBooks = new ConcurrentMultiValueMap<>();
private ArrayList<Book> 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<ReadCrawler> 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<SearchBookBean, Book> cmvm =
(ConcurrentMultiValueMap<SearchBookBean, Book>) 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<Book> 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<Book>) mBooks.getValues(sbb);
rc.onFinish(aBooks, 1);
}
}

@ -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;
@ -400,24 +405,31 @@ public class BookcasePresenter implements BasePresenter {
Thread update = new Thread(() -> {
final ArrayList<Chapter> mChapters = (ArrayList<Chapter>) mChapterService.findBookAllChapterByBookId(book.getId());
final ReadCrawler mReadCrawler = ReadCrawlerUtil.getReadCrawler(book.getSource());
CommonApi.getBookChapters(book.getChapterUrl(), mReadCrawler, true, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
ArrayList<Chapter> chapters = (ArrayList<Chapter>) o;
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());
mHandler.sendMessage(mHandler.obtainMessage(1));
mBookService.updateEntity(book);
emitter.onComplete();
})).compose(RxUtils::toSimpleSingle).subscribe(new MyObserver<Object>() {
@Override
public void onNext(@NotNull Object o) {
}
@Override
public void onError(Exception e) {
public void onComplete() {
mHandler.sendMessage(mHandler.obtainMessage(1));
}
@Override
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);
@ -768,19 +779,26 @@ public class BookcasePresenter implements BasePresenter {
}
mHandler.postDelayed(sendDownloadNotification, 2 * downloadInterval);
for (Chapter chapter : needDownloadChapters) {
getChapterContent(book, chapter, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
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, (String) o);
mChapterService.saveOrUpdateChapter(chapter, s);
successCathe++;
curCacheChapterNum++;
})).subscribe(new MyObserver<Object>() {
@Override
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);
}
/**
* 发送通知

@ -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<Chapter> mChapters = new ArrayList<>();
private ArrayList<Chapter> mConvertChapters = new ArrayList<>();
private List<Chapter> mChapters = new ArrayList<>();
private List<Chapter> mConvertChapters = new ArrayList<>();
private int curSortflag = 0; //0正序 1倒序
private ChapterTitleAdapter mChapterTitleAdapter;
private Book mBook;
@ -62,24 +68,21 @@ public class CatalogPresenter implements BasePresenter {
return;
}
mCatalogFragment.getPbLoading().setVisibility(View.VISIBLE);
CommonApi.getBookChapters(mBook.getChapterUrl(), ReadCrawlerUtil.getReadCrawler(mBook.getSource()),false,
new ResultCallback() {
CommonApi.getBookChapters(mBook.getChapterUrl(), ReadCrawlerUtil.getReadCrawler(mBook.getSource()))
.compose(RxUtils::toSimpleSingle).subscribe(new MyObserver<List<Chapter>>() {
@Override
public void onFinish(Object o, int code) {
mChapters = (ArrayList<Chapter>) o;
App.runOnUiThread(() -> {
public void onNext(@NotNull List<Chapter> chapters) {
mChapters = chapters;
mCatalogFragment.getPbLoading().setVisibility(View.GONE);
initChapterTitleList();
});
}
@Override
public void onError(Exception e) {
public void onError(Throwable e) {
e.printStackTrace();
ToastUtils.showError("章节目录加载失败!\n" + e.getLocalizedMessage());
App.runOnUiThread(() -> mCatalogFragment.getPbLoading().setVisibility(View.GONE));
mCatalogFragment.getPbLoading().setVisibility(View.GONE);
if (App.isDebug()) e.printStackTrace();
}
});
}

@ -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<String, List<Cookie>> cookieStore = new HashMap<>();
OkHttpClient client = new OkHttpClient.Builder()
.cookieJar(new CookieJar() {
@Override
public void saveFromResponse(HttpUrl httpUrl, List<Cookie> 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<Cookie> loadForRequest(HttpUrl httpUrl) {
List<Cookie> 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<File> files, Map<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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 {
}
}
}

@ -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);
}
}

@ -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<String> 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<String, String> 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<String, String> headers) throws IOException {
return Jsoup.connect(urlStr)
.sslSocketFactory(HttpUtil.createSSLSocketFactory())
.ignoreContentType(true)
.followRedirects(false)
.requestBody(body)
.headers(headers)
.method(Connection.Method.POST)
.execute();
}
}

@ -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;

@ -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<String, String> 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";

@ -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;

@ -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;
}
}

@ -0,0 +1,80 @@
package xyz.fycz.myreader.util.utils;
import java.util.BitSet;
/**
* 这里会有误差,比如输入一个字符串 123+456,它到底是原文就是123+456还是123 456做了urlEncode后的内容呢<br>
* 其实问题是一样的比如遇到123%2B456,它到底是原文即使如此还是123+456 urlEncode后的呢 <br>
* 在这里我认为只要符合urlEncode规范的就当作已经urlEncode过了<br>
* 毕竟这个方法的初衷就是判断string是否urlEncode过<br>
*/
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保留 <br>
* ! * ' ( ) ; : @ & = + $ , / ? # [ ] 保留
* 其他字符转成%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');
}
}

@ -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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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());
}
}
}
}
}

@ -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<List<BookType>>) emitter -> {
String html = OkHttpUtils.getHtml(findCrawler.getFindUrl(), findCrawler.getCharset());
emitter.onSuccess(findCrawler.getBookTypes(html));
}).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver<List<BookType>>() {
@Override
public void onFinish(Object o, int code) {
callback.onFinish(findCrawler.getBookTypes((String) o),0);
public void onSuccess(@NotNull List<BookType> 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<List<Book>>) emitter -> {
String html = OkHttpUtils.getHtml(bookType.getUrl(), findCrawler.getCharset());
emitter.onSuccess(findCrawler.getFindBooks(html, bookType));
}).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver<List<Book>>() {
@Override
public void onFinish(Object o, int code) {
callback.onFinish(findCrawler.getFindBooks((String) o, bookType),0);
public void onSuccess(@NotNull List<Book> books) {
callback.onFinish(books, 0);
}
@Override
public void onError(Exception e) {
callback.onError(e);
public void onError(Throwable e) {
callback.onError((Exception) e);
}
});
}
}

@ -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.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<String, String> 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获取蓝奏云可下载直链
*

@ -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<String, Object> 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<File> files, Map<String, Object> 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<File> files, Map<String, Object> 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<String, Object> 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);
}
}
});
}
}

@ -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<String>) emitter -> {
emitter.onSuccess(getUrl1(OkHttpUtils.getHtml(lanZouUrl)));
}).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver<String>() {
@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<String>) emitter -> {
emitter.onSuccess(getKey(OkHttpUtils.getHtml(url)));
}).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver<String>() {
@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<String>) 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<String, String> 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<String>() {
@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) {
@ -120,7 +141,6 @@ public class LanZousApi {
* 获取重定向地址
*
* @param path
*
*/
public static void getRedirectUrl(final String path, final ResultCallback callback) {
App.getApplication().newThread(() -> {

@ -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,16 +164,17 @@ public class QiDianMobileRank extends FindCrawler {
}
public void getRankBooks(BookType bookType, ResultCallback rc) {
CommonApi.getCommonReturnHtmlStringApi(bookType.getUrl(), null, "UTF-8", true,
new ResultCallback() {
Single.create((SingleOnSubscribe<List<QDBook>>) emitter -> {
emitter.onSuccess(getBooksFromJson(OkHttpUtils.getHtml(bookType.getUrl()), bookType));
}).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver<List<QDBook>>() {
@Override
public void onFinish(Object o, int code) {
rc.onFinish(getBooksFromJson((String) o, bookType), 1);
public void onSuccess(@NotNull List<QDBook> books) {
rc.onFinish(books, 0);
}
@Override
public void onError(Exception e) {
rc.onError(e);
public void onError(Throwable e) {
rc.onError((Exception) e);
}
});
}

@ -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() {
@Override
public void onFinish(final Object o, int code) {
CommonApi.getChapterContent(chapter.getUrl(), mReadCrawler).flatMap(s -> Observable.create(emitter -> {
loadingChapters.remove(chapter);
String content = (String) o;
if (StringHelper.isEmpty(content)) content = "章节内容为空";
String content = StringHelper.isEmpty(s) ? "章节内容为空" : s;
mChapterService.saveOrUpdateChapter(chapter, content);
emitter.onComplete();
})).compose(RxUtils::toSimpleSingle).subscribe(new MyObserver<Object>() {
@Override
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();
}
});
}
}
@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();
}
});
}

Loading…
Cancel
Save