新增书源

pull/5/head
fengyuecanzhu 4 years ago
parent 36a2647ccb
commit 44e0be9ab8
  1. BIN
      .idea/caches/build_file_checksums.ser
  2. 10
      .idea/misc.xml
  3. 2
      .idea/sqldialects.xml
  4. 5
      app/build.gradle
  5. BIN
      app/libs/adapter-rxjava2-2.7.2.jar
  6. BIN
      app/libs/classes.jar
  7. BIN
      app/libs/rxjava-2.2.19.jar
  8. 16
      app/src/main/assets/updatelog.fy
  9. 11
      app/src/main/java/xyz/fycz/myreader/application/MyApplication.java
  10. 6
      app/src/main/java/xyz/fycz/myreader/application/SysManager.java
  11. 2
      app/src/main/java/xyz/fycz/myreader/base/adapter/BaseListAdapter.java
  12. 5
      app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
  13. 63
      app/src/main/java/xyz/fycz/myreader/controller/SyncChaptersController.java
  14. 12
      app/src/main/java/xyz/fycz/myreader/creator/ListenerInterface.java
  15. 11
      app/src/main/java/xyz/fycz/myreader/enums/BookSource.java
  16. 20
      app/src/main/java/xyz/fycz/myreader/greendao/entity/Book.java
  17. 3
      app/src/main/java/xyz/fycz/myreader/greendao/service/BookMarkService.java
  18. 1
      app/src/main/java/xyz/fycz/myreader/greendao/service/BookService.java
  19. 253
      app/src/main/java/xyz/fycz/myreader/model/SearchEngine.java
  20. 8
      app/src/main/java/xyz/fycz/myreader/model/backup/BackupAndRestore.java
  21. 4
      app/src/main/java/xyz/fycz/myreader/model/backup/UserService.java
  22. 16
      app/src/main/java/xyz/fycz/myreader/model/mulvalmap/ConcurrentMultiValueMap.java
  23. 2
      app/src/main/java/xyz/fycz/myreader/model/mulvalmap/LinkedMultiValueMap.java
  24. 2
      app/src/main/java/xyz/fycz/myreader/model/mulvalmap/MultiValueMap.java
  25. 15
      app/src/main/java/xyz/fycz/myreader/model/mulvalmap/MultiValueSetMap.java
  26. 13
      app/src/main/java/xyz/fycz/myreader/source/HttpDataSource.java
  27. 3
      app/src/main/java/xyz/fycz/myreader/ui/activity/AboutActivity.java
  28. 90
      app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java
  29. 36
      app/src/main/java/xyz/fycz/myreader/ui/activity/BookstoreActivity.java
  30. 2
      app/src/main/java/xyz/fycz/myreader/ui/activity/FileSystemActivity.java
  31. 6
      app/src/main/java/xyz/fycz/myreader/ui/activity/LoginActivity.java
  32. 15
      app/src/main/java/xyz/fycz/myreader/ui/activity/MainActivity.java
  33. 62
      app/src/main/java/xyz/fycz/myreader/ui/activity/MoreSettingActivity.java
  34. 2
      app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
  35. 7
      app/src/main/java/xyz/fycz/myreader/ui/activity/RegisterActivity.java
  36. 196
      app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java
  37. 32
      app/src/main/java/xyz/fycz/myreader/ui/adapter/BookStoreBookAdapter.java
  38. 10
      app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseAdapter.java
  39. 5
      app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseDetailedAdapter.java
  40. 6
      app/src/main/java/xyz/fycz/myreader/ui/adapter/BookcaseDragAdapter.java
  41. 2
      app/src/main/java/xyz/fycz/myreader/ui/adapter/FontsAdapter.java
  42. 206
      app/src/main/java/xyz/fycz/myreader/ui/adapter/SearchBookAdapter.java
  43. 19
      app/src/main/java/xyz/fycz/myreader/ui/adapter/SourceExchangeAdapter.java
  44. 134
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/SearchBookHolder.java
  45. 40
      app/src/main/java/xyz/fycz/myreader/ui/adapter/holder/SourceExchangeHolder.java
  46. 4
      app/src/main/java/xyz/fycz/myreader/ui/dialog/APPDownloadTip.java
  47. 8
      app/src/main/java/xyz/fycz/myreader/ui/dialog/ChangeSourceDialog.java
  48. 8
      app/src/main/java/xyz/fycz/myreader/ui/dialog/DialogCreator.java
  49. 4
      app/src/main/java/xyz/fycz/myreader/ui/dialog/MultiChoiceDialog.java
  50. 2
      app/src/main/java/xyz/fycz/myreader/ui/dialog/MyAlertDialog.java
  51. 222
      app/src/main/java/xyz/fycz/myreader/ui/dialog/SourceExchangeDialog.java
  52. 7
      app/src/main/java/xyz/fycz/myreader/ui/fragment/BookStoreFragment.java
  53. 4
      app/src/main/java/xyz/fycz/myreader/ui/fragment/BookcaseFragment.java
  54. 14
      app/src/main/java/xyz/fycz/myreader/ui/fragment/FindFragment.java
  55. 9
      app/src/main/java/xyz/fycz/myreader/ui/fragment/MineFragment.java
  56. 12
      app/src/main/java/xyz/fycz/myreader/ui/presenter/BookStorePresenter.java
  57. 108
      app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java
  58. 5
      app/src/main/java/xyz/fycz/myreader/ui/presenter/CatalogPresenter.java
  59. 55
      app/src/main/java/xyz/fycz/myreader/ui/presenter/ReadPresenter.java
  60. 2
      app/src/main/java/xyz/fycz/myreader/util/DownloadMangerUtils.java
  61. 20
      app/src/main/java/xyz/fycz/myreader/util/EmailUtils.java
  62. 58
      app/src/main/java/xyz/fycz/myreader/util/FormatDateUtils.java
  63. 15
      app/src/main/java/xyz/fycz/myreader/util/HttpUtil.java
  64. 272
      app/src/main/java/xyz/fycz/myreader/util/ImageUtil.java
  65. 157
      app/src/main/java/xyz/fycz/myreader/util/ListViewHeight.java
  66. 69
      app/src/main/java/xyz/fycz/myreader/util/NotifyHelper.java
  67. 149
      app/src/main/java/xyz/fycz/myreader/util/PermissionHelper.java
  68. 4
      app/src/main/java/xyz/fycz/myreader/util/PermissionsChecker.java
  69. 31
      app/src/main/java/xyz/fycz/myreader/util/PingUtil.java
  70. 123
      app/src/main/java/xyz/fycz/myreader/util/QRCodeUtil.java
  71. 137
      app/src/main/java/xyz/fycz/myreader/util/SharedPreferencesUtils.java
  72. 98
      app/src/main/java/xyz/fycz/myreader/util/SoftKeyBroadManager.java
  73. 51
      app/src/main/java/xyz/fycz/myreader/util/TelephonyUtil.java
  74. 5
      app/src/main/java/xyz/fycz/myreader/util/UploadImageTask.java
  75. 27
      app/src/main/java/xyz/fycz/myreader/util/VibratorUtil.java
  76. 9
      app/src/main/java/xyz/fycz/myreader/util/notification/NotificationUtil.java
  77. 43
      app/src/main/java/xyz/fycz/myreader/util/utils/MD5Utils.java
  78. 69
      app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java
  79. 8
      app/src/main/java/xyz/fycz/myreader/webapi/BaseApi.java
  80. 10
      app/src/main/java/xyz/fycz/myreader/webapi/BookStoreApi.java
  81. 95
      app/src/main/java/xyz/fycz/myreader/webapi/CommonApi.java
  82. 6
      app/src/main/java/xyz/fycz/myreader/webapi/LanZousApi.java
  83. 2
      app/src/main/java/xyz/fycz/myreader/webapi/callback/HttpCallback.java
  84. 2
      app/src/main/java/xyz/fycz/myreader/webapi/callback/JsonCallback.java
  85. 2
      app/src/main/java/xyz/fycz/myreader/webapi/callback/ResultCallback.java
  86. 2
      app/src/main/java/xyz/fycz/myreader/webapi/callback/URLConnectionCallback.java
  87. 21
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/ReadCrawlerUtil.java
  88. 2
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/base/BookInfoCrawler.java
  89. 2
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/base/FindCrawler.java
  90. 6
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/base/ReadCrawler.java
  91. 117
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/find/ABC.java
  92. 270
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/find/QiDianMobileRank.java
  93. 151
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/Ben100ReadCrawler.java
  94. 15
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/BiQuGe44ReadCrawler.java
  95. 15
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/BiQuGeReadCrawler.java
  96. 139
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/CansShu99ReadCrawler.java
  97. 130
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/ChaoXingReadCrawler.java
  98. 14
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/FYReadCrawler.java
  99. 127
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/JiuTaoReadCrawler.java
  100. 140
      app/src/main/java/xyz/fycz/myreader/webapi/crawler/read/MiQuReadCrawler.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -5,10 +5,10 @@
</component>
<component name="NullableNotNullManager">
<option name="myDefaultNullable" value="android.support.annotation.Nullable" />
<option name="myDefaultNotNull" value="org.jetbrains.annotations.NotNull" />
<option name="myDefaultNotNull" value="io.reactivex.annotations.NonNull" />
<option name="myNullables">
<value>
<list size="13">
<list size="15">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
<item index="2" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
@ -22,12 +22,14 @@
<item index="10" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
<item index="11" class="java.lang.String" itemvalue="android.annotation.Nullable" />
<item index="12" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
<item index="13" class="java.lang.String" itemvalue="io.reactivex.annotations.Nullable" />
<item index="14" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.Nullable" />
</list>
</value>
</option>
<option name="myNotNulls">
<value>
<list size="12">
<list size="14">
<item index="0" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
<item index="1" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
<item index="2" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
@ -40,6 +42,8 @@
<item index="9" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
<item index="10" class="java.lang.String" itemvalue="android.annotation.NonNull" />
<item index="11" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.NonNull" />
<item index="12" class="java.lang.String" itemvalue="io.reactivex.annotations.NonNull" />
<item index="13" class="java.lang.String" itemvalue="io.reactivex.rxjava3.annotations.NonNull" />
</list>
</value>
</option>

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

@ -127,10 +127,13 @@ dependencies {
implementation 'net.ricecode:string-similarity:1.0.0'
implementation 'com.jayway.jsonpath:json-path:2.4.0'
//RxAndroid
implementation 'io.reactivex.rxjava2:rxjava:2.2.19'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
}
greendao {
schemaVersion 14
schemaVersion 16
daoPackage 'xyz.fycz.myreader.greendao.gen'
// targetGenDir 'src/main/java'
}

Binary file not shown.

Binary file not shown.

@ -1,3 +1,19 @@
2020.10.01
风月读书v1.20.100123
1、新增书源:
(1)网络小说书源:米趣小说、九桃小说、云中书库
(2)实体书书源:超星图书·实体、作品集·实体、99藏书·实体、100本·实体
2、优化书籍搜索显示,提升显示速度
3、优化换源对话框
4、阅读界面新增换源按钮,可在阅读界面直接换源
5、新增搜索线程设置
6、修复已知bug
2020.09.27
风月读书v1.20.092718
1、修复搜索书籍后书籍详情页点击更多闪退的问题
2、新增批量从分组移除书籍(不会删除书籍)
2020.09.26
风月读书v1.20.092617
1、新增书架自定义分组功能(点击编辑书架即可选择书籍加入指定分组[需要自行添加分组])

@ -22,6 +22,8 @@ import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.app.AppCompatDelegate;
import io.reactivex.internal.functions.Functions;
import io.reactivex.plugins.RxJavaPlugins;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
@ -41,8 +43,8 @@ import javax.net.ssl.X509TrustManager;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.creator.APPDownloadTip;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.ui.dialog.APPDownloadTip;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.entity.UpdateInfo;
import xyz.fycz.myreader.ui.activity.MainActivity;
@ -63,16 +65,19 @@ public class MyApplication extends Application {
super.onCreate();
application = this;
HttpUtil.trustAllHosts();//信任所有证书
RxJavaPlugins.setErrorHandler(Functions.emptyConsumer());
// handleSSLHandshake();
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannel();
}
mFixedThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());//初始化线程池
BaseActivity.setCloseAntiHijacking(true);
initNightTheme();
}
public void initNightTheme() {
if (isNightFS()){
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);

@ -8,6 +8,7 @@ import xyz.fycz.myreader.enums.ReadStyle;
import xyz.fycz.myreader.util.CacheHelper;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.widget.page.PageMode;
import static xyz.fycz.myreader.application.MyApplication.getVersionCode;
@ -78,11 +79,12 @@ public class SysManager {
public static void resetSetting(){
Setting setting = getSetting();
setting.setVolumeTurnPage(true);
/*setting.setVolumeTurnPage(true);
setting.setMatchChapter(true);
setting.setRefreshWhenStart(true);
setting.setOpenBookStore(true);
setting.setResetScreen(3);
setting.setResetScreen(3);*/
ReadCrawlerUtil.resetReaderCrawlers();
setting.setSettingVersion(APPCONST.SETTING_VERSION);
saveSetting(setting);
}

@ -18,7 +18,7 @@ public abstract class BaseListAdapter<T> extends RecyclerView.Adapter<RecyclerVi
private static final String TAG = "BaseListAdapter";
/*common statement*/
protected final List<T> mList = new ArrayList<>();
protected List<T> mList = new ArrayList<>();
protected OnItemClickListener mClickListener;
protected OnItemLongClickListener mLongClickListener;

@ -27,6 +27,8 @@ public class APPCONST {
//BookCachePath (因为getCachePath引用了Context,所以必须是静态变量,不能够是静态常量)
public static String BOOK_CACHE_PATH = FileUtils.getCachePath() + File.separator
+ "book_cache"+ File.separator ;
public static String HTML_CACHE_PATH = FileUtils.getCachePath() + File.separator
+ "html_cache"+ File.separator ;
public static final String LOADING_ERROR = "\t\t \t\t\t\t\n\n  章节内容转码失败!\n\n\t\t \t\t\t\n\n";
public static long exitTime;
@ -38,6 +40,7 @@ public class APPCONST {
public static final String CHAPTER_PAGE = "chapter_page";
public static final String SETTING = "setting";
public static final String SEARCH_BOOK_BEAN = "SearchBookBean";
public static final String SOURCE_INDEX = "sourceIndex";
public static final String FONT = "font";
public static final String RESULT_RESET_SCREEN = "result_reset_screen";
public static final String RESULT_IS_COLLECTED = "result_is_collected";
@ -76,7 +79,7 @@ public class APPCONST {
public static final int PERMISSIONS_REQUEST_STORAGE = 10001;
//设置版本号
public static final int SETTING_VERSION = 3;
public static final int SETTING_VERSION = 5;
public static final String FORMAT_FILE_DATE = "yyyy-MM-dd";

@ -1,63 +0,0 @@
package xyz.fycz.myreader.controller;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.service.ChapterService;
import java.util.ArrayList;
public class SyncChaptersController {
private ArrayList<Chapter> mLocalChapters;//本地章节
private ArrayList<Chapter> mNetChapters;//网络章节
private Book mBook;//书
private ChapterService mChapterService;
public SyncChaptersController(ArrayList<Chapter> localChapters,ArrayList<Chapter> netChapters,Book book){
mLocalChapters = localChapters;
mNetChapters = netChapters;
mBook = book;
mChapterService = ChapterService.getInstance();
}
/**
* 开始同步
*/
public void sync(){
}
/**
* 更新所有章节
*
* @param newChapters
*/
private void updateAllOldChapterData(ArrayList<Chapter> newChapters) {
int i;
for (i = 0; i < mLocalChapters.size() && i < newChapters.size(); i++) {
Chapter oldChapter = mLocalChapters.get(i);
Chapter newChapter = newChapters.get(i);
if (!oldChapter.getTitle().equals(newChapter.getTitle())) {
oldChapter.setTitle(newChapter.getTitle());
oldChapter.setUrl(newChapter.getUrl());
oldChapter.setContent(null);
mChapterService.updateEntity(oldChapter);
}
}
if (mLocalChapters.size() < newChapters.size()) {
for (int j = mLocalChapters.size(); j < newChapters.size(); j++) {
mLocalChapters.add(newChapters.get(j));
mChapterService.addChapter(newChapters.get(j), null);
}
} else if (mLocalChapters.size() > newChapters.size()) {
for (int j = newChapters.size(); j < mLocalChapters.size(); j++) {
mChapterService.deleteEntity(mLocalChapters.get(j));
}
mLocalChapters.subList(0, newChapters.size());
}
}
}

@ -1,12 +0,0 @@
package xyz.fycz.myreader.creator;
/**
* @author fengyue
* @date 2020/7/15 8:02
*/
public class ListenerInterface {
public interface OnRefreshFinishListener{
void onRefresh();
void onFinish();
}
}

@ -2,7 +2,6 @@ package xyz.fycz.myreader.enums;
/**
* 小说源
* Created by zhao on 2020/04/13.
*/
public enum BookSource {
@ -13,6 +12,16 @@ public enum BookSource {
pinshu("品书网"),
biquge("笔趣阁"),
qb5("全本小说"),
miqu("米趣小说"),
jiutao("九桃小说"),
yunzhong("云中书库"),
sonovel("搜小说网"),
quannovel("全小说网"),
qiqi("奇奇小说"),
chaoxing("超星图书·实体"),
zuopin("作品集·实体"),
cangshu99("99藏书·实体"),
ben100("100本·实体"),
local("本地书籍");
public String text;

@ -26,6 +26,7 @@ public class Book implements Serializable {
private String name;//书名
private String chapterUrl;//书目Url(本地书籍为:本地书籍地址)
private String infoUrl;//书目详情Url
private String imgUrl;//封面图片url
private String desc;//简介
private String author;//作者
@ -64,10 +65,10 @@ public class Book implements Serializable {
private int groupSort;//分组排序
@Generated(hash = 1308680987)
public Book(String id, String name, String chapterUrl, String imgUrl,
String desc, String author, String type, String updateDate,
String newestChapterId, String newestChapterTitle,
@Generated(hash = 170911086)
public Book(String id, String name, String chapterUrl, String infoUrl,
String imgUrl, String desc, String author, String type,
String updateDate, String newestChapterId, String newestChapterTitle,
String newestChapterUrl, String historyChapterId,
int histtoryChapterNum, int sortCode, int noReadNum,
int chapterTotalNum, int lastReadPosition, String source,
@ -76,6 +77,7 @@ public class Book implements Serializable {
this.id = id;
this.name = name;
this.chapterUrl = chapterUrl;
this.infoUrl = infoUrl;
this.imgUrl = imgUrl;
this.desc = desc;
this.author = author;
@ -101,6 +103,7 @@ public class Book implements Serializable {
this.id = book.id;
this.name = book.name;
this.chapterUrl = book.chapterUrl;
this.infoUrl = book.infoUrl;
this.imgUrl = book.imgUrl;
this.desc = book.desc;
this.author = book.author;
@ -119,6 +122,7 @@ public class Book implements Serializable {
this.isCloseUpdate = book.isCloseUpdate;
this.isDownLoadAll = book.isDownLoadAll;
this.groupId = book.groupId;
this.groupSort = book.groupSort;
}
@Generated(hash = 1839243756)
@ -311,6 +315,14 @@ public class Book implements Serializable {
this.groupSort = groupSort;
}
public String getInfoUrl() {
return this.infoUrl;
}
public void setInfoUrl(String infoUrl) {
this.infoUrl = infoUrl;
}

@ -59,6 +59,9 @@ public class BookMarkService extends BaseService {
* @return
*/
public List<BookMark> findBookAllBookMarkByBookId(String bookId) {
if (bookId == null) {
return new ArrayList<>();
}
return GreenDaoManager.getInstance().getSession().getBookMarkDao()
.queryBuilder()
.where(BookMarkDao.Properties.BookId.eq(bookId))

@ -101,6 +101,7 @@ public class BookService extends BaseService {
book.setSortCode(0);
book.setGroupSort(0);
book.setGroupId(SharedPreUtils.getInstance().getString("curBookGroupId", ""));
if (StringHelper.isEmpty(book.getId())) {
book.setId(StringHelper.getStringRandom(25));
}

@ -0,0 +1,253 @@
package xyz.fycz.myreader.model;
import androidx.annotation.NonNull;
import io.reactivex.Observer;
import io.reactivex.Scheduler;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.webapi.CommonApi;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SearchEngine {
private static final String TAG = "SearchEngine";
//线程池
private ExecutorService executorService;
private Scheduler scheduler;
private CompositeDisposable compositeDisposable;
private List<ReadCrawler> mSourceList = new ArrayList<>();
private int threadsNum;
private int searchSiteIndex;
private int searchSuccessNum;
private int searchFinishNum;
private OnSearchListener searchListener;
public SearchEngine() {
threadsNum = SharedPreUtils.getInstance().getInt("threadNum", 8);
}
public void setOnSearchListener(OnSearchListener searchListener) {
this.searchListener = searchListener;
}
/**
* 搜索引擎初始化
*/
public void initSearchEngine(@NonNull List<ReadCrawler> sourceList) {
mSourceList.addAll(sourceList);
executorService = Executors.newFixedThreadPool(threadsNum);
scheduler = Schedulers.from(executorService);
compositeDisposable = new CompositeDisposable();
}
public void stopSearch() {
if (compositeDisposable != null) compositeDisposable.dispose();
compositeDisposable = new CompositeDisposable();
searchListener.loadMoreFinish(true);
}
/**
* 刷新引擎
*
* @param sourceList
*/
public void refreshSearchEngine(@NonNull List<ReadCrawler> sourceList) {
mSourceList.clear();
mSourceList.addAll(sourceList);
}
/**
* 关闭引擎
*/
public void closeSearchEngine() {
executorService.shutdown();
if (!compositeDisposable.isDisposed())
compositeDisposable.dispose();
compositeDisposable = null;
}
/**
* 搜索关键字
*
* @param keyword
*/
public void search(String keyword) {
if (mSourceList.size() == 0) {
ToastUtils.showWarring("当前书源已全部禁用,无法搜索!");
searchListener.loadMoreFinish(true);
return;
}
searchSuccessNum = 0;
searchSiteIndex = -1;
searchFinishNum = 0;
for (int i = 0; i < threadsNum; i++) {
searchOnEngine(keyword);
}
}
/**
* 根据书名和作者搜索书籍
*
* @param title
* @param author
*/
public void search(String title, String author) {
if (mSourceList.size() == 0) {
ToastUtils.showWarring("当前书源已全部禁用,无法搜索!");
searchListener.loadMoreFinish(true);
return;
}
searchSuccessNum = 0;
searchSiteIndex = -1;
searchFinishNum = 0;
for (int i = 0; i < threadsNum; i++) {
searchOnEngine(title, author);
}
}
private synchronized void searchOnEngine(String keyword) {
searchSiteIndex++;
if (searchSiteIndex < mSourceList.size()) {
ReadCrawler crawler = mSourceList.get(searchSiteIndex);
String searchKey = keyword;
if (crawler.getSearchCharset().toLowerCase().equals("gbk")) {
try {
searchKey = URLEncoder.encode(keyword, crawler.getSearchCharset());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
CommonApi.search(searchKey, crawler)
.subscribeOn(scheduler)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ConcurrentMultiValueMap<SearchBookBean, Book>>() {
@Override
public void onSubscribe(Disposable d) {
compositeDisposable.add(d);
}
@Override
public void onNext(ConcurrentMultiValueMap<SearchBookBean, Book> bookSearchBeans) {
searchFinishNum++;
if (bookSearchBeans != null) {
searchSuccessNum++;
searchListener.loadMoreSearchBook(bookSearchBeans);
}
searchOnEngine(keyword);
}
@Override
public void onError(Throwable e) {
searchFinishNum++;
searchOnEngine(keyword);
}
@Override
public void onComplete() {
}
});
} else {
if (searchFinishNum >= mSourceList.size()) {
if (searchSuccessNum == 0) {
searchListener.searchBookError(new Throwable("未搜索到内容"));
}
searchListener.loadMoreFinish(true);
}
}
}
private synchronized void searchOnEngine(final String title, final String author) {
searchSiteIndex++;
if (searchSiteIndex < mSourceList.size()) {
ReadCrawler crawler = mSourceList.get(searchSiteIndex);
String searchKey = title;
if (crawler.getSearchCharset().toLowerCase().equals("gbk")) {
try {
searchKey = URLEncoder.encode(title, crawler.getSearchCharset());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
CommonApi.search(searchKey, crawler)
.subscribeOn(scheduler)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ConcurrentMultiValueMap<SearchBookBean, Book>>() {
@Override
public void onSubscribe(Disposable d) {
compositeDisposable.add(d);
}
@Override
public void onNext(ConcurrentMultiValueMap<SearchBookBean, Book> bookSearchBeans) {
searchFinishNum++;
if (bookSearchBeans != null) {
List<Book> books = bookSearchBeans.getValues(new SearchBookBean(title, author));
if (books != null) {
searchSuccessNum++;
searchListener.loadMoreSearchBook(books);
}
}
searchOnEngine(title, author);
}
@Override
public void onError(Throwable e) {
searchFinishNum++;
searchOnEngine(title, author);
}
@Override
public void onComplete() {
}
});
} else {
if (searchFinishNum >= mSourceList.size()) {
if (searchSuccessNum == 0) {
searchListener.searchBookError(new Throwable("未搜索到内容"));
}
searchListener.loadMoreFinish(true);
}
}
}
/************************************************************************/
public interface OnSearchListener {
void loadMoreFinish(Boolean isAll);
void loadMoreSearchBook(ConcurrentMultiValueMap<SearchBookBean, Book> items);
void loadMoreSearchBook(List<Book> items);
void searchBookError(Throwable throwable);
}
}

@ -1,4 +1,4 @@
package xyz.fycz.myreader.backup;
package xyz.fycz.myreader.model.backup;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.common.APPCONST;
@ -84,6 +84,7 @@ public class BackupAndRestore {
boolean isCloseUpdate = false;
boolean isDownloadAll = true;
String group = "allBook";
String infoUrl = "";
if(!sBookFields[2].contains("novel.fycz.xyz")){
source = sBookFields[17];
}
@ -99,7 +100,10 @@ public class BackupAndRestore {
if (sBookFields.length >= 21){
group = sBookFields[20];
}
Book book = new Book(sBookFields[0], sBookFields[1], sBookFields[2], sBookFields[3], sBookFields[4],
if (sBookFields.length >= 22){
infoUrl = sBookFields[21];
}
Book book = new Book(sBookFields[0], sBookFields[1], sBookFields[2], infoUrl, sBookFields[3], sBookFields[4],
sBookFields[5], sBookFields[6], sBookFields[7], sBookFields[8], sBookFields[9], sBookFields[10],
sBookFields[11], Integer.parseInt(sBookFields[12]), Integer.parseInt(sBookFields[13]),
Integer.parseInt(sBookFields[14]), Integer.parseInt(sBookFields[15]), Integer.parseInt(sBookFields[16])

@ -1,7 +1,7 @@
package xyz.fycz.myreader.backup;
package xyz.fycz.myreader.model.backup;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.util.*;

@ -1,4 +1,4 @@
package xyz.fycz.myreader.mulvalmap;
package xyz.fycz.myreader.model.mulvalmap;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@ -10,7 +10,7 @@ import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentMultiValueMap<K, V> implements MultiValueSetMap<K, V> {
protected Map<K, HashSet<V>> mSource = new ConcurrentHashMap<>();
protected Map<K, LinkedHashSet<V>> mSource = new ConcurrentHashMap<>();
public ConcurrentMultiValueMap() {
}
@ -20,13 +20,13 @@ public class ConcurrentMultiValueMap<K, V> implements MultiValueSetMap<K, V> {
if (key != null) {
// 如果有这个Key就继续添加Value,没有就创建一个List并添加Value
if (!mSource.containsKey(key))
mSource.put(key, new HashSet<V>(2));
mSource.put(key, new LinkedHashSet<V>(2));
mSource.get(key).add(value);
}
}
@Override
public void add(K key, HashSet<V> values) {
public void add(K key, LinkedHashSet<V> values) {
// 便利添加进来的List的Value,调用上面的add(K, V)方法添加
for (V value : values) {
add(key, value);
@ -36,7 +36,7 @@ public class ConcurrentMultiValueMap<K, V> implements MultiValueSetMap<K, V> {
@Override
public void addAll(MultiValueSetMap<K, V> mvm) {
for(K k : mvm.keySet()){
add(k, new HashSet<V>(mvm.getValues(k)));
add(k, new LinkedHashSet<V>(mvm.getValues(k)));
}
}
@ -48,21 +48,21 @@ public class ConcurrentMultiValueMap<K, V> implements MultiValueSetMap<K, V> {
}
@Override
public void set(K key, HashSet<V> values) {
public void set(K key, LinkedHashSet<V> values) {
// 移除Key,添加List<V>
mSource.remove(key);
add(key, values);
}
@Override
public void set(Map<K, HashSet<V>> map) {
public void set(Map<K, LinkedHashSet<V>> map) {
// 移除所有值,便利Map里的所有值添加进来
mSource.clear();
mSource.putAll(map);
}
@Override
public HashSet<V> remove(K key) {
public LinkedHashSet<V> remove(K key) {
return mSource.remove(key);
}

@ -1,4 +1,4 @@
package xyz.fycz.myreader.mulvalmap;
package xyz.fycz.myreader.model.mulvalmap;
import java.util.ArrayList;
import java.util.LinkedHashMap;

@ -1,4 +1,4 @@
package xyz.fycz.myreader.mulvalmap;
package xyz.fycz.myreader.model.mulvalmap;
import java.util.List;
import java.util.Map;

@ -1,9 +1,6 @@
package xyz.fycz.myreader.mulvalmap;
package xyz.fycz.myreader.model.mulvalmap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
/**
* <p>Can save multiple the value of the map.</p>
@ -29,7 +26,7 @@ public interface MultiValueSetMap<K, V> {
* @param key key.
* @param values values.
*/
void add(K key, HashSet<V> values);
void add(K key, LinkedHashSet<V> values);
/**
* 添加全部Key-List<Value>
@ -51,14 +48,14 @@ public interface MultiValueSetMap<K, V> {
* @param values values.
* @see #set(Object, Object)
*/
void set(K key, HashSet<V> values);
void set(K key, LinkedHashSet<V> values);
/**
* 替换所有的Key-HashSet<Value>
*
* @param values values.
*/
void set(Map<K, HashSet<V>> values);
void set(Map<K, LinkedHashSet<V>> values);
/**
* 移除某一个Key对应的所有值也将被移除
@ -66,7 +63,7 @@ public interface MultiValueSetMap<K, V> {
* @param key key.
* @return value.
*/
HashSet<V> remove(K key);
LinkedHashSet<V> remove(K key);
/**
* 移除所有的值

@ -5,7 +5,7 @@ import android.graphics.BitmapFactory;
import android.util.Base64;
import android.util.Log;
import xyz.fycz.myreader.callback.JsonCallback;
import xyz.fycz.myreader.webapi.callback.JsonCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.entity.JsonModel;
@ -13,8 +13,8 @@ import xyz.fycz.myreader.util.HttpUtil;
import xyz.fycz.myreader.util.RSAUtilV2;
import xyz.fycz.myreader.util.StringHelper;
import com.google.gson.Gson;
import xyz.fycz.myreader.callback.HttpCallback;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.HttpCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import java.io.BufferedReader;
import java.io.File;
@ -34,9 +34,9 @@ public class HttpDataSource {
* @param url
* @param callback
*/
public static void httpGet_html(String url, final String charsetName, final ResultCallback callback){
public static void httpGet_html(String url, final String charsetName, boolean isRefresh, final ResultCallback callback){
Log.d("HttpGet URl", url);
HttpUtil.sendGetRequest_okHttp(url, new HttpCallback() {
HttpUtil.sendGetRequest_okHttp(url, isRefresh, new HttpCallback() {
@Override
public void onFinish(Bitmap bm) {
@ -63,7 +63,8 @@ public class HttpDataSource {
@Override
public void onFinish(String response) {
Log.d("Local", "read finish:" + response);
callback.onFinish(response, 0);
}
@Override

@ -5,7 +5,6 @@ import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.widget.Toolbar;
import androidx.cardview.widget.CardView;
@ -13,7 +12,7 @@ import butterknife.BindView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.util.ShareUtils;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.ToastUtils;

@ -10,9 +10,7 @@ import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.*;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@ -21,22 +19,20 @@ import butterknife.OnClick;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.bumptech.glide.request.RequestOptions;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.MyAlertDialog;
import xyz.fycz.myreader.ui.dialog.SourceExchangeDialog;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.BlurTransformation;
import xyz.fycz.myreader.webapi.crawler.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.creator.ChangeSourceDialog;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
@ -93,6 +89,8 @@ public class BookDetailedActivity extends BaseActivity2 {
private ArrayList<Chapter> mChapters = new ArrayList<>();
private ArrayList<Chapter> mNewestChapters = new ArrayList<>();
private boolean isCollected;
private SourceExchangeDialog mSourceDialog;
private int sourceIndex;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@ -134,8 +132,9 @@ public class BookDetailedActivity extends BaseActivity2 {
mBookService = BookService.getInstance();
mChapterService = ChapterService.getInstance();
aBooks = (ArrayList<Book>) getIntent().getSerializableExtra(APPCONST.SEARCH_BOOK_BEAN);
sourceIndex = getIntent().getIntExtra(APPCONST.SOURCE_INDEX, 0);
if (aBooks != null) {
mBook = aBooks.get(0);
mBook = aBooks.get(sourceIndex);
} else {
mBook = (Book) getIntent().getSerializableExtra(APPCONST.BOOK);
}
@ -177,6 +176,24 @@ public class BookDetailedActivity extends BaseActivity2 {
bookDetailTvOpen.setText("继续阅读");
}
//Dialog
mSourceDialog = new SourceExchangeDialog(this, mBook);
if (aBooks != null && aBooks.size() > 0) {
if (isCollected) {
for (int i = 0; i < aBooks.size(); i++) {
Book book = aBooks.get(i);
if (book.getSource().equals(mBook.getSource())) {
book.setNewestChapterId("true");
sourceIndex = i;
break;
}
}
}else {
aBooks.get(sourceIndex).setNewestChapterId("true");
}
}
mSourceDialog.setABooks(aBooks);
mSourceDialog.setSourceIndex(sourceIndex);
}
@Override
@ -208,6 +225,35 @@ public class BookDetailedActivity extends BaseActivity2 {
});
flOpenBook.setOnClickListener(view -> goReadActivity());
//换源对话框
mSourceDialog.setOnSourceChangeListener((bean, pos) -> {
Book bookTem = new Book(mBook);
bookTem.setChapterUrl(bean.getChapterUrl());
if (!StringHelper.isEmpty(bean.getImgUrl())) {
bookTem.setImgUrl(bean.getImgUrl());
}
if (!StringHelper.isEmpty(bean.getType())) {
bookTem.setType(bean.getType());
}
if (!StringHelper.isEmpty(bean.getDesc())){
bookTem.setDesc(bean.getDesc());
}
bookTem.setSource(bean.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);
}
});
}
@Override
@ -308,7 +354,7 @@ public class BookDetailedActivity extends BaseActivity2 {
mHandler.sendMessage(mHandler.obtainMessage(3));
return;
}
pbLoading.setVisibility(View.GONE);
/*pbLoading.setVisibility(View.GONE);
CharSequence[] sources = new CharSequence[aBooks.size()];
int checkedItem = 0;
for (int i = 0; i < sources.length; i++) {
@ -351,7 +397,8 @@ public class BookDetailedActivity extends BaseActivity2 {
}
dialog1.dismiss();
}).create();
dialog.show();
dialog.show();*/
}
/**
@ -363,14 +410,14 @@ public class BookDetailedActivity extends BaseActivity2 {
if (isCollected) {
mChapters = (ArrayList<Chapter>) mChapterService.findBookAllChapterByBookId(mBook.getId());
}
CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler, new ResultCallback() {
CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler, isChangeSource, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
ArrayList<Chapter> chapters = (ArrayList<Chapter>) o;
mBook.setNewestChapterTitle(chapters.get(chapters.size() - 1).getTitle());
if (isCollected) {
int noReadNum = chapters.size() - mBook.getChapterTotalNum();
mBook.setNoReadNum(Math.max(noReadNum, 0));
mBook.setNewestChapterTitle(chapters.get(chapters.size() - 1).getTitle());
mChapterService.updateAllOldChapterData(mChapters, chapters, mBook.getId());
mBookService.updateEntity(mBook);
if (isChangeSource && SysManager.getSetting().isMatchChapter()) {
@ -435,11 +482,14 @@ public class BookDetailedActivity extends BaseActivity2 {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem isUpdate = menu.findItem(R.id.action_is_update);
MenuItem groupSetting = menu.findItem(R.id.action_group_setting);
if (isCollected) {
isUpdate.setVisible(true);
//groupSetting.setVisible(true);
isUpdate.setChecked(!mBook.getIsCloseUpdate());
} else {
isUpdate.setVisible(false);
groupSetting.setVisible(false);
}
return super.onPrepareOptionsMenu(menu);
}
@ -458,7 +508,7 @@ public class BookDetailedActivity extends BaseActivity2 {
ToastUtils.showWarring("无网络连接!");
return true;
}
pbLoading.setVisibility(View.VISIBLE);
/*pbLoading.setVisibility(View.VISIBLE);
if (aBooks == null) {
ChangeSourceDialog csd = new ChangeSourceDialog(this, mBook);
csd.init(new ResultCallback() {
@ -475,13 +525,11 @@ public class BookDetailedActivity extends BaseActivity2 {
});
} else {
createChangeSourceDia();
}
}*/
mSourceDialog.show();
break;
case R.id.action_reload: //重新加载
mChapters.clear();
mNewestChapters.clear();
initWidget();
processLogic();
mHandler.sendEmptyMessage(1);
break;
case R.id.action_is_update://是否更新
mBook.setIsCloseUpdate(!mBook.getIsCloseUpdate());
@ -492,6 +540,8 @@ public class BookDetailedActivity extends BaseActivity2 {
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
break;
case R.id.action_group_setting:
break;
default:
break;
}

@ -17,10 +17,10 @@ import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.ChangeSourceDialog;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.SourceExchangeDialog;
import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.entity.bookstore.QDBook;
import xyz.fycz.myreader.entity.bookstore.RankBook;
@ -32,8 +32,8 @@ import xyz.fycz.myreader.ui.adapter.BookStoreBookTypeAdapter;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.webapi.BookStoreApi;
import xyz.fycz.myreader.webapi.crawler.FindCrawler;
import xyz.fycz.myreader.webapi.crawler.find.ABC;
import xyz.fycz.myreader.webapi.crawler.base.FindCrawler;
import xyz.fycz.myreader.webapi.crawler.find.QiDianMobileRank;
import xyz.fycz.myreader.widget.RefreshLayout;
import java.util.ArrayList;
@ -62,6 +62,7 @@ public class BookstoreActivity extends BaseActivity2 {
private BookStoreBookAdapter mBookStoreBookAdapter;
private List<Book> bookList = new ArrayList<>();
private SourceExchangeDialog mSourceDia;
private BookType curType;
@ -166,7 +167,16 @@ public class BookstoreActivity extends BaseActivity2 {
goToBookDetail(book);
return;
}
mHandler.sendMessage(mHandler.obtainMessage(3));
mSourceDia = new SourceExchangeDialog(this, book);
mSourceDia.setOnSourceChangeListener((bean, pos1) -> {
Intent intent = new Intent(this, BookDetailedActivity.class);
intent.putExtra(APPCONST.SEARCH_BOOK_BEAN, (ArrayList<Book>) mSourceDia.getaBooks());
intent.putExtra(APPCONST.SOURCE_INDEX, pos1);
BookstoreActivity.this.startActivity(intent);
mSourceDia.dismiss();
});
mSourceDia.show();
/*mHandler.sendMessage(mHandler.obtainMessage(3));
ChangeSourceDialog csd = new ChangeSourceDialog(this, book);
csd.initOneBook(new ResultCallback() {
@Override
@ -183,7 +193,7 @@ public class BookstoreActivity extends BaseActivity2 {
DialogCreator.createTipDialog(BookstoreActivity.this, "未搜索到该书籍,无法进入书籍详情!");
mHandler.sendMessage(mHandler.obtainMessage(4));
}
});
});*/
}
});
}
@ -207,14 +217,14 @@ public class BookstoreActivity extends BaseActivity2 {
* 获取页面数据
*/
private void getData() {
if (findCrawler instanceof ABC) {
if (findCrawler instanceof QiDianMobileRank) {
SharedPreUtils spu = SharedPreUtils.getInstance();
if (spu.getString("qdCookie", "").equals("")) {
((ABC) findCrawler).initCookie(this, new ResultCallback() {
((QiDianMobileRank) findCrawler).initCookie(this, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
spu.putString("qdCookie", (String) o);
mBookTypes = ((ABC) findCrawler).getRankTypes();
mBookTypes = ((QiDianMobileRank) findCrawler).getRankTypes();
curType = mBookTypes.get(0);
mHandler.sendMessage(mHandler.obtainMessage(1));
page = 1;
@ -228,7 +238,7 @@ public class BookstoreActivity extends BaseActivity2 {
}
});
} else {
mBookTypes = ((ABC) findCrawler).getRankTypes();
mBookTypes = ((QiDianMobileRank) findCrawler).getRankTypes();
curType = mBookTypes.get(0);
mHandler.sendMessage(mHandler.obtainMessage(1));
page = 1;
@ -265,8 +275,8 @@ public class BookstoreActivity extends BaseActivity2 {
}
mHandler.sendEmptyMessage(3);
if (findCrawler instanceof ABC) {
((ABC) findCrawler).getRankBooks(curType, new ResultCallback() {
if (findCrawler instanceof QiDianMobileRank) {
((QiDianMobileRank) findCrawler).getRankBooks(curType, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
List<Book> books = new ArrayList<>();

@ -9,7 +9,7 @@ import androidx.viewpager.widget.ViewPager;
import butterknife.BindView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.BaseTabActivity;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.service.BookService;

@ -20,11 +20,11 @@ import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import com.google.android.material.textfield.TextInputLayout;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.backup.UserService;
import xyz.fycz.myreader.model.backup.UserService;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.util.CodeUtil;
import xyz.fycz.myreader.util.CyptoUtils;
import xyz.fycz.myreader.util.ToastUtils;

@ -6,13 +6,11 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.FileProvider;
import androidx.fragment.app.Fragment;
@ -25,12 +23,10 @@ import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.ui.fragment.BookStoreFragment;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.fragment.BookcaseFragment;
import xyz.fycz.myreader.ui.fragment.FindFragment;
import xyz.fycz.myreader.ui.fragment.MineFragment;
import xyz.fycz.myreader.ui.presenter.BookcasePresenter;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.ToastUtils;
@ -39,7 +35,6 @@ import java.util.ArrayList;
import java.util.List;
import static androidx.fragment.app.FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT;
import static xyz.fycz.myreader.application.MyApplication.checkVersionByServer;
/**
* @author fengyue
@ -237,12 +232,14 @@ public class MainActivity extends BaseActivity2 {
mBookcaseFragment.getmBookcasePresenter().cancelEdit();
invalidateOptionsMenu();
return true;
case R.id.action_change_group:
mBookcaseFragment.getmBookcasePresenter()
.showBookGroupMenu(findViewById(R.id.action_change_group), () -> {
case R.id.action_change_group: case R.id.action_group_man:
if (!mBookcaseFragment.getmBookcasePresenter().hasOnGroupChangeListener()) {
mBookcaseFragment.getmBookcasePresenter().addOnGroupChangeListener(() -> {
groupName = SharedPreUtils.getInstance().getString("curBookGroupName", "所有书籍");
getSupportActionBar().setSubtitle(groupName);
});
}
break;
case R.id.action_edit:
invalidateOptionsMenu();
break;

@ -3,6 +3,7 @@ package xyz.fycz.myreader.ui.activity;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.*;
import androidx.appcompat.app.AlertDialog;
@ -15,8 +16,9 @@ import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.creator.MultiChoiceDialog;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MultiChoiceDialog;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
@ -55,6 +57,10 @@ public class MoreSettingActivity extends BaseActivity2 {
LinearLayout mLlCloseRefresh;
@BindView(R.id.more_setting_ll_disable_source)
LinearLayout mLlDisableSource;
@BindView(R.id.more_setting_ll_thread_num)
LinearLayout mLlThreadNum;
@BindView(R.id.more_setting_tv_thread_num)
TextView mTvThreadNum;
@BindView(R.id.more_setting_iv_match_chapter_tip)
ImageView mIvMatchChapterTip;
@BindView(R.id.more_setting_rl_match_chapter)
@ -89,16 +95,20 @@ public class MoreSettingActivity extends BaseActivity2 {
private ArrayList<Book> mBooks;
int booksCount;
CharSequence[] mBooksName;
int threadNum;
//选择禁用更新书籍对话框
private AlertDialog mCloseRefreshDia;
//选择禁用更新书源对话框
private AlertDialog mDisableSourceDia;
//线程选择
private AlertDialog mThreadSelectDia;
//选择一键缓存书籍对话框
private AlertDialog mDownloadAllDia;
//选择清除缓存对话框
private AlertDialog mDeleteCatheDia;
@Override
protected int getContentId() {
return R.layout.activity_more_setting;
@ -115,6 +125,7 @@ public class MoreSettingActivity extends BaseActivity2 {
catheCap = mSetting.getCatheGap();
autoRefresh = mSetting.isRefreshWhenStart();
openBookStore = mSetting.isOpenBookStore();
threadNum = SharedPreUtils.getInstance().getInt("threadNum", 8);
}
@Override
@ -133,6 +144,7 @@ public class MoreSettingActivity extends BaseActivity2 {
} else {
mRlMatchChapterSuitability.setVisibility(View.GONE);
}
mTvThreadNum.setText(getString(R.string.cur_thread_num, threadNum));
}
private void initSwitchStatus() {
@ -261,6 +273,26 @@ public class MoreSettingActivity extends BaseActivity2 {
});
});
mLlThreadNum.setOnClickListener(v -> {
View view = LayoutInflater.from(this).inflate(R.layout.dialog_number_picker, null);
NumberPicker threadPick = view.findViewById(R.id.number_picker);
threadPick.setMaxValue(1024);
threadPick.setMinValue(1);
threadPick.setValue(threadNum);
threadPick.setOnScrollListener((view1, scrollState) -> {
});
mThreadSelectDia = MyAlertDialog.build(this)
.setTitle("搜索线程数")
.setView(view)
.setPositiveButton("确定", (dialog, which) -> {
threadNum = threadPick.getValue();
SharedPreUtils.getInstance().putInt("threadNum", threadNum);
mTvThreadNum.setText(getString(R.string.cur_thread_num, threadNum));
}).setNegativeButton("取消", null)
.show();
});
mRlMatchChapter.setOnClickListener(
(v) -> {
if (isMatchChapter) {
@ -363,20 +395,6 @@ public class MoreSettingActivity extends BaseActivity2 {
});
});
/*mRlBookstore.setOnClickListener(
(v) -> {
if (openBookStore) {
openBookStore = false;
} else {
openBookStore = true;
}
mScBookstore.setChecked(openBookStore);
mSetting.setOpenBookStore(openBookStore);
SysManager.saveSetting(mSetting);
ToastUtils.showInfo("重启后生效!");
}
);*/
}
@Override
@ -388,8 +406,8 @@ public class MoreSettingActivity extends BaseActivity2 {
private void initSpinner() {
// initSwitchStatus() be called earlier than onCreate(), so setSelection() won't work
ArrayAdapter<CharSequence> resetScreenAdapter = ArrayAdapter.createFromResource(this,
R.array.reset_screen_time, R.layout.spinner);
resetScreenAdapter.setDropDownViewResource(R.layout.spinner_item);
R.array.reset_screen_time, android.R.layout.simple_spinner_item);
resetScreenAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mScResetScreen.setAdapter(resetScreenAdapter);
int resetScreenSelection = 0;
@ -439,8 +457,8 @@ public class MoreSettingActivity extends BaseActivity2 {
ArrayAdapter<CharSequence> matchSuiAdapter = ArrayAdapter.createFromResource(this,
R.array.match_chapter_suitability, R.layout.spinner);
matchSuiAdapter.setDropDownViewResource(R.layout.spinner_item);
R.array.match_chapter_suitability, android.R.layout.simple_spinner_item);
matchSuiAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mScMatchChapterSuitability.setAdapter(matchSuiAdapter);
if (matchChapterSuitability == 0.0) {
@ -467,8 +485,8 @@ public class MoreSettingActivity extends BaseActivity2 {
ArrayAdapter<CharSequence> catheGapAdapter = ArrayAdapter.createFromResource(this,
R.array.cathe_chapter_gap, R.layout.spinner);
catheGapAdapter.setDropDownViewResource(R.layout.spinner_item);
R.array.cathe_chapter_gap, android.R.layout.simple_spinner_item);
catheGapAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
mScCatheGap.setAdapter(catheGapAdapter);
if (catheCap == 0) {

@ -20,7 +20,7 @@ import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.presenter.ReadPresenter;
import xyz.fycz.myreader.widget.page.PageView;

@ -3,7 +3,6 @@ package xyz.fycz.myreader.ui.activity;
import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.text.Editable;
@ -18,10 +17,10 @@ import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import com.google.android.material.textfield.TextInputLayout;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.backup.UserService;
import xyz.fycz.myreader.model.backup.UserService;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.util.CodeUtil;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.StringUtils;

@ -3,7 +3,6 @@ package xyz.fycz.myreader.ui.activity;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
@ -15,33 +14,38 @@ import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.*;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import me.gujun.android.taggroup.TagGroup;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.base.BaseActivity2;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.custom.DragSortGridView;
import xyz.fycz.myreader.model.SearchEngine;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.SearchHistory;
import xyz.fycz.myreader.greendao.service.SearchHistoryService;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.ui.adapter.SearchBookAdapter;
import xyz.fycz.myreader.ui.adapter.SearchHistoryAdapter;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.webapi.CommonApi;
import xyz.fycz.myreader.webapi.crawler.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.widget.RefreshProgressBar;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* @author fengyue
@ -54,12 +58,12 @@ public class SearchBookActivity extends BaseActivity2 {
TextView tvSearchConform;
@BindView(R.id.ll_refresh_suggest_books)
LinearLayout llRefreshSuggestBooks;
@BindView(R.id.gv_search_books_list)
DragSortGridView gvSearchBooksList;
@BindView(R.id.rv_search_books_list)
RecyclerView rvSearchBooksList;
@BindView(R.id.ll_suggest_books_view)
LinearLayout llSuggestBooksView;
@BindView(R.id.pb_loading)
ProgressBar pbLoading;
@BindView(R.id.rpb)
RefreshProgressBar rpb;
@BindView(R.id.lv_history_list)
ListView lvHistoryList;
@BindView(R.id.ll_clear_history)
@ -74,13 +78,14 @@ public class SearchBookActivity extends BaseActivity2 {
TextView renewByText;
@BindView(R.id.srl_search_book_list)
SmartRefreshLayout srlSearchBookList;
@BindView(R.id.fabSearchStop)
FloatingActionButton fabSearchStop;
private SearchBookAdapter mSearchBookAdapter;
private String searchKey;//搜索关键字
private ArrayList<SearchBookBean> mBooksBean = new ArrayList<>();
private ConcurrentMultiValueMap<SearchBookBean, Book> mBooks = new ConcurrentMultiValueMap<>();
private ArrayList<Book> mAdapterBooks = new ArrayList<>();
private ArrayList<SearchHistory> mSearchHistories = new ArrayList<>();
private ArrayList<String> mSuggestions = new ArrayList<>();
@ -97,6 +102,8 @@ public class SearchBookActivity extends BaseActivity2 {
private int inputConfirm = 0;//搜索输入确认
private int confirmTime = 1000;//搜索输入确认时间(毫秒)
private SearchEngine searchEngine;
private static String[] suggestion = {"第一序列", "大道朝天", "伏天氏", "终极斗罗", "我师兄实在太稳健了", "烂柯棋缘", "诡秘之主"};
private static String[] suggestion2 = {"不朽凡人", "圣墟", "我是至尊", "龙王传说", "太古神王", "一念永恒", "雪鹰领主", "大主宰"};
@ -109,18 +116,21 @@ public class SearchBookActivity extends BaseActivity2 {
search();
break;
case 2:
if (curThreadCount == 0 && !isStopSearch) {
initSearchList();
if (srlSearchBookList != null) {
srlSearchBookList.finishRefresh();
pbLoading.setVisibility(View.GONE);
}/*else {
notifyDataSetChanged();
mSearchBookActivity.getSrlSearchBookList().finishRefresh();
}*/
}
if (curThreadCount == 0 && !isStopSearch) {
rpb.setIsAutoLoading(false);
}
break;
case 3:
fabSearchStop.setVisibility(View.GONE);
break;
}
}
};
@Override
protected int getContentId() {
return R.layout.activity_search_book;
@ -140,6 +150,36 @@ public class SearchBookActivity extends BaseActivity2 {
for (int i = 0; i < suggestion.length; i++) {
mSuggestions.add(suggestion[i]);
}
searchEngine = new SearchEngine();
searchEngine.setOnSearchListener(new SearchEngine.OnSearchListener() {
@Override
public void loadMoreFinish(Boolean isAll) {
if (rpb != null) {
rpb.setIsAutoLoading(false);
}
mHandler.sendEmptyMessage(3);
}
@Override
public void loadMoreSearchBook(ConcurrentMultiValueMap<SearchBookBean, Book> items) {
mBooks.addAll(items);
curThreadCount--;
mSearchBookAdapter.addAll(new ArrayList<>(items.keySet()), searchKey);
mHandler.sendMessage(mHandler.obtainMessage(2));
}
@Override
public void loadMoreSearchBook(List<Book> items) {
}
@Override
public void searchBookError(Throwable throwable) {
curThreadCount = 0;
mHandler.sendMessage(mHandler.obtainMessage(2));
}
});
}
@Override
@ -177,11 +217,13 @@ public class SearchBookActivity extends BaseActivity2 {
}
});
rvSearchBooksList.setLayoutManager(new LinearLayoutManager(this));
//上拉刷新
srlSearchBookList.setOnRefreshListener(refreshLayout -> mHandler.sendMessage(mHandler.obtainMessage(1)));
srlSearchBookList.setEnableRefresh(false);
gvSearchBooksList.setNumColumns(1);
gvSearchBooksList.setDragModel(-1);
srlSearchBookList.setOnRefreshListener(refreshLayout -> {
stopSearch();
mHandler.sendMessage(mHandler.obtainMessage(1));
});
initSuggestionBook();
initHistoryList();
}
@ -194,12 +236,7 @@ public class SearchBookActivity extends BaseActivity2 {
renewByImage.setOnClickListener(new RenewSuggestionBook());
//换一批点击事件
renewByText.setOnClickListener(new RenewSuggestionBook());
//进入书籍详情页
gvSearchBooksList.setOnItemClickListener((adapterView, view, i, l) -> {
Intent intent = new Intent(this, BookDetailedActivity.class);
intent.putExtra(APPCONST.SEARCH_BOOK_BEAN, new ArrayList<>(mBooks.getValues(mBooksBean.get(i))));
startActivity(intent);
});
//搜索按钮点击事件
tvSearchConform.setOnClickListener(view -> mHandler.sendMessage(mHandler.obtainMessage(1)));
//suggestion搜索事件
@ -227,6 +264,10 @@ public class SearchBookActivity extends BaseActivity2 {
}
return true;
});
fabSearchStop.setOnClickListener(v -> {
stopSearch();
});
}
@ -267,105 +308,69 @@ public class SearchBookActivity extends BaseActivity2 {
* 初始化搜索列表
*/
private void initSearchList() {
initmBooksBean();
mSearchBookAdapter = new SearchBookAdapter(this,
R.layout.listview_search_book_item, mBooksBean, mBooks);
gvSearchBooksList.setAdapter(mSearchBookAdapter);
gvSearchBooksList.setVisibility(View.VISIBLE);
//initmBooksBean();
rvSearchBooksList.setVisibility(View.VISIBLE);
llSuggestBooksView.setVisibility(View.GONE);
llSuggestBooksView.setVisibility(View.GONE);
}
/**
* 更新搜索列表
*/
private void notifyDataSetChanged() {
if (curThreadCount == 0) {
pbLoading.setVisibility(View.GONE);
}
initmBooksBean();
}
/**
* 初始化mBooksBean
*/
private void initmBooksBean() {
synchronized (this) {
mBooksBean.clear();
mBooksBean.addAll(mBooks.keySet());
//排序,基于最符合关键字的搜书结果应该是最短的
//TODO ;这里只做了简单的比较排序,还需要继续完善
Collections.sort(mBooksBean, (o1, o2) -> {
if (searchKey.equals(o1.getName()))
return -1;
if (searchKey.equals(o2.getName()))
return 1;
if (searchKey.equals(o1.getAuthor()))
return -1;
if (searchKey.equals(o2.getAuthor()))
return 1;
if (o1.getName().length() < o2.getName().length())
return -1;
if (o1.getName().length() == o2.getName().length())
return 0;
return 1;
});
/*MyApplication.runOnUiThread(new Runnable() {
@Override
public void run() {
mSearchBookAdapter.notifyDataSetChanged();
}
});*/
}
}
/**
* 获取搜索数据
*/
private void getData() {
initSearchList();
mBooksBean.clear();
mBooks.clear();
ArrayList<ReadCrawler> readCrawlers = ReadCrawlerUtil.getReadCrawlers();
allThreadCount = readCrawlers.size();
if (allThreadCount == 0) {
ToastUtils.showWarring("当前书源已全部禁用,无法搜索!");
pbLoading.setVisibility(View.GONE);
rpb.setIsAutoLoading(false);
return;
}
curThreadCount = allThreadCount;
for (ReadCrawler readCrawler : readCrawlers){
/*for (ReadCrawler readCrawler : readCrawlers) {
searchBookByCrawler(readCrawler, readCrawler.getSearchCharset());
}
}*/
searchEngine.initSearchEngine(ReadCrawlerUtil.getReadCrawlers());
searchEngine.search(searchKey);
}
/**
* 搜索
*/
private void search() {
pbLoading.setVisibility(View.VISIBLE);
rpb.setIsAutoLoading(true);
fabSearchStop.setVisibility(View.VISIBLE);
if (StringHelper.isEmpty(searchKey)) {
isStopSearch = true;
pbLoading.setVisibility(View.GONE);
gvSearchBooksList.setVisibility(View.GONE);
stopSearch();
rpb.setIsAutoLoading(false);
rvSearchBooksList.setVisibility(View.GONE);
llSuggestBooksView.setVisibility(View.VISIBLE);
initHistoryList();
gvSearchBooksList.setAdapter(null);
rvSearchBooksList.setAdapter(null);
srlSearchBookList.setEnableRefresh(false);
} else {
isStopSearch = false;
gvSearchBooksList.setVisibility(View.VISIBLE);
mSearchBookAdapter = new SearchBookAdapter(mBooks);
rvSearchBooksList.setAdapter(mSearchBookAdapter);
//进入书籍详情页
mSearchBookAdapter.setOnItemClickListener((view, pos) -> {
Intent intent = new Intent(this, BookDetailedActivity.class);
intent.putExtra(APPCONST.SEARCH_BOOK_BEAN, new ArrayList<>(mBooks.getValues(mSearchBookAdapter.getItem(pos))));
startActivity(intent);
});
srlSearchBookList.setEnableRefresh(true);
rvSearchBooksList.setVisibility(View.VISIBLE);
llSuggestBooksView.setVisibility(View.GONE);
llHistoryView.setVisibility(View.GONE);
getData();
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (gvSearchBooksList.getmScrollView().getScrollY() == 0){
srlSearchBookList.setEnableRefresh(true);
}
//滑动监听器
gvSearchBooksList.getmScrollView().setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
srlSearchBookList.setEnableRefresh(scrollY == 0);
});
}
mSearchHistoryService.addOrUpadteHistory(searchKey);
//收起软键盘
InputMethodManager imm = (InputMethodManager) MyApplication.getmContext().getSystemService(Context.INPUT_METHOD_SERVICE);
@ -374,6 +379,11 @@ public class SearchBookActivity extends BaseActivity2 {
}
}
private void stopSearch() {
searchEngine.stopSearch();
mHandler.sendEmptyMessage(3);
}
@Override
public void onBackPressed() {
if (StringHelper.isEmpty(searchKey)) {
@ -413,7 +423,11 @@ public class SearchBookActivity extends BaseActivity2 {
@Override
protected void onDestroy() {
super.onDestroy();
isStopSearch = true;
stopSearch();
for (int i = 0; i < 9; i++) {
mHandler.removeMessages(i + 1);
}
super.onDestroy();
}
}

@ -1,40 +1,10 @@
package xyz.fycz.myreader.ui.adapter;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.adapter.BaseListAdapter;
import xyz.fycz.myreader.base.adapter.IViewHolder;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.custom.DragAdapter;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.ui.activity.BookDetailedActivity;
import xyz.fycz.myreader.ui.adapter.holder.BookStoreBookHolder;
import xyz.fycz.myreader.webapi.crawler.BiQuGeReadCrawler;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.CommonApi;
import java.util.List;
import xyz.fycz.myreader.ui.adapter.holder.BookStoreBookHolder;
public class BookStoreBookAdapter extends BaseListAdapter<Book> {

@ -4,8 +4,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.widget.*;
import androidx.appcompat.app.AlertDialog;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
@ -17,9 +15,9 @@ import java.util.*;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.creator.MyAlertDialog;
import xyz.fycz.myreader.custom.DragAdapter;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.widget.custom.DragAdapter;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.service.BookService;
@ -122,7 +120,7 @@ public abstract class BookcaseAdapter extends DragAdapter {
}, null);
}else {
DialogCreator.createCommonDialog(mContext, "删除/移除书籍", "您是希望删除《" + book.getName() + "》及其所有缓存还是从分组中移除该书籍(不会删除书籍)呢?",
true, "删除书籍", "从分组中移除书籍",(dialogInterface, i) -> {
true, "删除书籍", "从分组中移除",(dialogInterface, i) -> {
remove(book);
ToastUtils.showSuccess("书籍删除成功!");
mBookcasePresenter.init();

@ -5,7 +5,6 @@ import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
@ -21,8 +20,8 @@ import java.util.ArrayList;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.creator.MyAlertDialog;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.ui.activity.BookDetailedActivity;
import xyz.fycz.myreader.ui.activity.ReadActivity;

@ -1,12 +1,10 @@
package xyz.fycz.myreader.ui.adapter;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import androidx.appcompat.app.AlertDialog;
@ -20,8 +18,8 @@ import java.util.ArrayList;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.creator.MyAlertDialog;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.ui.activity.BookDetailedActivity;
import xyz.fycz.myreader.ui.activity.ReadActivity;

@ -27,7 +27,7 @@ import java.util.ArrayList;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.entity.Setting;

@ -1,191 +1,81 @@
package xyz.fycz.myreader.ui.adapter;
import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import xyz.fycz.myreader.R;
import android.text.TextUtils;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.crawler.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.custom.DragAdapter;
import xyz.fycz.myreader.base.adapter.BaseListAdapter;
import xyz.fycz.myreader.base.adapter.IViewHolder;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.CommonApi;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.ui.adapter.holder.SearchBookHolder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* Created by zhao on 2017/7/26.
* @author fengyue
* @date 2020/10/2 10:08
*/
public class SearchBookAdapter extends DragAdapter {
private int mResourceId;
private Context mContext;
public class SearchBookAdapter extends BaseListAdapter<SearchBookBean> {
private ConcurrentMultiValueMap<SearchBookBean, Book> mBooks;
private ArrayList<SearchBookBean> mSearchBookBeans;
private ArrayList<SearchBookBean> mTempSearchBookBeans = new ArrayList<>();
private HashMap<SearchBookBean, Book> mTempBooks = new HashMap<>();
private Handler mHandle = new Handler(message -> {
switch (message.what) {
case 1:
ViewHolder holder = (ViewHolder) message.obj;
int pos = message.arg1;
SearchBookAdapter.this.initOtherInfo(pos, holder);
break;
}
return false;
});
public SearchBookAdapter(Context context, int resourceId, ArrayList<SearchBookBean> datas,
ConcurrentMultiValueMap<SearchBookBean, Book> mBooks){
mContext = context;
mSearchBookBeans = datas;
mResourceId = resourceId;
public SearchBookAdapter(ConcurrentMultiValueMap<SearchBookBean, Book> mBooks) {
this.mBooks = mBooks;
}
@Override
public int getCount() {
return mSearchBookBeans.size();
protected IViewHolder<SearchBookBean> createViewHolder(int viewType) {
return new SearchBookHolder(mBooks);
}
@Override
public SearchBookBean getItem(int position) {
return mSearchBookBeans.get(position);
public synchronized void addAll(List<SearchBookBean> newDataS, String keyWord) {
List<SearchBookBean> copyDataS = mList;
if (newDataS != null && newDataS.size() > 0) {
List<SearchBookBean> searchBookBeansAdd = new ArrayList<>();
if (copyDataS.size() == 0) {
copyDataS.addAll(newDataS);
} else {
//存在
for (SearchBookBean temp : newDataS) {
boolean hasSame = false;
for (int i = 0, size = copyDataS.size(); i < size; i++) {
SearchBookBean searchBook = copyDataS.get(i);
if (TextUtils.equals(temp.getName(), searchBook.getName())
&& TextUtils.equals(temp.getAuthor(), searchBook.getAuthor())) {
hasSame = true;
break;
}
@Override
public long getItemId(int position) {
return 0;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null){
viewHolder = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(mResourceId,null);
viewHolder.ivBookImg = convertView.findViewById(R.id.iv_book_img);
viewHolder.tvBookName = convertView.findViewById(R.id.tv_book_name);
viewHolder.tvAuthor = convertView.findViewById(R.id.tv_book_author);
viewHolder.tvDesc = convertView.findViewById(R.id.tv_book_desc);
viewHolder.tvType = convertView.findViewById(R.id.tv_book_type);
viewHolder.tvSource = convertView.findViewById(R.id.tv_book_source);
viewHolder.tvNewestChapter = convertView.findViewById(R.id.tv_book_newest_chapter);
//viewHolder.tvNewestChapter = convertView.findViewById(R.id.tv_book_newest_chapter);
convertView.setTag(viewHolder);
}else {
viewHolder = (ViewHolder) convertView.getTag();
if (!hasSame) {
searchBookBeansAdd.add(temp);
}
initView(position,viewHolder);
return convertView;
}
private void initView(final int position, final ViewHolder viewHolder){
List<Book> aBooks = mBooks.getValues(getItem(position));
int bookCount = aBooks.size();
Book book = aBooks.get(0);
SearchBookBean ssb = new SearchBookBean(book.getName(), book.getAuthor());
//判断是否已经加载,防止多次加载
/*for (SearchBookBean temp : mTempSearchBookBeans){//已加载
if (ssb.equals(temp)){
book = mTempBooks.get(ssb);
//添加
for (SearchBookBean temp : searchBookBeansAdd) {
if (TextUtils.equals(keyWord, temp.getName())) {
for (int i = 0; i < copyDataS.size(); i++) {
SearchBookBean searchBook = copyDataS.get(i);
if (!TextUtils.equals(keyWord, searchBook.getName())) {
copyDataS.add(i, temp);
break;
}
}*/
if (StringHelper.isEmpty(book.getImgUrl())){
book.setImgUrl("");
}
viewHolder.tvBookName.setText(book.getName());
viewHolder.tvNewestChapter.setText("最新章节:" + book.getNewestChapterTitle());
viewHolder.tvAuthor.setText(book.getAuthor());
viewHolder.tvSource.setText("书源:" + BookSource.fromString(book.getSource()).text
+ " 共" + bookCount + "个源");
viewHolder.tvDesc.setText("");
viewHolder.tvType.setText("");
ReadCrawler rc = ReadCrawlerUtil.getReadCrawler(book.getSource());
if (rc instanceof BookInfoCrawler && StringHelper.isEmpty(book.getImgUrl())){
BookInfoCrawler bic = (BookInfoCrawler) rc;
CommonApi.getBookInfo(book, bic, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
mHandle.sendMessage(mHandle.obtainMessage(1, position,0,viewHolder));
}
@Override
public void onError(Exception e) {
} else if (TextUtils.equals(keyWord, temp.getAuthor())) {
for (int i = 0; i < copyDataS.size(); i++) {
SearchBookBean searchBook = copyDataS.get(i);
if (!TextUtils.equals(keyWord, searchBook.getName()) && !TextUtils.equals(keyWord, searchBook.getAuthor())) {
copyDataS.add(i, temp);
break;
}
}
});
} else {
initOtherInfo(position, viewHolder);
}
//viewHolder.tvNewestChapter.setText(book.getNewestChapterTitle());
//添加已经加载的书籍
/*mTempSearchBookBeans.add(ssb);
mTempBooks.put(ssb, book);*/
}
private void initOtherInfo(final int position, final ViewHolder holder){
Book book = mBooks.getValue(getItem(position), 0);
//图片
if (!MyApplication.isDestroy((Activity) mContext)) {
Glide.with(mContext)
.load(book.getImgUrl())
// .override(DipPxUtil.dip2px(getContext(), 80), DipPxUtil.dip2px(getContext(), 150))
.error(R.mipmap.no_image)
.placeholder(R.mipmap.no_image)
//设置圆角
.apply(RequestOptions.bitmapTransform(new RoundedCorners(8)))
.into(holder.ivBookImg);
}
//简介
holder.tvDesc.setText("简介:" + book.getDesc());
holder.tvType.setText(book.getType());
copyDataS.add(temp);
}
@Override
public void onDataModelMove(int from, int to) {
}
class ViewHolder{
ImageView ivBookImg;
TextView tvBookName;
TextView tvDesc;
TextView tvAuthor;
TextView tvType;
TextView tvSource;
TextView tvNewestChapter;
//TextView tvNewestChapter;
}
MyApplication.runOnUiThread(this::notifyDataSetChanged);
}
}
}

@ -0,0 +1,19 @@
package xyz.fycz.myreader.ui.adapter;
import xyz.fycz.myreader.base.adapter.BaseListAdapter;
import xyz.fycz.myreader.base.adapter.IViewHolder;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.ui.adapter.holder.SourceExchangeHolder;
import java.util.List;
/**
* @author fengyue
* @date 2020/9/30 18:42
*/
public class SourceExchangeAdapter extends BaseListAdapter<Book> {
@Override
protected IViewHolder createViewHolder(int viewType) {
return new SourceExchangeHolder();
}
}

@ -0,0 +1,134 @@
package xyz.fycz.myreader.ui.adapter.holder;
import android.app.Activity;
import android.os.Handler;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.bumptech.glide.request.RequestOptions;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.base.adapter.ViewHolderImpl;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.CommonApi;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.List;
/**
* @author fengyue
* @date 2020/10/2 10:10
*/
public class SearchBookHolder extends ViewHolderImpl<SearchBookBean> {
private ConcurrentMultiValueMap<SearchBookBean, Book> mBooks;
private Handler mHandle = new Handler(message -> {
switch (message.what) {
case 1:
Book book = (Book) message.obj;
initOtherInfo(book);
break;
}
return false;
});
public SearchBookHolder(ConcurrentMultiValueMap<SearchBookBean, Book> mBooks) {
this.mBooks = mBooks;
}
ImageView ivBookImg;
TextView tvBookName;
TextView tvDesc;
TextView tvAuthor;
TextView tvType;
TextView tvSource;
TextView tvNewestChapter;
@Override
protected int getItemLayoutId() {
return R.layout.listview_search_book_item;
}
@Override
public void initView() {
ivBookImg = findById(R.id.iv_book_img);
tvBookName = findById(R.id.tv_book_name);
tvAuthor = findById(R.id.tv_book_author);
tvDesc = findById(R.id.tv_book_desc);
tvType = findById(R.id.tv_book_type);
tvSource = findById(R.id.tv_book_source);
tvNewestChapter = findById(R.id.tv_book_newest_chapter);
}
@Override
public void onBind(SearchBookBean data, int pos) {
List<Book> aBooks = mBooks.getValues(data);
int bookCount = aBooks.size();
Book book = aBooks.get(0);
if (StringHelper.isEmpty(book.getImgUrl())){
book.setImgUrl("");
}
tvBookName.setText(book.getName());
tvNewestChapter.setText(getContext().getString(R.string.newest_chapter, book.getNewestChapterTitle()));
tvAuthor.setText(book.getAuthor());
tvSource.setText(getContext().getString(R.string.source_title_num, BookSource.fromString(book.getSource()).text, bookCount));
tvDesc.setText("");
tvType.setText("");
ReadCrawler rc = ReadCrawlerUtil.getReadCrawler(book.getSource());
if (rc instanceof BookInfoCrawler){
if (tvBookName.getTag() == null || !(Boolean) tvBookName.getTag()) {
tvBookName.setTag(true);
} else {
initOtherInfo(book);
return;
}
Log.i(book.getName(), "initOtherInfo");
BookInfoCrawler bic = (BookInfoCrawler) rc;
CommonApi.getBookInfo(book, bic, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
mHandle.sendMessage(mHandle.obtainMessage(1, pos, 0, book));
}
@Override
public void onError(Exception e) {
tvBookName.setTag(false);
}
});
}else {
initOtherInfo(book);
}
}
private void initOtherInfo(Book book){
//图片
if (!MyApplication.isDestroy((Activity) getContext())) {
Glide.with(getContext())
.load(book.getImgUrl())
// .override(DipPxUtil.dip2px(getContext(), 80), DipPxUtil.dip2px(getContext(), 150))
.error(R.mipmap.no_image)
.placeholder(R.mipmap.no_image)
//设置圆角
.apply(RequestOptions.bitmapTransform(new RoundedCorners(8)))
.into(ivBookImg);
}
//简介
if (book.getDesc() == null) {
tvDesc.setText("");
}else {
tvDesc.setText("简介:" + book.getDesc());
}
tvType.setText(book.getType());
tvNewestChapter.setText("最新章节:" + book.getNewestChapterTitle());
tvAuthor.setText(book.getAuthor());
}
}

@ -0,0 +1,40 @@
package xyz.fycz.myreader.ui.adapter.holder;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.adapter.ViewHolderImpl;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
/**
* @author fengyue
* @date 2020/9/30 18:43
*/
public class SourceExchangeHolder extends ViewHolderImpl<Book> {
TextView sourceTvTitle;
TextView sourceTvChapter;
ImageView sourceIv;
@Override
protected int getItemLayoutId() {
return R.layout.item_change_source;
}
@Override
public void initView() {
sourceTvTitle = findById(R.id.tv_source_name);
sourceTvChapter = findById(R.id.tv_lastChapter);
sourceIv = findById(R.id.iv_checked);
}
@Override
public void onBind(Book data, int pos) {
sourceTvTitle.setText(BookSource.fromString(data.getSource()).text);
sourceTvChapter.setText(data.getNewestChapterTitle());
if (Boolean.parseBoolean(data.getNewestChapterId()))
sourceIv.setVisibility(View.VISIBLE);
else
sourceIv.setVisibility(View.GONE);
}
}

@ -1,4 +1,4 @@
package xyz.fycz.myreader.creator;
package xyz.fycz.myreader.ui.dialog;
import android.annotation.SuppressLint;
@ -9,7 +9,7 @@ import android.os.Message;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.ui.activity.MainActivity;
import xyz.fycz.myreader.ui.fragment.BookcaseFragment;

@ -1,17 +1,19 @@
package xyz.fycz.myreader.creator;
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.MyApplication;
import xyz.fycz.myreader.callback.ResultCallback;
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.mulvalmap.ConcurrentMultiValueMap;
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;

@ -1,6 +1,5 @@
package xyz.fycz.myreader.creator;
package xyz.fycz.myreader.ui.dialog;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
@ -18,7 +17,6 @@ import android.widget.*;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatImageView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
@ -28,7 +26,6 @@ import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.enums.ReadStyle;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.service.BookService;
import xyz.fycz.myreader.util.BrightUtil;
import xyz.fycz.myreader.util.IOUtils;
import xyz.fycz.myreader.util.StringHelper;
@ -384,6 +381,9 @@ public class DialogCreator {
intent.putExtra(APPCONST.BOOK, mBook);
context.startActivity(intent);*/
});
if ("本地书籍".equals(mBook.getType())){
ivChangeSource.setVisibility(View.GONE);
}
//换源
ivChangeSource.setOnClickListener(changeSourceListener);

@ -1,11 +1,10 @@
package xyz.fycz.myreader.creator;
package xyz.fycz.myreader.ui.dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.widget.Button;
import android.widget.ListView;
import androidx.appcompat.app.AlertDialog;
import xyz.fycz.myreader.R;
/**
* @author fengyue
@ -25,7 +24,6 @@ public class MultiChoiceDialog {
int itemsCount = checkedItems.length;
AlertDialog multiChoiceDialog = MyAlertDialog.build(context)
.setTitle(title)
.setCancelable(false)
.setMultiChoiceItems(items, checkedItems, (dialog, which, isChecked) -> {
if (onMultiDialogListener != null) {
onMultiDialogListener.onItemClick(dialog, which, isChecked);

@ -1,4 +1,4 @@
package xyz.fycz.myreader.creator;
package xyz.fycz.myreader.ui.dialog;
import android.content.Context;
import androidx.appcompat.app.AlertDialog;

@ -0,0 +1,222 @@
package xyz.fycz.myreader.ui.dialog;
import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.model.SearchEngine;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.ui.adapter.SourceExchangeAdapter;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.widget.RefreshProgressBar;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Zhouas666 on 2019-04-11
* Github: https://github.com/zas023
* <p>
* 换源dialog
*/
public class SourceExchangeDialog extends Dialog {
private static final String TAG = "SourceExchangeDialog";
/*@BindView(R.id.dialog_tv_title)
TextView dialogTvTitle;*/
@BindView(R.id.toolbar)
Toolbar toolbar;
@BindView(R.id.iv_refresh_search)
AppCompatImageView ivRefreshSearch;
@BindView(R.id.iv_stop_search)
AppCompatImageView ivStopSearch;
@BindView(R.id.rpb)
RefreshProgressBar rpb;
@BindView(R.id.dialog_rv_content)
RecyclerView dialogRvContent;
private SearchEngine searchEngine;
private SourceExchangeAdapter mAdapter;
private OnSourceChangeListener listener;
private Activity mActivity;
private Book mShelfBook;
private List<Book> aBooks;
private AlertDialog mErrorDia;
private int sourceIndex = -1;
/***************************************************************************/
public SourceExchangeDialog(@NonNull Activity activity, Book bookBean) {
super(activity);
mActivity = activity;
mShelfBook = bookBean;
}
public void setShelfBook(Book mShelfBook) {
this.mShelfBook = mShelfBook;
}
public void setABooks(List<Book> aBooks){
this.aBooks = aBooks;
}
public void setSourceIndex(int sourceIndex){
this.sourceIndex = sourceIndex;
}
public void setOnSourceChangeListener(OnSourceChangeListener listener) {
this.listener = listener;
}
public List<Book> getaBooks(){return aBooks;}
/*****************************Initialization********************************/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_book_source);
ButterKnife.bind(this);
setUpWindow();
initData();
initClick();
}
@Override
protected void onStart() {
super.onStart();
//执行业务逻辑
if (aBooks.size() == 0) {
searchEngine.search(mShelfBook.getName(), mShelfBook.getAuthor());
ivStopSearch.setVisibility(View.VISIBLE);
rpb.setIsAutoLoading(true);
}else {
if (mAdapter.getItemCount() == 0) {
mAdapter.addItems(aBooks);
}
}
}
/**
* 设置Dialog显示的位置
*/
private void setUpWindow() {
Window window = getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.CENTER;
window.setAttributes(lp);
}
/**
* 初始化数据
*/
private void initData() {
toolbar.setTitle(mShelfBook.getName());
toolbar.setSubtitle(mShelfBook.getAuthor());
//dialogTvTitle.setText(mShelfBook.getName() + "(" + mShelfBook.getAuthor() + ")");
if (aBooks == null) {
aBooks = new ArrayList<>();
}
mAdapter = new SourceExchangeAdapter();
dialogRvContent.setLayoutManager(new LinearLayoutManager(mActivity));
dialogRvContent.setAdapter(mAdapter);
searchEngine = new SearchEngine();
searchEngine.initSearchEngine(ReadCrawlerUtil.getReadCrawlers());
}
private void initClick() {
searchEngine.setOnSearchListener(new SearchEngine.OnSearchListener() {
@Override
public void loadMoreFinish(Boolean isAll) {
synchronized (RefreshProgressBar.class) {
rpb.setIsAutoLoading(false);
ivStopSearch.setVisibility(View.GONE);
}
}
@Override
public void loadMoreSearchBook(ConcurrentMultiValueMap<SearchBookBean, Book> items) {
}
@Override
public void loadMoreSearchBook(List<Book> items) {
//确保只有一个结果
if (items != null && items.size() != 0) {
Book bean = items.get(0);
if (bean.getSource().equals(mShelfBook.getSource())) {
bean.setNewestChapterId("true");
sourceIndex = mAdapter.getItemSize();
}
mAdapter.addItem(items.get(0));
aBooks.add(bean);
}
}
@Override
public void searchBookError(Throwable throwable) {
dismiss();
DialogCreator.createTipDialog(mActivity, "未搜索到该书籍,书源加载失败!");
}
});
mAdapter.setOnItemClickListener((view, pos) -> {
if (listener == null) return;
Book newBook = mAdapter.getItem(pos);
if (mShelfBook.getSource() == null) {
listener.onSourceChanged(newBook, pos);
searchEngine.stopSearch();
return;
}
if (mShelfBook.getSource().equals(newBook.getSource())) return;
mShelfBook = newBook;
listener.onSourceChanged(newBook, pos);
mAdapter.getItem(pos).setNewestChapterId("true");
if (sourceIndex > -1)
mAdapter.getItem(sourceIndex).setNewestChapterId("false");
sourceIndex = pos;
mAdapter.notifyDataSetChanged();
dismiss();
});
ivStopSearch.setOnClickListener(v -> searchEngine.stopSearch());
ivRefreshSearch.setOnClickListener(v -> {
searchEngine.stopSearch();
ivStopSearch.setVisibility(View.VISIBLE);
mAdapter.clear();
aBooks.clear();
mAdapter.notifyDataSetChanged();
searchEngine.search(mShelfBook.getName(), mShelfBook.getAuthor());
});
}
/**************************Interface**********************************/
public interface OnSourceChangeListener {
void onSourceChanged(Book bean, int pos);
}
}

@ -2,13 +2,9 @@ package xyz.fycz.myreader.ui.fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.CookieManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
@ -17,10 +13,7 @@ import butterknife.ButterKnife;
import butterknife.Unbinder;
import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.custom.DragSortGridView;
import xyz.fycz.myreader.ui.presenter.BookStorePresenter;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.widget.RefreshLayout;
/**

@ -3,7 +3,6 @@ package xyz.fycz.myreader.ui.fragment;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -12,8 +11,7 @@ import android.widget.*;
import androidx.fragment.app.Fragment;
import com.scwang.smartrefresh.layout.SmartRefreshLayout;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.custom.DragSortGridView;
import xyz.fycz.myreader.widget.custom.DragSortGridView;
import butterknife.ButterKnife;
import butterknife.BindView;

@ -7,9 +7,9 @@ import xyz.fycz.myreader.R;
import xyz.fycz.myreader.base.BaseFragment;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.ui.activity.BookstoreActivity;
import xyz.fycz.myreader.webapi.crawler.FindCrawler;
import xyz.fycz.myreader.webapi.crawler.QB5ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.find.ABC;
import xyz.fycz.myreader.webapi.crawler.base.FindCrawler;
import xyz.fycz.myreader.webapi.crawler.read.QB5ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.find.QiDianMobileRank;
/**
* @author fengyue
@ -37,10 +37,10 @@ public class FindFragment extends BaseFragment {
@Override
protected void initClick() {
super.initClick();
mRlQiDianTop.setOnClickListener(v -> comeToBookstore(new ABC(false)));
mRlQiDianNSTop.setOnClickListener(v -> comeToBookstore(new ABC(true)));
mRlQiDianSort.setOnClickListener(v -> comeToBookstore(new ABC(false, true)));
mRlQiDianNSSort.setOnClickListener(v -> comeToBookstore(new ABC(true, true)));
mRlQiDianTop.setOnClickListener(v -> comeToBookstore(new QiDianMobileRank(false)));
mRlQiDianNSTop.setOnClickListener(v -> comeToBookstore(new QiDianMobileRank(true)));
mRlQiDianSort.setOnClickListener(v -> comeToBookstore(new QiDianMobileRank(false, true)));
mRlQiDianNSSort.setOnClickListener(v -> comeToBookstore(new QiDianMobileRank(true, true)));
mRlQB5Store.setOnClickListener(v -> comeToBookstore(new QB5ReadCrawler()));
}

@ -5,7 +5,6 @@ import android.content.*;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ArrayAdapter;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
@ -15,12 +14,12 @@ import butterknife.BindView;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.backup.BackupAndRestore;
import xyz.fycz.myreader.backup.UserService;
import xyz.fycz.myreader.model.backup.BackupAndRestore;
import xyz.fycz.myreader.model.backup.UserService;
import xyz.fycz.myreader.base.BaseFragment;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.creator.MyAlertDialog;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.service.BookService;

@ -7,10 +7,10 @@ import android.os.Message;
import android.view.View;
import androidx.recyclerview.widget.LinearLayoutManager;
import xyz.fycz.myreader.base.BasePresenter;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.ChangeSourceDialog;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.ui.dialog.ChangeSourceDialog;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.entity.bookstore.RankBook;
import xyz.fycz.myreader.greendao.entity.Book;
@ -20,7 +20,7 @@ import xyz.fycz.myreader.ui.adapter.BookStoreBookAdapter;
import xyz.fycz.myreader.ui.adapter.BookStoreBookTypeAdapter;
import xyz.fycz.myreader.ui.fragment.BookStoreFragment;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.webapi.crawler.find.ABC;
import xyz.fycz.myreader.webapi.crawler.find.QiDianMobileRank;
import java.util.ArrayList;
@ -42,7 +42,7 @@ public class BookStorePresenter implements BasePresenter {
private List<Book> bookList = new ArrayList<>();
private BookType curType;
private ABC findCrawler;
private QiDianMobileRank findCrawler;
private int page = 1;
@ -86,7 +86,7 @@ public class BookStorePresenter implements BasePresenter {
public void start() {
mBookStoreFragment.getSrlBookList().setEnableRefresh(false);
mBookStoreFragment.getSrlBookList().setEnableLoadMore(false);
findCrawler = new ABC(true);
findCrawler = new QiDianMobileRank(true);
//小说列表下拉加载更多事件
mBookStoreFragment.getSrlBookList().setOnLoadMoreListener(refreshLayout -> {
page++;

@ -22,9 +22,7 @@ import android.widget.EditText;
import android.widget.PopupMenu;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
@ -32,25 +30,24 @@ import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.backup.BackupAndRestore;
import xyz.fycz.myreader.backup.UserService;
import xyz.fycz.myreader.base.BasePresenter;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.MultiChoiceDialog;
import xyz.fycz.myreader.creator.MyAlertDialog;
import xyz.fycz.myreader.ui.dialog.MultiChoiceDialog;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.greendao.entity.BookGroup;
import xyz.fycz.myreader.greendao.service.BookGroupService;
import xyz.fycz.myreader.model.backup.BackupAndRestore;
import xyz.fycz.myreader.model.backup.UserService;
import xyz.fycz.myreader.ui.activity.*;
import xyz.fycz.myreader.webapi.crawler.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.custom.DragSortGridView;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.widget.custom.DragSortGridView;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.enums.BookcaseStyle;
@ -109,6 +106,7 @@ public class BookcasePresenter implements BasePresenter {
private Runnable sendDownloadNotification;//发送通知的线程
private boolean isFirstRefresh = true;//是否首次进入刷新
private boolean isGroup;
private MainActivity.OnGroupChangeListener ogcl;
public static final String CANCEL_ACTION = "cancelAction";
@ -236,6 +234,7 @@ public class BookcasePresenter implements BasePresenter {
//删除监听器
mBookcaseFragment.getmBtnDelete().setOnClickListener(v -> {
if (!isGroup) {
DialogCreator.createCommonDialog(mMainActivity, "批量删除书籍",
"确定要删除这些书籍吗?", true, (dialog, which) -> {
for (Book book : mBookcaseAdapter.getSelectBooks()) {
@ -244,6 +243,24 @@ public class BookcasePresenter implements BasePresenter {
ToastUtils.showSuccess("书籍删除成功!");
init();
}, null);
}else {
DialogCreator.createCommonDialog(mMainActivity, "批量删除/移除书籍",
"您是希望是要删除这些书籍及其所有缓存还是从分组中移除(不会删除书籍)呢?", true,
"删除书籍", "从分组中移除" ,(dialog, which) -> {
for (Book book : mBookcaseAdapter.getSelectBooks()) {
mBookService.deleteBook(book);
}
ToastUtils.showSuccess("书籍删除成功!");
init();
}, (dialog, which) -> {
for (Book book : mBookcaseAdapter.getSelectBooks()) {
book.setGroupId("");
mBookService.updateEntity(book);
}
ToastUtils.showSuccess("书籍已从分组中移除!");
init();
});
}
});
//加入分组监听器
@ -254,7 +271,10 @@ public class BookcasePresenter implements BasePresenter {
BookGroup bookGroup = mBookGroups.get(which);
ArrayList<Book> mSelectBooks = (ArrayList<Book>) mBookcaseAdapter.getSelectBooks();
for (Book book : mSelectBooks) {
if (!bookGroup.getId().equals(book.getGroupId())) {
book.setGroupId(bookGroup.getId());
book.setGroupSort(0);
}
}
mBookService.updateBooks(mSelectBooks);
ToastUtils.showSuccess("成功将《" + mSelectBooks.get(0).getName() + "》"
@ -262,7 +282,7 @@ public class BookcasePresenter implements BasePresenter {
+ "加入[" + bookGroup.getName() + "]分组");
init();
} else if (which == mBookGroups.size()) {
showAddOrRenameGroupDia(false, 0);
showAddOrRenameGroupDia(false, true, 0);
}
});
});
@ -272,9 +292,8 @@ public class BookcasePresenter implements BasePresenter {
//获取数据
public void getData() {
init();
initBookGroups(true);
if (mSetting.isRefreshWhenStart() || android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
initNoReadNum();
mHandler.postDelayed(this::initNoReadNum, 500);
}
}
@ -343,7 +362,8 @@ public class BookcasePresenter implements BasePresenter {
mGroupNames = new CharSequence[isAdd ? mBookGroups.size() + 1 : mBookGroups.size()];
for (int i = 0; i < mBookGroups.size(); i++) {
String groupName = mBookGroups.get(i).getName();
mGroupNames[i] = groupName.getBytes().length > 20 ? groupName.substring(0, 8) + "···" : groupName;
// mGroupNames[i] = groupName.getBytes().length > 20 ? groupName.substring(0, 8) + "···" : groupName;
mGroupNames[i] = groupName;
}
if (isAdd) {
mGroupNames[mBookGroups.size()] = "添加分组";
@ -369,7 +389,7 @@ 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, new ResultCallback() {
CommonApi.getBookChapters(book.getChapterUrl(), mReadCrawler, true, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
ArrayList<Chapter> chapters = (ArrayList<Chapter>) o;
@ -421,6 +441,10 @@ public class BookcasePresenter implements BasePresenter {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_change_group:
mBookcaseFragment.getmBookcasePresenter()
.showBookGroupMenu(mMainActivity.findViewById(R.id.action_change_group));
return true;
case R.id.action_edit:
editBookcase(true);
return true;
@ -473,8 +497,11 @@ public class BookcasePresenter implements BasePresenter {
return false;
}
//显示书籍分组菜单
public void showBookGroupMenu(View view, MainActivity.OnGroupChangeListener ogcl) {
/**
* 显示书籍分组菜单
*
*/
public void showBookGroupMenu(View view) {
initBookGroups(false);
PopupMenu popupMenu = new PopupMenu(mMainActivity, view, Gravity.END);
popupMenu.getMenu().add(0, 0, 0, "所有书籍");
@ -539,11 +566,11 @@ public class BookcasePresenter implements BasePresenter {
initBookGroups(false);
switch (which){
case 0:
showAddOrRenameGroupDia(false, 0);
showAddOrRenameGroupDia(false, false,0);
break;
case 1:
showSelectGroupDia((dialog1, which1) -> {
showAddOrRenameGroupDia(true, which1);
showAddOrRenameGroupDia(true,false, which1);
});
break;
case 2:
@ -557,7 +584,7 @@ public class BookcasePresenter implements BasePresenter {
/**
* 添加/重命名分组对话框
*/
private void showAddOrRenameGroupDia(boolean isRename, int groupNum){
private void showAddOrRenameGroupDia(boolean isRename, boolean isAddGroup, int groupNum){
View view = LayoutInflater.from(mMainActivity).inflate(R.layout.edit_dialog, null);
TextInputLayout textInputLayout = view.findViewById(R.id.text_input_lay);
EditText editText = textInputLayout.getEditText();
@ -568,9 +595,8 @@ public class BookcasePresenter implements BasePresenter {
editText.setText(oldName);
}
editText.requestFocus();
InputMethodManager imm = (InputMethodManager) mMainActivity.getSystemService(Context.INPUT_METHOD_SERVICE);
mHandler.postDelayed(() ->{
InputMethodManager imm = (InputMethodManager) mMainActivity
.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
}, 220);
AlertDialog newGroupDia = MyAlertDialog.build(mMainActivity)
@ -583,16 +609,32 @@ public class BookcasePresenter implements BasePresenter {
Button posBtn = newGroupDia.getButton(AlertDialog.BUTTON_POSITIVE);
posBtn.setEnabled(false);
posBtn.setOnClickListener(v1 -> {
bookGroup.setName(editText.getText().toString());
CharSequence newGroupName = editText.getText().toString();
for (CharSequence oldGroupName : mGroupNames){
if (oldGroupName.equals(newGroupName)){
ToastUtils.showWarring("分组[" + newGroupName + "]已存在,无法" + (!isRename ? "添加!" : "重命名!"));
return;
}
}
bookGroup.setName(newGroupName.toString());
if (!isRename) {
mBookGroupService.addBookGroup(bookGroup);
}else {
mBookGroupService.updateEntity(bookGroup);
SharedPreUtils spu = SharedPreUtils.getInstance();
if (spu.getString("curBookGroupName", "").equals(oldName)){
spu.putString("curBookGroupName", newGroupName.toString());
ogcl.onChange();
}
}
ToastUtils.showSuccess("成功" +
(!isRename ? "添加分组[" : "成功将[" + oldName + "]重命名为[")
+ bookGroup.getName() + "]");
imm.toggleSoftInput(0, InputMethodManager.SHOW_FORCED);
newGroupDia.dismiss();
if (isAddGroup){
mBookcaseFragment.getmBtnAddGroup().performClick();
}
});
editText.addTextChangedListener(new TextWatcher() {
@Override
@ -634,6 +676,13 @@ public class BookcasePresenter implements BasePresenter {
if (sb.length() > 0){
sb.deleteCharAt(sb.lastIndexOf("、"));
}
SharedPreUtils spu = SharedPreUtils.getInstance();
if (mBookGroupService.getGroupById(spu.getString("curBookGroupId", "")) == null){
spu.putString("curBookGroupId", "");
spu.putString("curBookGroupName", "");
ogcl.onChange();
init();
}
ToastUtils.showSuccess("分组[" + sb.toString() + "]删除成功!");
}, null, null);
}
@ -648,6 +697,16 @@ public class BookcasePresenter implements BasePresenter {
.show();
}
//分组切换监听器
public void addOnGroupChangeListener(MainActivity.OnGroupChangeListener ogcl){
this.ogcl = ogcl;
}
//是否有分组切换监听器
public boolean hasOnGroupChangeListener(){
return this.ogcl != null;
}
/**********************************************缓存书籍***************************************************************/
/**
* 缓存所有书籍
@ -889,6 +948,7 @@ public class BookcasePresenter implements BasePresenter {
}
return;
}
ArrayList<Book> mBooks = (ArrayList<Book>) BookService.getInstance().getAllBooks();
if (mBooks.size() == 0) {
if (!isAutoSyn) {
ToastUtils.showWarring("当前书架无任何书籍,无法同步!");

@ -5,9 +5,8 @@ import android.content.Intent;
import android.view.View;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BasePresenter;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.ui.activity.CatalogActivity;
import xyz.fycz.myreader.util.ToastUtils;
@ -63,7 +62,7 @@ public class CatalogPresenter implements BasePresenter {
return;
}
mCatalogFragment.getPbLoading().setVisibility(View.VISIBLE);
CommonApi.getBookChapters(mBook.getChapterUrl(), ReadCrawlerUtil.getReadCrawler(mBook.getSource()),
CommonApi.getBookChapters(mBook.getChapterUrl(), ReadCrawlerUtil.getReadCrawler(mBook.getSource()),false,
new ResultCallback() {
@Override
public void onFinish(Object o, int code) {

@ -25,16 +25,17 @@ import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.base.BasePresenter;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.creator.MyAlertDialog;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.ui.dialog.SourceExchangeDialog;
import xyz.fycz.myreader.ui.activity.*;
import xyz.fycz.myreader.util.*;
import xyz.fycz.myreader.util.utils.ColorUtil;
import xyz.fycz.myreader.webapi.crawler.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.enums.Font;
@ -116,6 +117,10 @@ public class ReadPresenter implements BasePresenter {
"覆盖", "仿真", "滑动", "滚动", "无动画"
};
private SourceExchangeDialog mSourceDialog;
private boolean hasChangeSource;
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
@ -214,6 +219,7 @@ public class ReadPresenter implements BasePresenter {
if (SharedPreUtils.getInstance().getBoolean("isNightFS", false)) {
mSetting.setDayStyle(!ColorUtil.isColorLight(mReadActivity.getColor(R.color.textPrimary)));
}
//息屏时间
screenTimeOut = mSetting.getResetScreen() * 60;
@ -225,6 +231,7 @@ public class ReadPresenter implements BasePresenter {
upHpbNextPage = this::upHpbNextPage;
sendDownloadNotification = this::sendNotification;
notificationUtil = NotificationUtil.getInstance();
//注册广播
@ -244,12 +251,15 @@ public class ReadPresenter implements BasePresenter {
isCollected = mReadActivity.getIntent().getBooleanExtra("isCollected", true);
hasChangeSource = mReadActivity.getIntent().getBooleanExtra("hasChangeSource", false);
//当书籍Collected且书籍id不为空的时候保存上次阅读信息
if (isCollected && !StringHelper.isEmpty(mBook.getId())) {
//保存上次阅读信息
SharedPreUtils.getInstance().putString("lastRead", mBook.getId());
}
mReadCrawler = ReadCrawlerUtil.getReadCrawler(mBook.getSource());
mPageLoader = mReadActivity.getSrlContent().getPageLoader(mBook, mReadCrawler, mSetting);
@ -257,14 +267,45 @@ public class ReadPresenter implements BasePresenter {
mReadActivity.getPbLoading().setVisibility(View.VISIBLE);
initListener();
//Dialog
mSourceDialog = new SourceExchangeDialog(mReadActivity, mBook);
mSourceDialog.setOnSourceChangeListener((bean, pos) -> {
Book bookTem = new Book(mBook);
bookTem.setChapterUrl(bean.getChapterUrl());
bookTem.setSource(bean.getSource());
if (!StringHelper.isEmpty(bean.getImgUrl())) {
bookTem.setImgUrl(bean.getImgUrl());
}
if (!StringHelper.isEmpty(bean.getType())) {
bookTem.setType(bean.getType());
}
if (!StringHelper.isEmpty(bean.getDesc())){
bookTem.setDesc(bean.getDesc());
}
if (isCollected) {
mBookService.updateBook(mBook, bookTem);
}
mBook = bookTem;
mSettingDialog.dismiss();
Intent intent = new Intent(mReadActivity, ReadActivity.class)
.putExtra(APPCONST.BOOK, mBook)
.putExtra("hasChangeSource", true);
if (!isCollected){
intent.putExtra("isCollected", false);
}
mReadActivity.finish();
mReadActivity.startActivity(intent);
});
getData();
}
/**
* 进入阅读书籍有三种方式
* 1直接从书架进入这种方式书籍一定Collected
* 2从外部打开txt文件这种方式会添加进书架
* 3从快捷图标打开上次阅读书籍
*
* @return 是否加载成功
*/
private boolean loadBook() {
@ -324,6 +365,7 @@ public class ReadPresenter implements BasePresenter {
mSettingDialog.dismiss();
mReadActivity.onBackPressed();
}, v -> {//换源
mSourceDialog.show();
}, v -> {//刷新
isPrev = false;
if (!"本地书籍".equals(mBook.getType())) {
@ -718,7 +760,7 @@ public class ReadPresenter implements BasePresenter {
}
});
} else {
CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler, new ResultCallback() {
CommonApi.getBookChapters(mBook.getChapterUrl(), mReadCrawler,false, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
ArrayList<Chapter> chapters = (ArrayList<Chapter>) o;
@ -762,6 +804,9 @@ public class ReadPresenter implements BasePresenter {
mHandler.sendMessage(mHandler.obtainMessage(1));
return;
}
if (hasChangeSource){
mBookService.matchHistoryChapterPos(mBook, mChapters);
}
getChapterContent(mChapters.get(mBook.getHisttoryChapterNum()), new ResultCallback() {
@Override
public void onFinish(Object o, int code) {

@ -21,7 +21,7 @@ import java.util.HashMap;
import java.util.Map;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.ui.dialog.DialogCreator;
import static android.app.DownloadManager.Request.VISIBILITY_HIDDEN;

@ -1,20 +0,0 @@
package xyz.fycz.myreader.util;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class EmailUtils {
/**
* 检验邮箱格式
* @param email
* @return
*/
public static boolean isMatched(String email){
String check = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
Pattern regex = Pattern.compile(check);
Matcher matcher = regex.matcher(email);
return matcher.matches();
}
}

@ -1,58 +0,0 @@
package xyz.fycz.myreader.util;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
public class FormatDateUtils {
private static Date date;
private static SimpleDateFormat format;
private static SimpleDateFormat mDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
public static String formatDateTime(long time) {
if (0 == time) {
return "";
}
return mDateFormat.format(new Date(time));
}
public static String long2date(String str, String formater){
return formateLongTime(long2String(str),formater);
}
public static String long2date(long times, String formater){
return formateLongTime(times,formater);
}
private static String formateLongTime(long times, String formater){
date=new Date(times);
GregorianCalendar gc = new GregorianCalendar();
gc.setTime(date);
format = new SimpleDateFormat(formater);
String sb=format.format(gc.getTime());
return sb;
}
public static long long2String(String str){
long time = 0;
try {
time = mDateFormat.parse(str).getTime();
} catch (ParseException e) {
e.printStackTrace();
}
return time;
}
public static long day2long(long times){
long day = times / (1000*60*60*24);
return day;
}
}

@ -8,9 +8,9 @@ import com.google.gson.Gson;
import okhttp3.*;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.application.TrustAllCerts;
import xyz.fycz.myreader.callback.HttpCallback;
import xyz.fycz.myreader.callback.JsonCallback;
import xyz.fycz.myreader.callback.URLConnectionCallback;
import xyz.fycz.myreader.webapi.callback.HttpCallback;
import xyz.fycz.myreader.webapi.callback.JsonCallback;
import xyz.fycz.myreader.webapi.callback.URLConnectionCallback;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.entity.JsonModel;
@ -43,7 +43,6 @@ public class HttpUtil {
private static SSLSocketFactory createSSLSocketFactory() {
SSLSocketFactory ssfFactory = null;
try {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom());
@ -54,10 +53,12 @@ public class HttpUtil {
return ssfFactory;
}
private static synchronized OkHttpClient getOkHttpClient() {
public static synchronized OkHttpClient getOkHttpClient() {
if (mClient == null) {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS);
builder.connectTimeout(5, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS);
builder.sslSocketFactory(createSSLSocketFactory());
builder.hostnameVerifier((hostname, session) -> true);
mClient = builder
@ -206,7 +207,7 @@ public class HttpUtil {
}).start();
}
public static void sendGetRequest_okHttp(final String address, final HttpCallback callback) {
public static void sendGetRequest_okHttp(final String address, boolean isRefresh, final HttpCallback callback) {
MyApplication.getApplication().newThread(() -> {
/* HttpURLConnection connection = null;
try {

@ -1,272 +0,0 @@
package xyz.fycz.myreader.util;
import android.content.Context;
import android.graphics.*;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.callback.ResultCallback;
import static xyz.fycz.myreader.util.DipPxUtil.dp2px;
/**
* @author fengyue
* @date 2020/8/13 7:56
*/
public class ImageUtil {
/**
* 设置水印图片在左上角
* @param context
* @param src
* @param watermark
* @param paddingLeft
* @param paddingTop
* @return
*/
public static Bitmap createWaterMaskLeftTop(
Context context, Bitmap src, Bitmap watermark,
int paddingLeft, int paddingTop) {
return createWaterMaskBitmap(src, watermark,
dp2px(context, paddingLeft), dp2px(context, paddingTop));
}
private static Bitmap createWaterMaskBitmap(Bitmap src, Bitmap watermark,
int paddingLeft, int paddingTop) {
if (src == null) {
return null;
}
int width = src.getWidth();
int height = src.getHeight();
//创建一个bitmap
Bitmap newb = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);// 创建一个新的和SRC长度宽度一样的位图
//将该图片作为画布
Canvas canvas = new Canvas(newb);
//在画布 0,0坐标上开始绘制原始图片
canvas.drawBitmap(src, 0, 0, null);
//在画布上绘制水印图片
canvas.drawBitmap(watermark, paddingLeft, paddingTop, null);
// 保存
canvas.save();
// 存储
canvas.restore();
return newb;
}
/**
* 设置水印图片在右下角
* @param context
* @param src
* @param watermark
* @param paddingRight
* @param paddingBottom
* @return
*/
public static Bitmap createWaterMaskRightBottom(
Context context, Bitmap src, Bitmap watermark,
int paddingRight, int paddingBottom) {
return createWaterMaskBitmap(src, watermark,
src.getWidth() - watermark.getWidth() - dp2px(context, paddingRight),
src.getHeight() - watermark.getHeight() - dp2px(context, paddingBottom));
}
/**
* 设置水印图片到右上角
* @param context
* @param src
* @param watermark
* @param paddingRight
* @param paddingTop
* @return
*/
public static Bitmap createWaterMaskRightTop(
Context context, Bitmap src, Bitmap watermark,
int paddingRight, int paddingTop) {
return createWaterMaskBitmap( src, watermark,
src.getWidth() - watermark.getWidth() - dp2px(context, paddingRight),
dp2px(context, paddingTop));
}
/**
* 设置水印图片到左下角
* @param context
* @param src
* @param watermark
* @param paddingLeft
* @param paddingBottom
* @return
*/
public static Bitmap createWaterMaskLeftBottom(
Context context, Bitmap src, Bitmap watermark,
int paddingLeft, int paddingBottom) {
return createWaterMaskBitmap(src, watermark, dp2px(context, paddingLeft),
src.getHeight() - watermark.getHeight() - dp2px(context, paddingBottom));
}
/**
* 设置水印图片到中间
* @param src
* @param src
* @param watermark
* @return
*/
public static Bitmap createWaterMaskCenter(Bitmap src, Bitmap watermark) {
return createWaterMaskBitmap(src, watermark,
(src.getWidth() - watermark.getWidth()) / 2,
(src.getHeight() - watermark.getHeight()) / 2);
}
/**
* 给图片添加文字到左上角
* @param context
* @param bitmap
* @param text
* @return
*/
public static Bitmap drawTextToLeftTop(Context context, Bitmap bitmap, String text,
int size, int color, int paddingLeft, int paddingTop) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setTextSize(dp2px(context, size));
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
return drawTextToBitmap(context, bitmap, text, paint, bounds,
dp2px(context, paddingLeft),
dp2px(context, paddingTop) + bounds.height());
}
/**
* 绘制文字到右下角
* @param context
* @param bitmap
* @param text
* @param size
* @param color
* @param paddingRight
* @param paddingBottom
* @return
*/
public static Bitmap drawTextToRightBottom(Context context, Bitmap bitmap, String text,
int size, int color, int paddingRight, int paddingBottom) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setTextSize(dp2px(context, size));
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
return drawTextToBitmap(context, bitmap, text, paint, bounds,
bitmap.getWidth() - bounds.width() - dp2px(context, paddingRight),
bitmap.getHeight() - dp2px(context, paddingBottom));
}
/**
* 绘制文字到右上方
* @param context
* @param bitmap
* @param text
* @param size
* @param color
* @param paddingRight
* @param paddingTop
* @return
*/
public static Bitmap drawTextToRightTop(Context context, Bitmap bitmap, String text,
int size, int color, int paddingRight, int paddingTop) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setTextSize(dp2px(context, size));
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
return drawTextToBitmap(context, bitmap, text, paint, bounds,
bitmap.getWidth() - bounds.width() - dp2px(context, paddingRight),
dp2px(context, paddingTop) + bounds.height());
}
/**
* 绘制文字到左下方
* @param context
* @param bitmap
* @param text
* @param size
* @param color
* @param paddingLeft
* @param paddingBottom
* @return
*/
public static Bitmap drawTextToLeftBottom(Context context, Bitmap bitmap, String text,
int size, int color, int paddingLeft, int paddingBottom) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setTextSize(dp2px(context, size));
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
return drawTextToBitmap(context, bitmap, text, paint, bounds,
dp2px(context, paddingLeft),
bitmap.getHeight() - dp2px(context, paddingBottom));
}
/**
* 绘制文字到中间
* @param context
* @param bitmap
* @param text
* @param size
* @param color
* @return
*/
public static Bitmap drawTextToCenter(Context context, Bitmap bitmap, String text,
int size, int color) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
paint.setTextSize(dp2px(context, size));
Rect bounds = new Rect();
paint.getTextBounds(text, 0, text.length(), bounds);
return drawTextToBitmap(context, bitmap, text, paint, bounds,
(bitmap.getWidth() - bounds.width()) / 2,
(bitmap.getHeight() + bounds.height()) / 2);
}
//图片上绘制文字
private static Bitmap drawTextToBitmap(Context context, Bitmap bitmap, String text,
Paint paint, Rect bounds, int paddingLeft, int paddingTop) {
android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig();
paint.setDither(true); // 获取跟清晰的图像采样
paint.setFilterBitmap(true);// 过滤一些
// paint.setFakeBoldText(true); //加粗
if (bitmapConfig == null) {
bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888;
}
bitmap = bitmap.copy(bitmapConfig, true);
Canvas canvas = new Canvas(bitmap);
canvas.drawText(text, paddingLeft, paddingTop, paint);
return bitmap;
}
/**
* 缩放图片
* @param src
* @param w
* @param h
* @return
*/
public static Bitmap scaleWithWH(Bitmap src, double w, double h) {
if (w == 0 || h == 0 || src == null) {
return src;
} else {
// 记录src的宽高
int width = src.getWidth();
int height = src.getHeight();
// 创建一个matrix容器
Matrix matrix = new Matrix();
// 计算缩放比例
float scaleWidth = (float) (w / width);
float scaleHeight = (float) (h / height);
// 开始缩放
matrix.postScale(scaleWidth, scaleHeight);
// 创建缩放后的图片
return Bitmap.createBitmap(src, 0, 0, width, height, matrix, true);
}
}
}

@ -1,157 +0,0 @@
package xyz.fycz.myreader.util;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;
import android.widget.ListAdapter;
import android.widget.ListView;
import xyz.fycz.myreader.base.BaseActivity;
import java.lang.reflect.Field;
public class ListViewHeight {
private static HandlerThread thread = new HandlerThread("listview");
private static int totalHeight = 0;
static {
thread.start();
}
private static Handler handler = new Handler(thread.getLooper()){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
break;
}
}
};
/**
* 计算listview的高度
* @param listView
* @return
*/
public static int setListViewHeightBasedOnChildren(final ListView listView) {
ListAdapter listAdapter = listView.getAdapter();
if (listAdapter == null) {
// pre-condition
return 0;
}
int totalHeight = 0;
for (int i = 0; i < listAdapter.getCount(); i++) {
View listItem = listAdapter.getView(i, null, listView);
listItem.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
// listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
final ViewGroup.LayoutParams params = listView.getLayoutParams();
params.height = totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
// MyApplication.getApplication().runOnUiThread(new Runnable() {
// @Override
// public void run() {
listView.setLayoutParams(params);
// }
// });
return totalHeight + (listView.getDividerHeight() * (listAdapter.getCount() - 1));
}
/**
* 日期gridview的高度
* @param gridView
* @return
*/
public static int setDateGridViewHeightBasedOnChildren(GridView gridView) {
ListAdapter listAdapter = gridView.getAdapter();
if (listAdapter == null) {
// pre-condition
return 0;
}
int totalHeight = 0;
for (int i = 0; i < (listAdapter.getCount()/7); i++) {
View listItem = listAdapter.getView(i, null, gridView);
listItem.measure(0, 0);
totalHeight += listItem.getMeasuredHeight();
}
ViewGroup.LayoutParams params = gridView.getLayoutParams();
params.height = totalHeight + (1 * (listAdapter.getCount()/7 - 1));
gridView.setLayoutParams(params);
return totalHeight + (1 * (listAdapter.getCount()/7 - 1));
}
/**
* 计算gridview的高度
* @param gridView
*/
public static void setGridViewHeightBasedOnChildren(GridView gridView) {
// 获取GridView对应的Adapter
ListAdapter listAdapter = gridView.getAdapter();
if (listAdapter == null) {
return;
}
int rows;
int columnWidths;
int columns=0;
int horizontalBorderHeight = 0;
int verticalBorderHeight = 0;
Class<?> clazz=gridView.getClass();
try {
//利用反射,取得横向分割线高度
Field vertical=clazz.getDeclaredField("mVerticalSpacing");
vertical.setAccessible(true);
verticalBorderHeight =(Integer)vertical.get(gridView);
//利用反射,取得纵向分割线高度
Field horizontalSpacing=clazz.getDeclaredField("mRequestedHorizontalSpacing");
horizontalSpacing.setAccessible(true);
horizontalBorderHeight=(Integer)horizontalSpacing.get(gridView);
//利用反射,取得每行显示的个数
Field column=clazz.getDeclaredField("mRequestedNumColumns");
column.setAccessible(true);
columns=(Integer)column.get(gridView);
if(columns == -1 || columns == 0){
Field columnWidth = clazz.getDeclaredField("mRequestedColumnWidth");
columnWidth.setAccessible(true);
columnWidths = (Integer)columnWidth.get(gridView);
int pad = gridView.getPaddingLeft() * 2;
int width = BaseActivity.width - pad * 2;
columns = width / (columnWidths /*+ horizontalBorderHeight*/);
if(horizontalBorderHeight * (columns - 1) + columnWidths * columns > width){
columns --;
}
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
//判断数据总数除以每行个数是否整除。不能整除代表有多余,需要加一行
if(listAdapter.getCount()%columns > 0){
rows=listAdapter.getCount()/columns+1;
}else {
rows=listAdapter.getCount()/columns;
}
int totalHeight = 0;
for (int i = 0; i < rows; i++) { //只计算每项高度*行数
View listItem = listAdapter.getView(i, null, gridView);
listItem.measure(0, 0); // 计算子项View 的宽高
totalHeight += listItem.getMeasuredHeight(); // 统计所有子项的总高度
}
ViewGroup.LayoutParams params = gridView.getLayoutParams();
params.height = totalHeight + gridView.getPaddingTop() + + gridView.getPaddingBottom() + verticalBorderHeight * (rows-1);//最后加上分割线总高度
gridView.setLayoutParams(params);
}
}

@ -1,69 +0,0 @@
package xyz.fycz.myreader.util;
import android.app.NotificationManager;
import android.content.Context;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
public class NotifyHelper {
private static NotificationManager mNotificationManager;
private static Map mNotifyId = new HashMap();
private static ArrayList<String> mCurrentConvIds = new ArrayList<String>();
public static void init(Context context){
mNotificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
}
public static int createNotifyId(String convId) {
if (!mNotifyId.containsKey(convId)) {
for (int i = 0; i < 1000; i++) {
if (!mNotifyId.containsValue(i)) {
mNotifyId.put(convId, i);
return i;
}
}
}else {
return (int)mNotifyId.get(convId);
}
return -1;
}
public static void closeNotification(String convId){
//关闭通知
int notifyId = getNotifyId(convId);
if(notifyId != -1){
mNotificationManager.cancel(notifyId);
mNotifyId.remove(convId);
}
}
public static int getNotifyId(String convId){
if(mNotifyId.get(convId) == null){
return -1;
}else {
return (int)mNotifyId.get(convId);
}
}
public static boolean isOpenConv(String convId){
return mCurrentConvIds.contains(convId);
}
public static void addCurrentConv(String convId){
if(!mCurrentConvIds.contains(convId)){
mCurrentConvIds.add(convId);
}
}
public static void removeCurrenConv(String convId){
mCurrentConvIds.remove(convId);
}
}

@ -1,149 +0,0 @@
package xyz.fycz.myreader.util;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public class PermissionHelper {
/**
* 设备信息读取权限
* @param context
* @return
*/
public static boolean isREAD_PHONE_STATE(Context context) {
boolean permission = false;
if (Build.VERSION.SDK_INT >= 23) {
int checkReadPhoneStatePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE);
if (checkReadPhoneStatePermission != PackageManager.PERMISSION_GRANTED) {
// 弹出对话框接收权限
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.READ_PHONE_STATE}, 1);
ToastUtils.showWarring("当前应用未拥读取设备状态权限");
} else if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
// 弹出对话框接收权限
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.READ_PHONE_STATE}, 1);
ToastUtils.showWarring("当前应用未拥读取设备状态权限");
} else {
permission = true;
}
} else {
permission = true;
}
return permission;
}
/**
* 蓝牙设备权限
* @param context
* @return
*/
public static boolean isACCESS_COARSE_LOCATION(Context context) {
boolean permission = false;
if (Build.VERSION.SDK_INT >= 23) {
int checkReadPhoneStatePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION);
if (checkReadPhoneStatePermission != PackageManager.PERMISSION_GRANTED) {
// 弹出对话框接收权限
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
ToastUtils.showWarring("当前应用未拥有蓝牙设备使用权限");
} else if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// 弹出对话框接收权限
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
ToastUtils.showWarring("当前应用未拥有蓝牙设备使用权限");
} else {
permission = true;
}
} else {
permission = true;
}
return permission;
}
/**
* 文件读写权限
* @param context
* @return
*/
public static boolean isWRPermission(Context context) {
boolean permission = false;
if (Build.VERSION.SDK_INT >= 23) {
int checkReadPhoneStatePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (checkReadPhoneStatePermission != PackageManager.PERMISSION_GRANTED) {
// 弹出对话框接收权限
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
ToastUtils.showWarring("当前应用未拥有存储设备读写权限");
} else if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// 弹出对话框接收权限
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
ToastUtils.showWarring("当前应用未拥有存储设备读写权限");
} else {
permission = true;
}
} else {
permission = true;
}
return permission;
}
/**
* 声音设备权限
* @param context
* @return
*/
public static boolean isAudioPermission(Context context) {
boolean permission = false;
if (Build.VERSION.SDK_INT >= 23) {
int checkReadPhoneStatePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO);
if (checkReadPhoneStatePermission != PackageManager.PERMISSION_GRANTED) {
// 弹出对话框接收权限
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.RECORD_AUDIO}, 1);
ToastUtils.showWarring("当前应用未拥有音频录制权限");
} else if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
// 弹出对话框接收权限
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.RECORD_AUDIO}, 1);
ToastUtils.showWarring("当前应用未拥有音频录制权限");
} else {
permission = true;
}
} else {
permission = true;
}
return permission;
}
/**
* 摄像头权限
* @param context
* @return
*/
public static boolean isCameraPermission(Context context) {
boolean permission = false;
if (Build.VERSION.SDK_INT >= 23) {
int checkReadPhoneStatePermission = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA);
if (checkReadPhoneStatePermission != PackageManager.PERMISSION_GRANTED) {
// 弹出对话框接收权限
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.CAMERA}, 1);
ToastUtils.showWarring("当前应用未拥有调用摄像头权限");
} else if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
// 弹出对话框接收权限
ActivityCompat.requestPermissions((AppCompatActivity) context, new String[]{android.Manifest.permission.CAMERA}, 1);
ToastUtils.showWarring("当前应用未拥有调用摄像头权限");
} else {
permission = true;
}
} else {
permission = true;
}
return permission;
}
}

@ -4,9 +4,7 @@ import android.content.Context;
import android.content.pm.PackageManager;
import androidx.core.content.ContextCompat;
/**
* Created by newbiechen on 2017/10/8.
*/
public class PermissionsChecker {
private final Context mContext;

@ -1,31 +0,0 @@
package xyz.fycz.myreader.util;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
public class PingUtil {
public static boolean ping(String host, int port) {
if (port == 0) port = 80;
Socket connect = new Socket();
try {
connect.connect(new InetSocketAddress(host, port), 10 * 1000);
return connect.isConnected();
} catch (IOException e) {
e.printStackTrace();
}finally{
try {
connect.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
}

@ -1,123 +0,0 @@
package xyz.fycz.myreader.util;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import xyz.fycz.myreader.application.MyApplication;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* 二维码生成工具类
*/
public class QRCodeUtil {
/**
* 生成二维码Bitmap
*
* @param content 内容
* @param widthPix 图片宽度
* @param heightPix 图片高度
* @param logoBm 二维码中心的Logo图标可以为null
* @param filePath 用于存储二维码图片的文件路径
* @return 生成二维码及保存文件是否成功
*/
public static boolean createQRImage(String content, int widthPix, int heightPix, Bitmap logoBm, String filePath) {
try {
if (content == null || "".equals(content)) {
return false;
}
//配置参数
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
//容错级别
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
//设置空白边距的宽度
// hints.put(EncodeHintType.MARGIN, 2); //default is 4
// 图像数据转换,使用了矩阵转换
BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, widthPix, heightPix, hints);
int[] pixels = new int[widthPix * heightPix];
// 下面这里按照二维码的算法,逐个生成二维码的图片,
// 两个for循环是图片横列扫描的结果
for (int y = 0; y < heightPix; y++) {
for (int x = 0; x < widthPix; x++) {
if (bitMatrix.get(x, y)) {
pixels[y * widthPix + x] = 0xff000000;
} else {
pixels[y * widthPix + x] = 0xffffffff;
}
}
}
// 生成二维码图片的格式,使用ARGB_8888
Bitmap bitmap = Bitmap.createBitmap(widthPix, heightPix, Bitmap.Config.ARGB_8888);
bitmap.setPixels(pixels, 0, widthPix, 0, 0, widthPix, heightPix);
if (logoBm != null) {
bitmap = addLogo(bitmap, logoBm);
}
//必须使用compress方法将bitmap保存到文件中再进行读取。直接返回的bitmap是没有任何压缩的,内存消耗巨大!
FileOutputStream fos = MyApplication.getApplication().openFileOutput(filePath, MyApplication.getApplication().MODE_PRIVATE);
return bitmap != null && bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
} catch (WriterException | IOException e) {
e.printStackTrace();
}
return false;
}
/**
* 在二维码中间添加Logo图案
*/
private static Bitmap addLogo(Bitmap src, Bitmap logo) {
if (src == null) {
return null;
}
if (logo == null) {
return src;
}
//获取图片的宽高
int srcWidth = src.getWidth();
int srcHeight = src.getHeight();
int logoWidth = logo.getWidth();
int logoHeight = logo.getHeight();
if (srcWidth == 0 || srcHeight == 0) {
return null;
}
if (logoWidth == 0 || logoHeight == 0) {
return src;
}
//logo大小为二维码整体大小的1/5
float scaleFactor = srcWidth * 1.0f / 5 / logoWidth;
Bitmap bitmap = Bitmap.createBitmap(srcWidth, srcHeight, Bitmap.Config.ARGB_8888);
try {
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(src, 0, 0, null);
canvas.scale(scaleFactor, scaleFactor, srcWidth / 2, srcHeight / 2);
canvas.drawBitmap(logo, (srcWidth - logoWidth) / 2, (srcHeight - logoHeight) / 2, null);
canvas.save();
canvas.restore();
} catch (Exception e) {
bitmap = null;
e.getStackTrace();
}
return bitmap;
}
}

@ -1,137 +0,0 @@
package xyz.fycz.myreader.util;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.preference.PreferenceManager;
import xyz.fycz.myreader.application.MyApplication;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
public class SharedPreferencesUtils {
private static Editor mEditor;
private SharedPreferences mSharedPreferences;
private static SharedPreferencesUtils mSharedPreferencesUtils;
public static SharedPreferencesUtils getInstance() {
if(mSharedPreferencesUtils == null) {
mSharedPreferencesUtils = new SharedPreferencesUtils(MyApplication.getmContext(),
"FYReaderShare");
}
return mSharedPreferencesUtils;
}
/**
*
* @param context
*/
public SharedPreferencesUtils(Context context){
mSharedPreferences = PreferenceManager
.getDefaultSharedPreferences(context);
mEditor = mSharedPreferences.edit();
}
/**
*
* @param context
* @param fileName
*/
public SharedPreferencesUtils(Context context, String fileName){
mSharedPreferences = context
.getSharedPreferences(fileName, Context.MODE_PRIVATE);
mEditor = mSharedPreferences.edit();
}
/**
* 设置通知公告是否已被查看
* @param key
* @param flag
*/
public void setNoticeFlag(String key, boolean flag) {
mEditor.putBoolean(key, flag);
mEditor.commit();
}
/**
* 判断在该机该用户的通知公告是否已被查看
* @param key
* @return
*/
public boolean isNoticeFlag(String key){
return mSharedPreferences.getBoolean(key,false);
}
/**
* 保存数据
* @param key 键值
* @param news 需要保存的数据
*/
public void save(String key, String news) {
Set<String> values = null;
//获取或创建key,如果xml文件中含有key
if(mSharedPreferences.contains(key)){
//将数据存储到集合里
values = mSharedPreferences.getStringSet(key, null);
//删除xml文件中key,并提交
mEditor.remove(key).commit();
}else{//如果xml文件中没有key
values = new HashSet<String>();
}
//将新的数据添加到集合中
values.add(news);
//将集合保存到偏好设置中
mEditor.putStringSet(key, values);
//提交
mEditor.commit();
}
/**
* 清空数据
*/
public void clear(){
mEditor.clear();
mEditor.commit();
}
/**
* 获取数据
* @param key 键值
* @return 返回一个字符串
*/
public List<String> query(String key){
//创建StringBuffer对象,用来存储从偏好设置中获取的数据
List<String> list = new ArrayList<String>();
//获取xml中对应key的值
Set<String> values = mSharedPreferences.getStringSet(key, null);
//判断该数据是否为空,如果不为空,则
if(key != null && values != null){
//遍历集合
Iterator<String> iterator = values.iterator();
while(iterator.hasNext()){
//将数据添加到StringBuffer
list.add(iterator.next());
}
}
return list;
}
/**
* 设置是否是第一次登录
* @param values
*/
public void setFirstEntry(int values){
mEditor.putInt("first_entry", values);
mEditor.commit();
}
public int getFirstEntry(){
return mSharedPreferences.getInt("first_entry",-1);
}
}

@ -1,98 +0,0 @@
package xyz.fycz.myreader.util;
import android.graphics.Rect;
import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import java.util.LinkedList;
import java.util.List;
/**
* 软键盘弹出/关闭监听
*/
public class SoftKeyBroadManager implements ViewTreeObserver.OnGlobalLayoutListener{
public interface SoftKeyboardStateListener {
void onSoftKeyboardOpened(int keyboardHeightInPx);
void onSoftKeyboardClosed();
}
private final List<SoftKeyboardStateListener> listeners = new LinkedList<SoftKeyboardStateListener>();
private final View activityRootView;
private int lastSoftKeyboardHeightInPx;
private boolean isSoftKeyboardOpened;
public SoftKeyBroadManager(View activityRootView) {
this(activityRootView,false);
}
public SoftKeyBroadManager(View activityRootView, boolean isSoftKeyboardOpened) {
this.activityRootView = activityRootView;
this.isSoftKeyboardOpened = isSoftKeyboardOpened;
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(this);
}
@Override
public void onGlobalLayout() {
final Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
final int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
Log.d("SoftKeyboardStateHelper","heightDiff:" + heightDiff);
if (!isSoftKeyboardOpened && heightDiff > 500) { // if more than 100 pixels, its probably a keyboard...
isSoftKeyboardOpened = true;
notifyOnSoftKeyboardOpened(heightDiff);
//if (isSoftKeyboardOpened && heightDiff < 100)
} else if (isSoftKeyboardOpened && heightDiff < 500) {
isSoftKeyboardOpened = false;
notifyOnSoftKeyboardClosed();
}
}
public void setIsSoftKeyboardOpened(boolean isSoftKeyboardOpened) {
this.isSoftKeyboardOpened = isSoftKeyboardOpened;
}
public boolean isSoftKeyboardOpened() {
return isSoftKeyboardOpened;
}
/**
* Default value is zero (0)
*
* @return last saved keyboard height in px
*/
public int getLastSoftKeyboardHeightInPx() {
return lastSoftKeyboardHeightInPx;
}
public void addSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
listeners.add(listener);
}
public void removeSoftKeyboardStateListener(SoftKeyboardStateListener listener) {
listeners.remove(listener);
}
private void notifyOnSoftKeyboardOpened(int keyboardHeightInPx) {
this.lastSoftKeyboardHeightInPx = keyboardHeightInPx;
for (SoftKeyboardStateListener listener : listeners) {
if (listener != null) {
listener.onSoftKeyboardOpened(keyboardHeightInPx);
}
}
}
private void notifyOnSoftKeyboardClosed() {
for (SoftKeyboardStateListener listener : listeners) {
if (listener != null) {
listener.onSoftKeyboardClosed();
}
}
}
}

@ -1,51 +0,0 @@
package xyz.fycz.myreader.util;
import android.content.Context;
import android.telephony.TelephonyManager;
import java.lang.reflect.Method;
public class TelephonyUtil {
public static String getNum1(Context context){
String tel;
String IMSI;
try {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
String deviceid = tm.getDeviceId();
tel = tm.getLine1Number();//手机号码
Class clazz = tm.getClass();
Method getPhoneNumber = clazz.getDeclaredMethod("getLine1NumberForSubscriber",int.class);
String tel0 = (String)getPhoneNumber.invoke(tm, 0);
String tel1 = (String)getPhoneNumber.invoke(tm, 1);
IMSI = tm.getSubscriberId();
ToastUtils.showInfo(IMSI);
}catch (Exception e){
e.printStackTrace();
tel = "";
}
return tel;
}
public static String getNum2(Context context){
String tel;
try {
TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
String deviceid = tm.getDeviceId();
tel = tm.getLine1Number();//手机号码
}catch (Exception e){
e.printStackTrace();
tel = "";
}
return tel;
}
}

@ -1,5 +0,0 @@
package xyz.fycz.myreader.util;
public class UploadImageTask{
}

@ -1,27 +0,0 @@
package xyz.fycz.myreader.util;
import android.app.Service;
import android.os.Vibrator;
import androidx.appcompat.app.AppCompatActivity;
public class VibratorUtil {
/**
* final Activity activity 调用该方法的Activity实例
* long milliseconds 震动的时长单位是毫秒
* long[] pattern 自定义震动模式 数组中数字的含义依次是[静止时长震动时长静止时长震动时长]时长的单位是毫秒
* boolean isRepeat 是否反复震动如果是true反复震动如果是false只震动一次
*/
public static void Vibrate(final AppCompatActivity activity, long milliseconds) {
Vibrator vib = (Vibrator) activity.getSystemService(Service.VIBRATOR_SERVICE);
vib.vibrate(milliseconds);
}
public static void Vibrate(final AppCompatActivity activity, long[] pattern, boolean isRepeat) {
Vibrator vib = (Vibrator) activity.getSystemService(Service.VIBRATOR_SERVICE);
vib.vibrate(pattern, isRepeat ? 1 : -1);
}
}

@ -4,24 +4,17 @@ import android.annotation.TargetApi;
import android.app.*;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import androidx.appcompat.app.AlertDialog;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.creator.MyAlertDialog;
import xyz.fycz.myreader.greendao.service.BookMarkService;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import static xyz.fycz.myreader.util.notification.NotificationClickReceiver.CANCEL_ACTION;

@ -0,0 +1,43 @@
package xyz.fycz.myreader.util.utils;
/**
* Created by newbiechen on 2018/1/1.
*/
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 将字符串转化为MD5
*/
public class MD5Utils {
public static String strToMd5By32(String str) {
String reStr = null;
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
byte[] bytes = md5.digest(str.getBytes());
StringBuilder stringBuffer = new StringBuilder();
for (byte b : bytes) {
int bt = b & 0xff;
if (bt < 16) {
stringBuffer.append(0);
}
stringBuffer.append(Integer.toHexString(bt));
}
reStr = stringBuffer.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return reStr;
}
public static String strToMd5By16(String str) {
String reStr = strToMd5By32(str);
if (reStr != null) {
reStr = reStr.substring(8, 24);
}
return reStr;
}
}

@ -0,0 +1,69 @@
package xyz.fycz.myreader.util.utils;
import android.util.Log;
import okhttp3.*;
import xyz.fycz.myreader.application.TrustAllCerts;
import xyz.fycz.myreader.util.HttpUtil;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.security.SecureRandom;
public class OkHttpUtils {
public static OkHttpClient okHttpClient = HttpUtil.getOkHttpClient();
/**
* 同步获取html文件默认编码utf-8
*/
public static String getHtml(String url) throws IOException {
return getHtml(url, "utf-8");
}
public static String getHtml(String url, String encodeType) throws IOException {
return getHtml(url, null, encodeType);
}
public static String getHtml(String url, RequestBody requestBody, String encodeType) throws IOException {
Request.Builder builder = new Request.Builder()
.addHeader("accept", "*/*")
.addHeader("connection", "Keep-Alive")
//.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/70.0.3538.110 Safari/537.36");
if (requestBody != null) {
builder.post(requestBody);
Log.d("HttpPost URl", url);
}else {
Log.d("HttpGet URl", url);
}
Request request = builder
.url(url)
.build();
Response response = okHttpClient
.newCall(request)
.execute();
ResponseBody body=response.body();
if (body == null) {
return "";
} else {
String bodyStr = new String(body.bytes(), encodeType);
Log.d("Http: read finish", bodyStr);
return bodyStr;
}
}
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;
}
}

@ -6,8 +6,8 @@ import xyz.fycz.myreader.entity.JsonModel;
import xyz.fycz.myreader.util.HttpUtil;
import xyz.fycz.myreader.util.JsonArrayToObjectArray;
import com.google.gson.Gson;
import xyz.fycz.myreader.callback.JsonCallback;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.callback.JsonCallback;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.source.HttpDataSource;
import xyz.fycz.myreader.util.ToastUtils;
@ -156,8 +156,8 @@ public class BaseApi {
* @param params
* @param callback
*/
public static void getCommonReturnHtmlStringApi(String url, Map<String, Object> params, String charsetName, final ResultCallback callback) {
HttpDataSource.httpGet_html(HttpUtil.makeURL(url, params), charsetName, new ResultCallback() {
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);

@ -1,10 +1,8 @@
package xyz.fycz.myreader.webapi;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.webapi.crawler.BiQuGeReadCrawler;
import xyz.fycz.myreader.webapi.crawler.FindCrawler;
import xyz.fycz.myreader.webapi.crawler.QB5ReadCrawler;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.webapi.crawler.base.FindCrawler;
/**
* Created by zhao on 2017/7/24.
@ -20,7 +18,7 @@ public class BookStoreApi extends BaseApi{
*/
public static void getBookTypeList(FindCrawler findCrawler, final ResultCallback callback){
getCommonReturnHtmlStringApi(findCrawler.getFindUrl(), null, findCrawler.getCharset(), new ResultCallback() {
getCommonReturnHtmlStringApi(findCrawler.getFindUrl(), null, findCrawler.getCharset(), true, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
callback.onFinish(findCrawler.getBookTypeList((String) o),0);
@ -42,7 +40,7 @@ public class BookStoreApi extends BaseApi{
*/
public static void getBookRankList(String url, FindCrawler findCrawler, final ResultCallback callback){
getCommonReturnHtmlStringApi(url, null, findCrawler.getCharset(), new ResultCallback() {
getCommonReturnHtmlStringApi(url, null, findCrawler.getCharset(),true, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
callback.onFinish(findCrawler.getRankBookList((String) o),0);

@ -1,12 +1,21 @@
package xyz.fycz.myreader.webapi;
import io.reactivex.Observable;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.webapi.crawler.*;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.util.utils.OkHttpUtils;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.read.FYReadCrawler;
import xyz.fycz.myreader.webapi.crawler.read.TianLaiReadCrawler;
import java.util.HashMap;
import java.util.Map;
import java.io.IOException;
public class CommonApi extends BaseApi {
@ -17,9 +26,9 @@ public class CommonApi extends BaseApi {
* @param url
* @param callback
*/
public static void getBookChapters(String url, final ReadCrawler rc, final ResultCallback callback) {
public static void getBookChapters(String url, final ReadCrawler rc, boolean isRefresh, final ResultCallback callback) {
String charset = rc.getCharset();
getCommonReturnHtmlStringApi(url, null, charset, new ResultCallback() {
getCommonReturnHtmlStringApi(url, null, charset, isRefresh, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
callback.onFinish(rc.getChaptersFromHtml((String) o), 0);
@ -53,7 +62,7 @@ public class CommonApi extends BaseApi {
url = URLCONST.nameSpace_FY + url;
}
}
getCommonReturnHtmlStringApi(url, null, charset, new ResultCallback() {
getCommonReturnHtmlStringApi(url, null, charset, true, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
callback.onFinish(rc.getContentFormHtml((String) o), 0);
@ -75,20 +84,13 @@ public class CommonApi extends BaseApi {
*/
public static void search(String key, final ReadCrawler rc, final ResultCallback callback) {
Map<String, Object> params = new HashMap<>();
String charset = "utf-8";
if (rc instanceof TianLaiReadCrawler) {
charset = "utf-8";
} else {
charset = rc.getCharset();
}
params.put(rc.getSearchKey(), key);
if (rc instanceof PinShuReadCrawler) {
params.put("SearchClass", 1);
}else if (rc instanceof QB5ReadCrawler){
params.put("submit", "%CB%D1%CB%F7");
}
getCommonReturnHtmlStringApi(rc.getSearchLink(), params, charset, new ResultCallback() {
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);
@ -101,6 +103,63 @@ public class CommonApi extends BaseApi {
});
}
/**
* 搜索小说
*
* @param key
*/
public static Observable<ConcurrentMultiValueMap<SearchBookBean, Book>> search(String key, final ReadCrawler rc) {
String charset = "utf-8";
if (rc instanceof TianLaiReadCrawler) {
charset = "utf-8";
} else {
charset = rc.getCharset();
}
String finalCharset = charset;
return Observable.create(emitter -> {
try {
if (rc.isPost()){
String url = rc.getSearchLink();
String[] urlInfo = url.split(",");
url = urlInfo[0];
String body = makeSearchUrl(urlInfo[1], key);
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody requestBody = RequestBody.create(mediaType, body);
emitter.onNext(rc.getBooksFromSearchHtml(OkHttpUtils.getHtml(url, requestBody, finalCharset)));
}else {
emitter.onNext(rc.getBooksFromSearchHtml(OkHttpUtils.getHtml(makeSearchUrl(rc.getSearchLink(), key), finalCharset)));
}
emitter.onComplete();
} catch (IOException e) {
e.printStackTrace();
emitter.onError(e);
}
});
}
public static String makeSearchUrl(String url, String key){
return url.replace("{key}", key);
}
/**
* 获取小说详细信息
*
* @param book
*/
public static Observable<Book> getBookInfo(final Book book, final BookInfoCrawler bic) {
String url;
if (StringHelper.isEmpty(book.getInfoUrl())){
url = book.getChapterUrl();
}else {
url = book.getInfoUrl();
}
return Observable.create(emitter -> {
emitter.onNext(bic.getBookInfo(OkHttpUtils.getHtml(url, bic.getCharset()), book));
emitter.onComplete();
});
}
/**
* 获取小说详细信息
*
@ -108,7 +167,11 @@ public class CommonApi extends BaseApi {
* @param callback
*/
public static void getBookInfo(final Book book, final BookInfoCrawler bic, final ResultCallback callback) {
getCommonReturnHtmlStringApi(book.getChapterUrl(), null, bic.getCharset(), new ResultCallback() {
String url = book.getInfoUrl();
if (StringHelper.isEmpty(url)){
url = book.getChapterUrl();
}
getCommonReturnHtmlStringApi(url, null, bic.getCharset(), false, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
callback.onFinish(bic.getBookInfo((String) o, book), 0);

@ -3,7 +3,7 @@ package xyz.fycz.myreader.webapi;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.callback.ResultCallback;
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;
@ -28,7 +28,7 @@ public class LanZousApi {
* @param callback
*/
public static void getUrl1(String lanZouUrl, final ResultCallback callback) {
getCommonReturnHtmlStringApi(lanZouUrl, null, "utf-8", new ResultCallback() {
getCommonReturnHtmlStringApi(lanZouUrl, null, "utf-8", true, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
callback.onFinish(getUrl1((String) o), code);
@ -48,7 +48,7 @@ public class LanZousApi {
* @param callback
*/
public static void getKey(String url, final ResultCallback callback) {
getCommonReturnHtmlStringApi(url, null, "utf-8", new ResultCallback() {
getCommonReturnHtmlStringApi(url, null, "utf-8", true, new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
callback.onFinish(getKey((String) o), code);

@ -1,4 +1,4 @@
package xyz.fycz.myreader.callback;
package xyz.fycz.myreader.webapi.callback;
import android.graphics.Bitmap;

@ -1,4 +1,4 @@
package xyz.fycz.myreader.callback;
package xyz.fycz.myreader.webapi.callback;
import xyz.fycz.myreader.entity.JsonModel;

@ -1,4 +1,4 @@
package xyz.fycz.myreader.callback;
package xyz.fycz.myreader.webapi.callback;
/**
* Created by zhao on 2016/4/16.

@ -3,7 +3,8 @@ package xyz.fycz.myreader.webapi.crawler;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.webapi.crawler.read.FYReadCrawler;
import java.util.ArrayList;
import java.util.HashMap;
@ -24,7 +25,7 @@ public class ReadCrawlerUtil {
if (searchSource == null) {
StringBuilder sb = new StringBuilder();
for (BookSource bookSource : BookSource.values()) {
if (bookSource.equals(BookSource.fynovel) || bookSource.equals(BookSource.local) || bookSource.equals(BookSource.biquge))
if (bookSource.equals(BookSource.fynovel) || bookSource.equals(BookSource.local))
continue;
sb.append(bookSource.toString());
sb.append(",");
@ -49,10 +50,6 @@ public class ReadCrawlerUtil {
if (searchSource == null) {
for (BookSource bookSource : BookSource.values()) {
if (bookSource.equals(BookSource.fynovel) || bookSource.equals(BookSource.local)) continue;
if (bookSource.equals(BookSource.biquge)) {
mSources.put(bookSource.text, true);
continue;
}
mSources.put(bookSource.text, false);
}
} else {
@ -73,6 +70,18 @@ public class ReadCrawlerUtil {
}
public static void resetReaderCrawlers(){
StringBuilder sb = new StringBuilder();
for (BookSource bookSource : BookSource.values()) {
if (bookSource.equals(BookSource.fynovel) || bookSource.equals(BookSource.local))
continue;
sb.append(bookSource.toString());
sb.append(",");
}
sb.deleteCharAt(sb.lastIndexOf(","));
SharedPreUtils.getInstance().putString("searchSource", sb.toString());
}
public static ReadCrawler getReadCrawler(String bookSource) {
ResourceBundle rb = ResourceBundle.getBundle("crawler");
try {

@ -1,4 +1,4 @@
package xyz.fycz.myreader.webapi.crawler;
package xyz.fycz.myreader.webapi.crawler.base;
import xyz.fycz.myreader.greendao.entity.Book;

@ -1,4 +1,4 @@
package xyz.fycz.myreader.webapi.crawler;
package xyz.fycz.myreader.webapi.crawler.base;
import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.greendao.entity.Book;

@ -1,9 +1,9 @@
package xyz.fycz.myreader.webapi.crawler;
package xyz.fycz.myreader.webapi.crawler.base;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import java.util.ArrayList;
@ -17,7 +17,7 @@ public interface ReadCrawler {
String getCharset();
String getSearchCharset();
String getNameSpace();
String getSearchKey();
Boolean isPost();
String getContentFormHtml(String html);
ArrayList<Chapter> getChaptersFromHtml(String html);
ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html);

@ -1,117 +0,0 @@
package xyz.fycz.myreader.webapi.crawler.find;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
import android.webkit.*;
import okhttp3.*;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.callback.ResultCallback;
import xyz.fycz.myreader.creator.DialogCreator;
import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.entity.bookstore.QDBook;
import xyz.fycz.myreader.entity.bookstore.RankBook;
import xyz.fycz.myreader.entity.bookstore.SortBook;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.util.IOUtils;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.CommonApi;
import xyz.fycz.myreader.webapi.crawler.FindCrawler;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @author fengyue
* @date 2020/9/16 22:01
*/
public class ABC extends FindCrawler {
private String rankUrl = "";
private String sortUrl = "";
private String[] sex = {"male", "female"};
private String aParam = "";
private String imgUrl = "";
private String defaultCookie = "";
private String yearmonthFormat = "";
private LinkedHashMap<String, String> rankName = new LinkedHashMap<>();
private LinkedHashMap<String, Integer> sortName = new LinkedHashMap<>();
private boolean isFemale;
private boolean isSort;
public ABC(boolean isFemale) {
this.isFemale = isFemale;
}
public ABC(boolean isFemale, boolean isSort) {
this.isFemale = isFemale;
this.isSort = isSort;
}
private void initMaleRankName() {
}
private void initSortNames() {
}
public List<BookType> getRankTypes() {
return null;
}
public void getRankBooks(BookType bookType, ResultCallback rc) {
}
private List<QDBook> getBooksFromJson(String json) {
return null;
}
@Override
public String getCharset() {
return null;
}
@Override
public String getFindName() {
return null;
}
@Override
public String getFindUrl() {
return null;
}
@Override
public boolean getTypePage(BookType curType, int page) {
return false;
}
@Override
public boolean hasImg() {
return true;
}
@Override
public List<BookType> getBookTypeList(String html) {
return null;
}
@Override
public List<Book> getRankBookList(String html) {
return null;
}
public void initCookie(Context mContext, ResultCallback rc) {
}
}

@ -0,0 +1,270 @@
package xyz.fycz.myreader.webapi.crawler.find;
import android.content.Context;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import xyz.fycz.myreader.application.MyApplication;
import xyz.fycz.myreader.webapi.callback.ResultCallback;
import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.entity.bookstore.QDBook;
import xyz.fycz.myreader.entity.bookstore.RankBook;
import xyz.fycz.myreader.entity.bookstore.SortBook;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.util.IOUtils;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.CommonApi;
import xyz.fycz.myreader.webapi.crawler.base.FindCrawler;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @author fengyue
* @date 2020/9/16 22:01
*/
public class QiDianMobileRank extends FindCrawler {
private String rankUrl = "https://m.qidian.com/majax/rank/{rankName}list?_csrfToken={cookie}&gender={sex}&pageNum={page}&catId=-1";
private String sortUrl = "https://m.qidian.com/majax/category/list?_csrfToken={cookie}&gender={sex}&pageNum={page}&orderBy=&catId={catId}&subCatId=";
private String[] sex = {"male", "female"};
private String yuepiaoParam = "&yearmonth={yearmonth}";
private String imgUrl = "https://bookcover.yuewen.com/qdbimg/349573/{bid}/150";
private String defaultCookie = "eXRDlZxmRDLvFAmdgzqvwWAASrxxp2WkVlH4ZM7e";
private String yearmonthFormat = "yyyyMM";
private LinkedHashMap<String, String> rankName = new LinkedHashMap<>();
private LinkedHashMap<String, Integer> sortName = new LinkedHashMap<>();
private boolean isFemale;
private boolean isSort;
public QiDianMobileRank(boolean isFemale) {
this.isFemale = isFemale;
}
public QiDianMobileRank(boolean isFemale, boolean isSort) {
this.isFemale = isFemale;
this.isSort = isSort;
}
private void initMaleRankName() {
if (!isFemale) {
rankName.put("风云榜", "yuepiao");
rankName.put("畅销榜", "hotsales");
rankName.put("阅读榜", "readIndex");
rankName.put("粉丝榜", "newfans");
rankName.put("推荐榜", "rec");
rankName.put("更新榜", "update");
rankName.put("签约榜", "sign");
rankName.put("新书榜", "newbook");
rankName.put("新人榜", "newauthor");
} else {
rankName.put("风云榜", "yuepiao");
rankName.put("阅读榜", "readIndex");
rankName.put("粉丝榜", "newfans");
rankName.put("推荐榜", "rec");
rankName.put("更新榜", "update");
rankName.put("收藏榜", "collect");
rankName.put("免费榜", "free");
}
}
private void initSortNames() {
/*
{value: -1, text: "全站"}
1: {value: 21, text: "玄幻"}
2: {value: 1, text: "奇幻"}
3: {value: 2, text: "武侠"}
4: {value: 22, text: "仙侠"}
5: {value: 4, text: "都市"}
6: {value: 15, text: "现实"}
7: {value: 6, text: "军事"}
8: {value: 5, text: "历史"}
9: {value: 7, text: "游戏"}
10: {value: 8, text: "体育"}
11: {value: 9, text: "科幻"}
12: {value: 10, text: "悬疑"}
13: {value: 12, text: "轻小说"}
*/
if (!isFemale) {
sortName.put("玄幻小说", 21);
sortName.put("奇幻小说", 1);
sortName.put("武侠小说", 2);
sortName.put("都市小说", 4);
sortName.put("现实小说", 15);
sortName.put("军事小说", 6);
sortName.put("历史小说", 5);
sortName.put("体育小说", 8);
sortName.put("科幻小说", 9);
sortName.put("悬疑小说", 10);
sortName.put("轻小说", 12);
sortName.put("短篇小说", 20076);
} else {
sortName.put("古代言情", 80);
sortName.put("仙侠奇缘", 81);
sortName.put("现代言情", 82);
sortName.put("烂漫青春", 83);
sortName.put("玄幻言情", 84);
sortName.put("悬疑推理", 85);
sortName.put("短篇小说", 30083);
sortName.put("科幻空间", 86);
sortName.put("游戏竞技", 88);
sortName.put("轻小说", 87);
sortName.put("现实生活", 30120);
}
}
public List<BookType> getRankTypes() {
if (!isSort) {
initMaleRankName();
} else {
initSortNames();
}
List<BookType> bookTypes = new ArrayList<>();
Set<String> names = !isSort ? rankName.keySet() : sortName.keySet();
for (String name : names) {
BookType bookType = new BookType();
bookType.setTypeName(name);
String url;
if (!isSort) {
url = rankUrl.replace("{rankName}", rankName.get(name));
} else {
url = sortUrl.replace("{catId}", sortName.get(name) + "");
}
url = url.replace("{sex}", !isFemale ? sex[0] : sex[1]);
SharedPreUtils spu = SharedPreUtils.getInstance();
String cookie = spu.getString("qdCookie", "");
if (!cookie.equals("")) {
url = url.replace("{cookie}", StringHelper.getSubString(cookie, "_csrfToken=", ";"));
} else {
url = url.replace("{cookie}", defaultCookie);
}
if ("风云榜".equals(name)) {
SimpleDateFormat sdf = new SimpleDateFormat(yearmonthFormat, Locale.CHINA);
String yearmonth = sdf.format(new Date());
url = url + yuepiaoParam.replace("{yearmonth}", yearmonth);
}
bookType.setUrl(url);
bookTypes.add(bookType);
}
return bookTypes;
}
public void getRankBooks(BookType bookType, ResultCallback rc) {
CommonApi.getCommonReturnHtmlStringApi(bookType.getUrl(), null, "UTF-8", true,
new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
rc.onFinish(getBooksFromJson((String) o), 1);
}
@Override
public void onError(Exception e) {
rc.onError(e);
}
});
}
private List<QDBook> getBooksFromJson(String json) {
List<QDBook> books = new ArrayList<>();
try {
JSONObject all = new JSONObject(json);
JSONObject data = all.getJSONObject("data");
JSONArray jsonBooks = data.getJSONArray("records");
for (int i = 0; i < jsonBooks.length(); i++) {
JSONObject jsonBook = jsonBooks.getJSONObject(i);
QDBook book = !isSort ? new RankBook() : new SortBook();
book.setbName(jsonBook.getString("bName"));
book.setbAuth(jsonBook.getString("bAuth"));
book.setBid(jsonBook.getString("bid"));
book.setCat(jsonBook.getString("cat"));
book.setCatId(jsonBook.getInt("catId"));
book.setCnt(jsonBook.getString("cnt"));
book.setDesc(jsonBook.getString("desc"));
book.setImg(imgUrl.replace("{bid}", jsonBook.getString("bid")));
if (!isSort) {
((RankBook) book).setRankCnt(jsonBook.getString("rankCnt"));
((RankBook) book).setRankNum(jsonBook.getInt("rankNum"));
}else {
((SortBook) book).setState(jsonBook.getString("state"));
}
books.add(book);
}
} catch (JSONException e) {
e.printStackTrace();
}
return books;
}
@Override
public String getCharset() {
return null;
}
@Override
public String getFindName() {
return !isFemale ? !isSort ? "排行榜[男生小说]" : "分类[男生小说]" : !isSort ? "排行榜[女生小说]" : "分类[女生小说]";
}
@Override
public String getFindUrl() {
return null;
}
@Override
public boolean getTypePage(BookType curType, int page) {
if (!isSort) {
if (page > 30) {
return true;
}
}else {
if (page > 5) {
return true;
}
}
String pageNum = curType.getUrl().substring(curType.getUrl().indexOf("pageNum=") + 8, curType.getUrl().indexOf("&catId"));
curType.setUrl(curType.getUrl().replace("pageNum=" + pageNum, "pageNum=" + page));
return false;
}
@Override
public boolean hasImg() {
return true;
}
@Override
public List<BookType> getBookTypeList(String html) {
return null;
}
@Override
public List<Book> getRankBookList(String html) {
return null;
}
public void initCookie(Context mContext, ResultCallback rc) {
MyApplication.getApplication().newThread(() -> {
BufferedReader br = null;
try {
br = new BufferedReader(new InputStreamReader(mContext.getAssets().open("_csrfToken.fy")));
StringBuilder assetText = new StringBuilder();
String line;
while ((line = br.readLine()) != null) {
assetText.append(line);
}
String[] _csrfTokens = assetText.toString().split(",");
Random random = new Random();
rc.onFinish(_csrfTokens[random.nextInt(_csrfTokens.length)], 1);
} catch (IOException e) {
rc.onError(e);
} finally {
IOUtils.close(br);
}
});
}
}

@ -0,0 +1,151 @@
package xyz.fycz.myreader.webapi.crawler.read;
import android.text.Html;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
public class Ben100ReadCrawler implements ReadCrawler, BookInfoCrawler {
public static final String NAME_SPACE = "https://www.100ben.net";
public static final String NOVEL_SEARCH = "https://www.100ben.net/plus/search.php?keyword={key}";
public static final String CHARSET = "UTF-8";
public static final String SEARCH_CHARSET = "utf-8";
@Override
public String getSearchLink() {
return NOVEL_SEARCH;
}
@Override
public String getCharset() {
return CHARSET;
}
@Override
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public Boolean isPost() {
return false;
}
@Override
public String getSearchCharset() {
return SEARCH_CHARSET;
}
/**
* 从html中获取章节正文
*
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Document doc = Jsoup.parse(html);
Element divContent = doc.getElementById("content");
if (divContent != null) {
String content = Html.fromHtml(divContent.html()).toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ");
return content;
} else {
return "";
}
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
try {
Element divList = doc.getElementById("dir");
int i = 0;
Elements elementsByTag = divList.getElementsByTag("dd");
for (int j = 0; j < elementsByTag.size(); j++) {
Element dd = elementsByTag.get(j);
Elements as = dd.getElementsByTag("a");
Element a = as.get(0);
String title = a.text();
Chapter chapter = new Chapter();
chapter.setNumber(i++);
chapter.setTitle(title);
String url = NAME_SPACE + a.attr("href");
chapter.setUrl(url);
chapters.add(chapter);
}
} catch (Exception e) {
e.printStackTrace();
}
return chapters;
}
/**
* 从搜索html中得到书列表
*
* @param html
* @return
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
// try {
Elements divs = doc.getElementsByClass("recommand");
Element div = divs.get(0);
Elements elementsByTag = div.getElementsByTag("li");
for (Element element : elementsByTag) {
Book book = new Book();
String name = element.getElementsByClass("titles").first().getElementsByTag("a").first().text();
book.setName(name);
String author = element.getElementsByClass("author").first().text().replace("作者:", "");
book.setAuthor(author);
String imgUrl = element.getElementsByTag("img").first().attr("src");
book.setImgUrl(imgUrl);
String chapterUrl = element.getElementsByClass("titles").first().getElementsByTag("a").first().attr("href");
book.setChapterUrl(NAME_SPACE + chapterUrl);
String desc = element.getElementsByClass("intro").first().text();
book.setDesc(desc);
book.setNewestChapterTitle("");
book.setSource(BookSource.ben100.toString());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
// } catch (Exception e) {
// e.printStackTrace();
// }
return books;
}
/**
* 获取书籍详细信息
* @param book
*/
public Book getBookInfo(String html, Book book){
Document doc = Jsoup.parse(html);
Element img = doc.getElementById("fmimg");
book.setImgUrl(img.getElementsByTag("img").get(0).attr("src"));
Element desc = doc.getElementById("intro");
book.setDesc(desc.getElementsByTag("p").get(0).text());
Element type = doc.getElementsByClass("con_top").get(0);
book.setType(type.getElementsByTag("a").get(2).text());
return book;
}
}

@ -1,4 +1,4 @@
package xyz.fycz.myreader.webapi.crawler;
package xyz.fycz.myreader.webapi.crawler.read;
import android.text.Html;
import org.jsoup.Jsoup;
@ -9,16 +9,17 @@ import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
public class BiQuGe44ReadCrawler implements ReadCrawler, BookInfoCrawler {
public static final String NAME_SPACE = "https://www.wqge.cc";
public static final String NOVEL_SEARCH = "https://www.wqge.cc/modules/article/search.php";
public static final String SEARCH_KEY = "searchkey";
public static final String NOVEL_SEARCH = "https://www.wqge.cc/modules/article/search.php?searchkey={key}";
public static final String CHARSET = "GBK";
public static final String SEARCH_CHARSET = "utf-8";
@Override
@ -35,12 +36,10 @@ public class BiQuGe44ReadCrawler implements ReadCrawler, BookInfoCrawler{
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public String getSearchKey() {
return SEARCH_KEY;
public Boolean isPost() {
return false;
}
@Override
public String getSearchCharset() {
return SEARCH_CHARSET;

@ -1,4 +1,4 @@
package xyz.fycz.myreader.webapi.crawler;
package xyz.fycz.myreader.webapi.crawler.read;
import android.text.Html;
import org.jsoup.Jsoup;
@ -10,8 +10,10 @@ import xyz.fycz.myreader.entity.bookstore.BookType;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.util.StringHelper;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
import java.util.List;
@ -21,8 +23,7 @@ import java.util.regex.Pattern;
public class BiQuGeReadCrawler implements ReadCrawler, BookInfoCrawler {
private static final String NAME_SPACE = "https://www.52bqg.net";
private static final String NOVEL_SEARCH = "https://www.52bqg.net/modules/article/search.php";
private static final String SEARCH_KEY = "searchkey";
private static final String NOVEL_SEARCH = "https://www.52bqg.net/modules/article/search.php?searchkey={key}";
private static final String CHARSET = "GBK";
public static final String SEARCH_CHARSET = "GBK";
@ -37,12 +38,10 @@ public class BiQuGeReadCrawler implements ReadCrawler, BookInfoCrawler {
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public String getSearchKey() {
return SEARCH_KEY;
public Boolean isPost() {
return false;
}
@Override
public String getCharset() {
return CHARSET;

@ -0,0 +1,139 @@
package xyz.fycz.myreader.webapi.crawler.read;
import android.text.Html;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
public class CansShu99ReadCrawler implements ReadCrawler {
public static final String NAME_SPACE = "http://www.99csw.com";
public static final String NOVEL_SEARCH = "http://www.99csw.com/book/search.php?type=all&keyword={key}";
public static final String CHARSET = "UTF-8";
public static final String SEARCH_CHARSET = "utf-8";
@Override
public String getSearchLink() {
return NOVEL_SEARCH;
}
@Override
public String getCharset() {
return CHARSET;
}
@Override
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public Boolean isPost() {
return false;
}
@Override
public String getSearchCharset() {
return SEARCH_CHARSET;
}
/**
* 从html中获取章节正文
*
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Document doc = Jsoup.parse(html);
Element divContent = doc.getElementById("content");
if (divContent != null) {
String content = Html.fromHtml(divContent.html()).toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ");
return content;
} else {
return "";
}
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
try {
Element divList = doc.getElementById("dir");
int i = 0;
Elements elementsByTag = divList.getElementsByTag("dd");
for (int j = 0; j < elementsByTag.size(); j++) {
Element dd = elementsByTag.get(j);
Elements as = dd.getElementsByTag("a");
Element a = as.get(0);
String title = a.text();
Chapter chapter = new Chapter();
chapter.setNumber(i++);
chapter.setTitle(title);
String url = NAME_SPACE + a.attr("href");
chapter.setUrl(url);
chapters.add(chapter);
}
} catch (Exception e) {
e.printStackTrace();
}
return chapters;
}
/**
* 从搜索html中得到书列表
*
* @param html
* @return
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
// try {
Elements divs = doc.getElementsByClass("list_box");
Element div = divs.get(0);
Elements elementsByTag = div.getElementsByTag("li");
for (Element element : elementsByTag) {
Book book = new Book();
String name = element.getElementsByTag("h2").first().getElementsByTag("a").first().text();
book.setName(name);
String author = element.getElementsByTag("h4").first().getElementsByTag("a").first().text();
book.setAuthor(author);
String type = element.getElementsByTag("h4").get(1).getElementsByTag("a").text();
book.setType(type);
String desc = element.getElementsByClass("intro").first().text();
book.setDesc(desc);
String imgUrl = element.getElementsByTag("img").first().attr("src");
book.setImgUrl("http:" + imgUrl);
String chapterUrl = element.getElementsByTag("h2").first().getElementsByTag("a").first().attr("href");
book.setChapterUrl(NAME_SPACE + chapterUrl);
book.setNewestChapterTitle("");
book.setSource(BookSource.cangshu99.toString());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
// } catch (Exception e) {
// e.printStackTrace();
// }
return books;
}
}

@ -0,0 +1,130 @@
package xyz.fycz.myreader.webapi.crawler.read;
import android.text.Html;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
public class ChaoXingReadCrawler implements ReadCrawler {
public static final String NAME_SPACE = "http://yz4.chaoxing.com";
public static final String NOVEL_SEARCH = "http://yz4.chaoxing.com/circlemarket/getsearch,start=0&size=25&sw={key}&channelId=52";
public static final String CHAPTERS_URL = "https://special.zhexuezj.cn/mobile/mooc/tocourse/";
public static final String DESC = "★★★ 超星·出版 ★★★\n★★★ 本书暂无简介 ★★★";
public static final String CHARSET = "UTF-8";
public static final String SEARCH_CHARSET = "UTF-8";
@Override
public String getSearchLink() {
return NOVEL_SEARCH;
}
@Override
public String getCharset() {
return CHARSET;
}
@Override
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public Boolean isPost() {
return true;
}
@Override
public String getSearchCharset() {
return SEARCH_CHARSET;
}
/**
* 从html中获取章节正文
*
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Document doc = Jsoup.parse(html);
Element divContent = doc.getElementById("contentBox");
Elements ps = divContent.getElementsByTag("p");
StringBuilder sb = new StringBuilder();
for (Element p : ps){
String content = Html.fromHtml(p.html()).toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ");
sb.append(content);
sb.append("\n");
}
return sb.toString();
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
Element divList = doc.getElementsByClass("con").first();
Elements elementsByTag = divList.getElementsByTag("a");
int i = 0;
for (Element a : elementsByTag) {
String title = a.text();
String url = a.attr("attr");
Chapter chapter = new Chapter();
chapter.setNumber(i++);
chapter.setTitle(title);
chapter.setUrl(url);
chapters.add(chapter);
}
return chapters;
}
/**
* 从搜索html中得到书列表
*
* @param json
* @return
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String json) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
try {
JSONArray booksArray = new JSONArray(json);
for (int i = 0; i < booksArray.length(); i++) {
JSONObject bookJson = booksArray.getJSONObject(i);
Book book = new Book();
book.setName(bookJson.getString("name"));
book.setAuthor(bookJson.getString("author"));
book.setImgUrl(bookJson.getString("coverUrl"));
book.setNewestChapterTitle("");
book.setChapterUrl(CHAPTERS_URL + bookJson.getInt("course_Id"));
book.setDesc(DESC);
book.setSource(BookSource.chaoxing.toString());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
} catch (JSONException e) {
e.printStackTrace();
}
return books;
}
}

@ -1,4 +1,4 @@
package xyz.fycz.myreader.webapi.crawler;
package xyz.fycz.myreader.webapi.crawler.read;
import android.text.Html;
@ -11,7 +11,8 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
import java.util.regex.Matcher;
@ -23,8 +24,7 @@ import java.util.regex.Pattern;
public class FYReadCrawler implements ReadCrawler {
public static final String NAME_SPACE = "https://novel.fycz.xyz";
public static final String NOVEL_SEARCH = "https://novel.fycz.xyz/search.html";
public static final String SEARCH_KEY = "keyword";
public static final String NOVEL_SEARCH = "https://novel.fycz.xyz/search.html?keyword={key}";
public static final String CHARSET = "utf-8";
public static final String SEARCH_CHARSET = "utf-8";
@Override
@ -40,12 +40,10 @@ public class FYReadCrawler implements ReadCrawler{
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public String getSearchKey() {
return SEARCH_KEY;
public Boolean isPost() {
return false;
}
@Override
public String getSearchCharset() {
return SEARCH_CHARSET;

@ -0,0 +1,127 @@
package xyz.fycz.myreader.webapi.crawler.read;
import android.text.Html;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
public class JiuTaoReadCrawler implements ReadCrawler {
public static final String NAME_SPACE = "https://www.9txs.com";
public static final String NOVEL_SEARCH = "https://www.9txs.com/search.html,searchkey={key}";
public static final String CHARSET = "UTF-8";
public static final String SEARCH_CHARSET = "UTF-8";
@Override
public String getSearchLink() {
return NOVEL_SEARCH;
}
@Override
public String getCharset() {
return CHARSET;
}
@Override
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public Boolean isPost() {
return true;
}
@Override
public String getSearchCharset() {
return SEARCH_CHARSET;
}
/**
* 从html中获取章节正文
*
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Document doc = Jsoup.parse(html);
Element divContent = doc.getElementById("content");
if (divContent != null) {
String content = Html.fromHtml(divContent.html()).toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ").replaceAll("您可以在.*最新章节!|\\\\", "");
return content;
} else {
return "";
}
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
Element divList = doc.getElementsByClass("read").first();
Elements elementsByTag = divList.getElementsByTag("a");
int i = 0;
for (int j = 12; j < elementsByTag.size(); j++) {
Element a = elementsByTag.get(j);
String title = a.text();
String url = a.attr("href");
Chapter chapter = new Chapter();
chapter.setNumber(i++);
chapter.setTitle(title);
chapter.setUrl(NAME_SPACE + url);
chapters.add(chapter);
}
return chapters;
}
/**
* 从搜索html中得到书列表
*
* @param html
* @return
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
// try {
Element div = doc.getElementsByClass("library").first();
Elements lis = div.getElementsByTag("li");
for (Element li : lis){
Elements as = li.getElementsByTag("a");
Book book = new Book();
book.setName(as.get(1).text());
book.setAuthor(as.get(2).text());
book.setType(as.get(3).text());
book.setNewestChapterTitle(as.get(4).text().replace("最新章节:", ""));
book.setDesc(li.getElementsByClass("intro").first().text());
book.setImgUrl(li.getElementsByTag("img").attr("src"));
book.setChapterUrl(NAME_SPACE + as.get(1).attr("href").replace(".html", "/"));
book.setSource(BookSource.jiutao.toString());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
// } catch (Exception e) {
// e.printStackTrace();
// }
return books;
}
}

@ -0,0 +1,140 @@
package xyz.fycz.myreader.webapi.crawler.read;
import android.text.Html;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import xyz.fycz.myreader.entity.SearchBookBean;
import xyz.fycz.myreader.enums.BookSource;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.model.mulvalmap.ConcurrentMultiValueMap;
import xyz.fycz.myreader.webapi.crawler.base.BookInfoCrawler;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import java.util.ArrayList;
public class MiQuReadCrawler implements ReadCrawler, BookInfoCrawler {
public static final String NAME_SPACE = "https://www.meegoq.com/";
public static final String NOVEL_SEARCH = "https://www.meegoq.com/search.htm?keyword={key}";
public static final String CHARSET = "UTF-8";
public static final String SEARCH_CHARSET = "UTF-8";
@Override
public String getSearchLink() {
return NOVEL_SEARCH;
}
@Override
public String getCharset() {
return CHARSET;
}
@Override
public String getNameSpace() {
return NAME_SPACE;
}
@Override
public Boolean isPost() {
return false;
}
@Override
public String getSearchCharset() {
return SEARCH_CHARSET;
}
/**
* 从html中获取章节正文
*
* @param html
* @return
*/
public String getContentFormHtml(String html) {
Document doc = Jsoup.parse(html);
Element divContent = doc.getElementById("content");
if (divContent != null) {
String content = Html.fromHtml(divContent.html()).toString();
char c = 160;
String spaec = "" + c;
content = content.replace(spaec, " ").replace("applyChapterSetting();", "");
return content;
} else {
return "";
}
}
/**
* 从html中获取章节列表
*
* @param html
* @return
*/
public ArrayList<Chapter> getChaptersFromHtml(String html) {
ArrayList<Chapter> chapters = new ArrayList<>();
Document doc = Jsoup.parse(html);
Element divList = doc.getElementsByClass("mulu").first();
Elements elementsByTag = divList.getElementsByTag("a");
int i = 0;
for (int j = 9; j < elementsByTag.size(); j++) {
Element a = elementsByTag.get(j);
String title = a.text();
String url = "http:" + a.attr("href");
Chapter chapter = new Chapter();
chapter.setNumber(i++);
chapter.setTitle(title);
chapter.setUrl(url);
chapters.add(chapter);
}
return chapters;
}
/**
* 从搜索html中得到书列表
*
* @param html
* @return
*/
public ConcurrentMultiValueMap<SearchBookBean, Book> getBooksFromSearchHtml(String html) {
ConcurrentMultiValueMap<SearchBookBean, Book> books = new ConcurrentMultiValueMap<>();
Document doc = Jsoup.parse(html);
Elements divs = doc.getElementsByClass("lastest");
Element div = divs.get(0);
Elements elementsByTag = div.getElementsByTag("li");
for (int i = 1; i < elementsByTag.size() - 1; i++) {
Element element = elementsByTag.get(i);
Book book = new Book();
Element info = element.getElementsByClass("n2").first();
book.setName(info.text());
book.setInfoUrl("http:" + info.getElementsByTag("a").attr("href"));
book.setChapterUrl("http:" + info.getElementsByTag("a").attr("href").replace("info", "book"));
book.setAuthor(element.getElementsByClass("a2").first().text());
book.setType(element.getElementsByClass("nt").first().text());
book.setNewestChapterTitle(element.getElementsByClass("c2").first().text());
book.setSource(BookSource.miqu.toString());
SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor());
books.add(sbb, book);
}
return books;
}
/**
* 获取书籍详细信息
*
* @param book
*/
public Book getBookInfo(String html, Book book) {
Document doc = Jsoup.parse(html);
Element img = doc.getElementsByClass("cover").first();
book.setImgUrl(img.getElementsByTag("img").get(0).attr("src"));
String desc = doc.select("meta[property=og:description]").attr("content");
book.setDesc(desc);
return book;
}
}

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

Loading…
Cancel
Save