支持epub阅读(仅支持文字)

pull/5/head
fengyuecanzhu 4 years ago
parent b983ca07c6
commit 0fe7b59989
  1. 4
      .idea/assetWizardSettings.xml
  2. 5
      app/build.gradle
  3. 1
      app/src/main/AndroidManifest.xml
  4. 9
      app/src/main/java/xyz/fycz/myreader/common/APPCONST.java
  5. 2
      app/src/main/java/xyz/fycz/myreader/common/URLCONST.java
  6. 31
      app/src/main/java/xyz/fycz/myreader/entity/Quotation.java
  7. 13
      app/src/main/java/xyz/fycz/myreader/ui/activity/AdSettingActivity.java
  8. 25
      app/src/main/java/xyz/fycz/myreader/ui/activity/DonateActivity.java
  9. 3
      app/src/main/java/xyz/fycz/myreader/ui/activity/FileSystemActivity.java
  10. 203
      app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java
  11. 6
      app/src/main/java/xyz/fycz/myreader/ui/activity/SearchBookActivity.java
  12. 14
      app/src/main/java/xyz/fycz/myreader/ui/fragment/FileCategoryFragment.java
  13. 46
      app/src/main/java/xyz/fycz/myreader/ui/fragment/FindFragment.java
  14. 2
      app/src/main/java/xyz/fycz/myreader/ui/fragment/LocalBookFragment.java
  15. 18
      app/src/main/java/xyz/fycz/myreader/util/media/LocalFileLoader.java
  16. 8
      app/src/main/java/xyz/fycz/myreader/util/utils/AdUtils.java
  17. 6
      app/src/main/java/xyz/fycz/myreader/util/utils/FileUtils.java
  18. 56
      app/src/main/java/xyz/fycz/myreader/widget/page/BookChapterBean.java
  19. 328
      app/src/main/java/xyz/fycz/myreader/widget/page/EpubPageLoader.java
  20. 122
      app/src/main/java/xyz/fycz/myreader/widget/page/LocalPageLoader.java
  21. 76
      app/src/main/java/xyz/fycz/myreader/widget/page/NetPageLoader.java
  22. 16
      app/src/main/java/xyz/fycz/myreader/widget/page/PageLoader.java
  23. 7
      app/src/main/java/xyz/fycz/myreader/widget/page/PageView.java
  24. 4
      app/src/main/res/drawable/ic_edit.xml
  25. 27
      app/src/main/res/drawable/ic_quotations.xml
  26. 0
      app/src/main/res/drawable/shape_runway.xml
  27. 10
      app/src/main/res/layout/activity_ad_setting.xml
  28. 8
      app/src/main/res/layout/activity_read.xml
  29. 4
      app/src/main/res/layout/activity_splash.xml
  30. 328
      app/src/main/res/layout/fragment_find.xml
  31. 2
      app/src/main/res/layout/menu_book.xml
  32. 2
      app/src/main/res/layout/menu_book_local.xml

@ -14,8 +14,8 @@
<option name="values">
<map>
<entry key="assetSourceType" value="FILE" />
<entry key="outputName" value="ic_right_arrow" />
<entry key="sourceFile" value="F:\SVG图标\右箭头.svg" />
<entry key="outputName" value="ic_yulu" />
<entry key="sourceFile" value="F:\SVG图标\留言板.svg" />
</map>
</option>
</PersistentState>

@ -213,6 +213,11 @@ dependencies {
//XXPermissions
implementation 'com.github.getActivity:XXPermissions:11.2'
//epub
implementation('com.positiondev.epublib:epublib-core:3.1') {
exclude group: 'org.slf4j'
exclude group: 'xmlpull'
}
}
greendao {

@ -109,6 +109,7 @@
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
<data android:mimeType="application/epub+zip"/>
</intent-filter>
</activity>
<activity android:name=".ui.activity.FontsActivity" />

@ -31,6 +31,7 @@ public class APPCONST {
public static final String FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/";
public static final String SHARE_FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/share/";
public static String LOG_DIR = FileUtils.getCachePath() + "/log/";
public static String COVER_DIR = FileUtils.getCachePath() + "/covers/";
public static final String BG_FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/bg/";
public static String TEM_FILE_DIR = FileUtils.getCachePath() + "/tem/";
public static final String BACKUP_FILE_DIR = Environment.getExternalStorageDirectory() + "/FYReader/backup/";
@ -124,12 +125,4 @@ public class APPCONST {
public static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4168.3 Safari/537.36";
private static Boolean hasStoragePermission = null;
public static boolean hasStoragePermission() {
if (hasStoragePermission == null) {
hasStoragePermission = XXPermissions.isGranted(App.getmContext(), STORAGE_PERMISSIONS);
}
return hasStoragePermission;
}
}

@ -29,5 +29,7 @@ public class URLCONST {
public static final String WX_ZSM = DONATE + "/wx_zsm.png";
public static final String ZFB_SKM = DONATE + "/zfb_skm.jpg";
public static final String QQ_SKM = DONATE + "/qq_skm.png";
public static final String QUOTATION = "https://v1.hitokoto.cn/?encode=json&charset=utf-8";
}

@ -0,0 +1,31 @@
package xyz.fycz.myreader.entity;
/**
* @author fengyue
* @date 2021/6/12 20:55
*/
public class Quotation {
private String hitokoto;
private String from;
public String getHitokoto() {
return hitokoto;
}
public void setHitokoto(String hitokoto) {
this.hitokoto = hitokoto;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
@Override
public String toString() {
return hitokoto + " --- " + from;
}
}

@ -1,5 +1,6 @@
package xyz.fycz.myreader.ui.activity;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
@ -10,10 +11,12 @@ import com.kongzue.dialogx.dialogs.BottomMenu;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.App;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.base.observer.MySingleObserver;
import xyz.fycz.myreader.databinding.ActivityAdSettingBinding;
import xyz.fycz.myreader.ui.dialog.LoadingDialog;
import xyz.fycz.myreader.ui.dialog.MyAlertDialog;
import xyz.fycz.myreader.util.help.DateHelper;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.ToastUtils;
@ -81,6 +84,7 @@ public class AdSettingActivity extends BaseActivity {
binding.scAd.setChecked(aBoolean);
if (aBoolean) {
binding.llAdSetting.setVisibility(View.VISIBLE);
binding.rlFlowAdCount.setVisibility(App.isDebug() ? View.VISIBLE : View.GONE);
}
loadingDialog.dismiss();
}
@ -93,6 +97,15 @@ public class AdSettingActivity extends BaseActivity {
String curAdTimesStr = getAdTimesStr(curAdTimes);
binding.tvSplashCurAdTimes.setText(getString(R.string.splash_cur_ad_times, curAdTimesStr, curAdCount + "次"));
binding.scBookDetailAd.setChecked(bookDetailAd);
binding.rlFlowAdCount.setOnClickListener(v -> {
int[] n = new int[1];
MyAlertDialog.createInputDia(this, "", "", "",
true, 1, (MyAlertDialog.onInputChangeListener) text -> {
n[0] = Integer.parseInt(text);
}, (dialog, which) -> {
SharedPreUtils.getInstance().putInt("flowAdCount", n[0]);
});
});
}
@Override

@ -16,10 +16,12 @@ import java.util.List;
import io.reactivex.annotations.NonNull;
import xyz.fycz.myreader.R;
import xyz.fycz.myreader.application.App;
import xyz.fycz.myreader.base.BaseActivity;
import xyz.fycz.myreader.base.observer.MySingleObserver;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.databinding.ActivityDonateBinding;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.AdUtils;
@ -60,25 +62,30 @@ public class DonateActivity extends BaseActivity {
private void initAd() {
binding.llAdSupport.setVisibility(View.VISIBLE);
new DdSdkFlowAd().getFlowViews(DonateActivity.this, 6, 1, new DdSdkFlowAd.FlowCallback() {
int flowAdCount = SharedPreUtils.getInstance().getInt("flowAdCount", 2);
int count = App.isDebug() ? flowAdCount : 1;
new DdSdkFlowAd().getFlowViews(DonateActivity.this, 6, count, new DdSdkFlowAd.FlowCallback() {
// 信息流广告拉取完毕后返回的 views
@Override
public void getFlowViews(List<View> views) {
Log.i(TAG, "信息流广告拉取完毕后返回了" + views.size() + "个view");
binding.llAdSupport.addView(views.get(0), 2);
Log.d(TAG, "信息流广告拉取完毕后返回了" + views.size() + "个view");
for (int i = 0; i < views.size(); i++) {
View view = views.get(i);
binding.llAdSupport.addView(view, i + 2);
}
}
// 信息流广告展示后调用
@Override
public void show() {
AdUtils.adRecord("flow","adShow");
Log.i(TAG, "信息流广告展示成功");
Log.d(TAG, "信息流广告展示成功");
}
// 广告拉取失败调用
@Override
public void error(String msg) {
Log.i(TAG, "广告拉取失败\n" + msg);
Log.d(TAG, "广告拉取失败\n" + msg);
}
});
}
@ -92,13 +99,13 @@ public class DonateActivity extends BaseActivity {
DdSdkRewardAd.show(this, new DdSdkRewardAd.DdSdkRewardCallback() {
@Override
public void show() {
Log.i(TAG, "激励视频展示成功");
Log.d(TAG, "激励视频展示成功");
AdUtils.adRecord("rewardVideo","adShow");
}
@Override
public void click() {
Log.i(TAG, "激励视频被点击");
Log.d(TAG, "激励视频被点击");
AdUtils.adRecord("rewardVideo","adClick");
}
@ -108,13 +115,13 @@ public class DonateActivity extends BaseActivity {
@Override
public void skip() {
Log.i(TAG, "激励视频被跳过");
Log.d(TAG, "激励视频被跳过");
AdUtils.adRecord("rewardVideo","adSkip");
}
@Override
public void finishCountdown() {
Log.i(TAG, "激励视频计时完成");
Log.d(TAG, "激励视频计时完成");
AdUtils.adRecord("rewardVideo","adFinishCount");
}
});

@ -21,6 +21,7 @@ import xyz.fycz.myreader.ui.fragment.BaseFileFragment;
import xyz.fycz.myreader.ui.fragment.FileCategoryFragment;
import xyz.fycz.myreader.ui.fragment.LocalBookFragment;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.FileUtils;
/**
@ -187,7 +188,7 @@ public class FileSystemActivity extends BaseTabActivity {
if (!file.exists()) continue;
Book book = new Book();
book.setName(file.getName().replace(".txt", ""));
book.setName(file.getName().replace(FileUtils.SUFFIX_EPUB, "").replace(FileUtils.SUFFIX_TXT, ""));
book.setChapterUrl(file.getAbsolutePath());
book.setType("本地书籍");
book.setHistoryChapterId("未开始阅读");

@ -15,7 +15,6 @@ import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.speech.tts.TextToSpeech;
import android.text.TextUtils;
import android.util.Log;
@ -50,7 +49,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.schedulers.Schedulers;
import xyz.fycz.myreader.ActivityManage;
@ -64,8 +62,8 @@ import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.databinding.ActivityReadBinding;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.enums.Font;
import xyz.fycz.myreader.enums.LocalBookSource;
import xyz.fycz.myreader.greendao.DbManager;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.entity.BookMark;
@ -92,26 +90,23 @@ import xyz.fycz.myreader.ui.popmenu.CustomizeComMenu;
import xyz.fycz.myreader.ui.popmenu.CustomizeLayoutMenu;
import xyz.fycz.myreader.ui.popmenu.ReadSettingMenu;
import xyz.fycz.myreader.util.BrightUtil;
import xyz.fycz.myreader.util.help.DateHelper;
import xyz.fycz.myreader.util.ShareUtils;
import xyz.fycz.myreader.util.SharedPreUtils;
import xyz.fycz.myreader.util.help.StringHelper;
import xyz.fycz.myreader.util.SystemUtil;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.help.DateHelper;
import xyz.fycz.myreader.util.help.StringHelper;
import xyz.fycz.myreader.util.notification.NotificationClickReceiver;
import xyz.fycz.myreader.util.notification.NotificationUtil;
import xyz.fycz.myreader.util.utils.ColorUtil;
import xyz.fycz.myreader.util.utils.NetworkUtils;
import xyz.fycz.myreader.util.utils.RxUtils;
import xyz.fycz.myreader.util.utils.ScreenUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
import xyz.fycz.myreader.util.utils.SystemBarUtils;
import xyz.fycz.myreader.webapi.BookApi;
import xyz.fycz.myreader.webapi.ResultCallback;
import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.widget.BubblePopupView;
import xyz.fycz.myreader.widget.page.LocalPageLoader;
import xyz.fycz.myreader.widget.page.PageLoader;
import xyz.fycz.myreader.widget.page.PageMode;
import xyz.fycz.myreader.widget.page.PageView;
@ -133,7 +128,7 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
/***************************variable*****************************/
private Book mBook;
private List<Book> aBooks;
private ArrayList<Chapter> mChapters = new ArrayList<>();
private List<Chapter> mChapters = new ArrayList<>();
private ChapterService mChapterService;
private BookService mBookService;
private BookMarkService mBookMarkService;
@ -212,55 +207,7 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
}
};
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
init();
break;
case 2:
try {
int chapterPos = msg.arg1;
int pagePos = msg.arg2;
mPageLoader.skipToChapter(chapterPos);
mPageLoader.skipToPage(pagePos);
} catch (Exception e) {
//ToastUtils.showError("章节跳转失败,请截图联系作者。\n" + e.getMessage());
e.printStackTrace();
}
break;
case 3:
break;
case 4:
saveLastChapterReadPosition();
screenOffTimerStart();
initMenu();
break;
case 5:
if (mPageLoader != null) {
mPageLoader.refreshUi();
}
break;
case 6:
mPageLoader.setmStatus(PageLoader.STATUS_LOADING);
break;
case 7:
ToastUtils.showWarring("无网络连接!");
mPageLoader.chapterError("无网络连接!");
break;
case 8:
binding.pbLoading.setVisibility(View.GONE);
break;
case 9:
ToastUtils.showInfo("正在后台缓存书籍,具体进度可查看通知栏!");
notificationUtil.requestNotificationPermissionDialog(ReadActivity.this);
break;
}
}
};
private Handler mHandler = new Handler();
@Override
protected void bindView() {
@ -335,6 +282,9 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
if (pagePos != -1 && chapterPos != -1) {
mBook.setHisttoryChapterNum(chapterPos);
mBook.setLastReadPosition(pagePos);
} else {
pagePos = mBook.getLastReadPosition();
chapterPos = mBook.getHisttoryChapterNum();
}
if (SharedPreUtils.getInstance().getBoolean(getString(R.string.isNightFS), false)) {
mSetting.setDayStyle(!ColorUtil.isColorLight(getResources().getColor(R.color.textPrimary)));
@ -373,7 +323,6 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
if (!mSetting.isBrightFollowSystem()) {
BrightUtil.setBrightness(this, mSetting.getBrightProgress());
}
binding.pbLoading.setVisibility(View.VISIBLE);
initEyeView();
initSettingListener();
initTopMenu();
@ -437,12 +386,26 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
new PageLoader.OnPageChangeListener() {
@Override
public void onChapterChange(int pos) {
chapterPos = pos;
mBook.setHistoryChapterId(mChapters.get(pos).getTitle());
recordReadTime();
}
@Override
public void onCategoryFinish(List<Chapter> chapters) {
mChapters = chapters;
mBook.setNoReadNum(0);
mBook.setChapterTotalNum(chapters.size());
mBook.setNewestChapterTitle(chapters.get(chapters.size() - 1).getTitle());
if (hasChangeSource) {
mBookService.matchHistoryChapterPos(mBook, mChapters);
skipToChapterAndPage(mBook.getHisttoryChapterNum(), mBook.getLastReadPosition());
hasChangeSource = false;
mChapterService.addChapters(chapters);
}
mBookService.updateEntity(mBook);
loadFinish = true;
invalidateOptionsMenu();
}
@Override
@ -452,7 +415,10 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
@Override
public void onPageChange(int pos, boolean resetRead) {
mHandler.sendMessage(mHandler.obtainMessage(4));
pagePos = pos;
saveLastChapterReadPosition();
screenOffTimerStart();
initMenu();
recordReadTime();
if (ReadAloudService.running) {
if (mPageLoader.hasChapterData(mChapters.get(mPageLoader.getChapterPos()))) {
@ -617,7 +583,7 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
}
//保存最近阅读时间
mBook.setLastReadTime(DateHelper.getLongDate());
getData();
init();
}
@ -723,9 +689,6 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
autoPageStop();
}
ReadAloudService.stop(this);
for (int i = 0; i < 9; i++) {
mHandler.removeMessages(i + 1);
}
if (mPageLoader != null) {
mPageLoader.closeBook();
mPageLoader = null;
@ -843,7 +806,9 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
boolean needRefresh = data.getBooleanExtra(APPCONST.RESULT_NEED_REFRESH, false);
boolean upMenu = data.getBooleanExtra(APPCONST.RESULT_UP_MENU, false);
if (needRefresh) {
mHandler.sendEmptyMessage(5);
if (mPageLoader != null) {
mPageLoader.refreshUi();
}
}
if (upMenu) {
initTopMenu();
@ -904,19 +869,16 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
screenOffTimerStart();
mPageLoader.init();
mPageLoader.refreshChapterList();
loadFinish = true;
invalidateOptionsMenu();
mHandler.sendMessage(mHandler.obtainMessage(8));
}
private void initMenu() {
if (mChapters != null && mChapters.size() != 0) {
Chapter curChapter = mChapters.get(mPageLoader.getChapterPos());
Chapter curChapter = mChapters.get(chapterPos);
String url = curChapter.getUrl();
binding.tvChapterTitleTop.setText(curChapter.getTitle());
binding.tvChapterUrl.setText(StringHelper.isEmpty(url) ? curChapter.getId() :
NetworkUtils.getAbsoluteURL(mReadCrawler.getNameSpace(), url));
binding.readSbChapterProgress.setProgress(mPageLoader.getPagePos());
binding.readSbChapterProgress.setProgress(pagePos);
binding.readSbChapterProgress.setMax(mPageLoader.getAllPagePos() - 1);
binding.readTvPageTip.setText(String.format("%s/%s",
binding.readSbChapterProgress.getProgress() + 1, binding.readSbChapterProgress.getMax() + 1));
@ -1023,85 +985,6 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
}, (dialog, which) -> dialog.dismiss());
}
/************************章节相关*************************/
/**
* 章节数据网络同步
*/
private void getData() {
mChapters = (ArrayList<Chapter>) mChapterService.findBookAllChapterByBookId(mBook.getId());
if (!isCollected || mChapters.size() == 0 || ("本地书籍".equals(mBook.getType()) && !ChapterService.isChapterCached(mBook.getId(), mChapters.get(0).getTitle())
)) {
if ("本地书籍".equals(mBook.getType())) {
if (!new File(mBook.getChapterUrl()).exists()) {
ToastUtils.showWarring("书籍缓存为空且源文件不存在,书籍加载失败!");
finish();
return;
}
if (mChapters.size() != 0 && mChapters.get(0).getEnd() > 0) {
initChapters();
return;
}
((LocalPageLoader) mPageLoader).loadChapters(new ResultCallback() {
@Override
public void onFinish(Object o, int code) {
ArrayList<Chapter> chapters = (ArrayList<Chapter>) o;
mBook.setChapterTotalNum(chapters.size());
mBook.setNewestChapterTitle(chapters.get(chapters.size() - 1).getTitle());
mBookService.updateEntity(mBook);
if (mChapters.size() == 0) {
updateAllOldChapterData(chapters);
}
initChapters();
mHandler.sendMessage(mHandler.obtainMessage(1));
}
@Override
public void onError(Exception e) {
e.printStackTrace();
mChapters.clear();
initChapters();
mHandler.sendMessage(mHandler.obtainMessage(1));
}
});
} else {
mPageLoader.setmStatus(PageLoader.STATUS_LOADING_CHAPTER);
BookApi.getBookChapters(mBook, mReadCrawler).flatMap(chapters -> Observable.create(emitter -> {
updateAllOldChapterData(chapters);
initChapters();
emitter.onNext(chapters);
emitter.onComplete();
})).compose(RxUtils::toSimpleSingle).subscribe(new MyObserver<Object>() {
@Override
public void onNext(@NotNull Object o) {
mPageLoader.setmStatus(PageLoader.STATUS_LOADING);
}
@Override
public void onError(Throwable e) {
mHandler.sendMessage(mHandler.obtainMessage(1));
if (App.isDebug()) e.printStackTrace();
}
});
}
} else {
initChapters();
}
}
/**
* 更新所有章节
*
* @param newChapters
*/
private void updateAllOldChapterData(List<Chapter> newChapters) {
for (Chapter newChapter : newChapters) {
newChapter.setId(StringHelper.getStringRandom(25));
newChapter.setBookId(mBook.getId());
mChapters.add(newChapter);
// mChapterService.addChapter(newChapters.get(j));
}
mChapterService.addChapters(mChapters);
}
/**
* 初始化章节
@ -1114,7 +997,6 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
}
if (mChapters.size() == 0) {
ToastUtils.showWarring("该书查询不到任何章节");
mHandler.sendMessage(mHandler.obtainMessage(8));
} else {
if (mBook.getHisttoryChapterNum() < 0) {
mBook.setHisttoryChapterNum(0);
@ -1122,14 +1004,11 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
mBook.setHisttoryChapterNum(mChapters.size() - 1);
}
if ("本地书籍".equals(mBook.getType())) {
mHandler.sendMessage(mHandler.obtainMessage(1));
return;
}
if (hasChangeSource) {
mBookService.matchHistoryChapterPos(mBook, mChapters);
}
mHandler.sendMessage(mHandler.obtainMessage(1));
mHandler.sendMessage(mHandler.obtainMessage(4));
}
}
@ -1140,7 +1019,7 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
* @param chapterPos
* @param pagePos
*/
private void skipToChapterAndPage(final int chapterPos, final int pagePos) {
private void skipToChapterAndPage(int chapterPos, int pagePos) {
mPageLoader.setPrev(false);
if (StringHelper.isEmpty(mChapters.get(chapterPos).getContent())) {
if ("本地书籍".equals(mBook.getType())) {
@ -1148,7 +1027,13 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
return;
}
}
mHandler.sendMessage(mHandler.obtainMessage(2, chapterPos, pagePos));
try {
mPageLoader.skipToChapter(chapterPos);
mPageLoader.skipToPage(pagePos);
} catch (Exception e) {
//ToastUtils.showError("章节跳转失败,请截图联系作者。\n" + e.getMessage());
e.printStackTrace();
}
}
/**
@ -1156,11 +1041,12 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
*/
public void saveLastChapterReadPosition() {
if (!StringHelper.isEmpty(mBook.getId()) && mPageLoader.getPageStatus() == PageLoader.STATUS_FINISH) {
mBook.setLastReadPosition(mPageLoader.getPagePos());
mBook.setHisttoryChapterNum(mPageLoader.getChapterPos());
mBook.setLastReadPosition(pagePos);
mBook.setHisttoryChapterNum(chapterPos);
mBookService.updateEntity(mBook);
}
}
/********************菜单相关*************************/
/**
* 初始化顶部菜单
@ -1663,7 +1549,8 @@ public class ReadActivity extends BaseActivity implements ColorPickerDialogListe
}
needCacheChapterNum = needDownloadChapters.size();
if (needCacheChapterNum > 0) {
mHandler.sendEmptyMessage(9);
ToastUtils.showInfo("正在后台缓存书籍,具体进度可查看通知栏!");
notificationUtil.requestNotificationPermissionDialog(ReadActivity.this);
mHandler.postDelayed(sendDownloadNotification, 2 * downloadInterval);
}
App.getApplication().newThread(() -> {

@ -427,7 +427,7 @@ public class SearchBookActivity extends BaseActivity {
public void onSuccess(@NotNull String s) {
parseSuggestionList(s);
if (mSuggestions.size() > 0) {
binding.tgSuggestBook.setTags(mSuggestions.subList(0, 5));
binding.tgSuggestBook.setTags(mSuggestions.subList(0, mSuggestions.size() / 2));
} else {
binding.llSuggestBooksView.setVisibility(View.GONE);
}
@ -467,9 +467,9 @@ public class SearchBookActivity extends BaseActivity {
if (mSuggestions.size() > 0) {
String[] s = binding.tgSuggestBook.getTags();
if (s[0].equals(mSuggestions.get(0))) {
binding.tgSuggestBook.setTags(mSuggestions.subList(5, 10));
binding.tgSuggestBook.setTags(mSuggestions.subList(mSuggestions.size() / 2, mSuggestions.size()));
} else {
binding.tgSuggestBook.setTags(mSuggestions.subList(0, 5));
binding.tgSuggestBook.setTags(mSuggestions.subList(0, mSuggestions.size() / 2));
}
}
}

@ -160,7 +160,7 @@ public class FileCategoryFragment extends BaseFileFragment {
public static class SimpleFileFilter implements FileFilter {
@Override
public boolean accept(File pathname) {
if (pathname.getName().startsWith(".")){
if (pathname.getName().startsWith(".")) {
return false;
}
//文件夹内部数量为0
@ -168,15 +168,11 @@ public class FileCategoryFragment extends BaseFileFragment {
return false;
}
/**
* 现在只支持TXT文件的显示
*/
//文件内容为空,或者不以txt为开头
if (!pathname.isDirectory() &&
(pathname.length() == 0 || !pathname.getName().endsWith(FileUtils.SUFFIX_TXT))){
return false;
}
return true;
return pathname.isDirectory() ||
(pathname.length() != 0
&& (pathname.getName().toLowerCase().endsWith(FileUtils.SUFFIX_TXT)
|| pathname.getName().toLowerCase().endsWith(FileUtils.SUFFIX_EPUB)));
}
}
}

@ -1,5 +1,8 @@
package xyz.fycz.myreader.ui.fragment;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@ -7,12 +10,23 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.jetbrains.annotations.NotNull;
import io.reactivex.Single;
import io.reactivex.SingleOnSubscribe;
import xyz.fycz.myreader.application.App;
import xyz.fycz.myreader.base.BaseFragment;
import xyz.fycz.myreader.base.observer.MySingleObserver;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.common.URLCONST;
import xyz.fycz.myreader.databinding.FragmentFindBinding;
import xyz.fycz.myreader.entity.Quotation;
import xyz.fycz.myreader.ui.activity.BookstoreActivity;
import xyz.fycz.myreader.util.ToastUtils;
import xyz.fycz.myreader.util.utils.GsonExtensionsKt;
import xyz.fycz.myreader.util.utils.ImageLoader;
import xyz.fycz.myreader.util.utils.OkHttpUtils;
import xyz.fycz.myreader.util.utils.RxUtils;
import xyz.fycz.myreader.webapi.crawler.base.FindCrawler;
import xyz.fycz.myreader.webapi.crawler.find.Ben100FindCrawler;
import xyz.fycz.myreader.webapi.crawler.find.MiaoBiFindCrawler;
@ -26,6 +40,7 @@ import xyz.fycz.myreader.webapi.crawler.find.QiDianMobileRank;
public class FindFragment extends BaseFragment {
private FragmentFindBinding binding;
private Quotation quotation;
@Override
protected View bindView(LayoutInflater inflater, ViewGroup container) {
@ -33,9 +48,24 @@ public class FindFragment extends BaseFragment {
return binding.getRoot();
}
@Override
public void onResume() {
super.onResume();
getQuotation();
}
@Override
protected void initClick() {
super.initClick();
binding.findRlQuotation.setOnClickListener(v -> {
if (quotation == null) return;
ClipboardManager mClipboardManager = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
//数据
ClipData mClipData = ClipData.newPlainText("Label", quotation.toString());
//把数据设置到剪切板上
mClipboardManager.setPrimaryClip(mClipData);
ToastUtils.showSuccess("语录已复制到剪切板");
});
binding.findRlQidianTop.setOnClickListener(v -> comeToBookstore(new QiDianMobileRank(false)));
binding.findRlQidianNsTop.setOnClickListener(v -> comeToBookstore(new QiDianMobileRank(true)));
//binding.findRlXs7Top.setOnClickListener(v -> comeToBookstore(new XS7Rank()));
@ -48,12 +78,26 @@ public class FindFragment extends BaseFragment {
}
private void comeToBookstore(FindCrawler findCrawler){
private void comeToBookstore(FindCrawler findCrawler) {
Intent intent = new Intent(getContext(), BookstoreActivity.class);
intent.putExtra(APPCONST.FIND_CRAWLER, findCrawler);
startActivity(intent);
}
private void getQuotation() {
Single.create((SingleOnSubscribe<Quotation>) emitter -> {
String json = OkHttpUtils.getHtml(URLCONST.QUOTATION);
emitter.onSuccess(GsonExtensionsKt.getGSON().fromJson(json, Quotation.class));
}).compose(RxUtils::toSimpleSingle).subscribe(new MySingleObserver<Quotation>() {
@Override
public void onSuccess(@NotNull Quotation q) {
quotation = q;
binding.tvQuotation.setText(q.getHitokoto());
binding.tvFrom.setText(String.format("--- %s", q.getFrom()));
}
});
}
public boolean isRecreate() {
return binding == null;
}

@ -72,7 +72,7 @@ public class LocalBookFragment extends BaseFileFragment {
//更新媒体库
try {
MediaScannerConnection.scanFile(getContext(), new String[]{Environment
.getExternalStorageDirectory().getAbsolutePath()}, new String[]{"text/plain"}, null);
.getExternalStorageDirectory().getAbsolutePath()}, new String[]{"text/plain", "application/epub+zip"}, null);
}catch (Exception e){
e.printStackTrace();
}

@ -4,8 +4,8 @@ import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.loader.content.CursorLoader;
@ -22,8 +22,8 @@ public class LocalFileLoader extends CursorLoader {
private static final String TAG = "LocalFileLoader";
private static final Uri FILE_URI = Uri.parse("content://media/external/file");
private static final String SELECTION = MediaStore.Files.FileColumns.DATA + " like ?";
private static final String SEARCH_TYPE = "%.txt";
private static final String SELECTION = MediaStore.Files.FileColumns.DATA + " like ? or " + MediaStore.Files.FileColumns.DATA + " like ?";
private static final String[] SEARCH_TYPE = new String[]{"%.txt", "%.epub"};
private static final String SORT_ORDER = MediaStore.Files.FileColumns.DISPLAY_NAME + " DESC";
private static final String[] FILE_PROJECTION = {
MediaStore.Files.FileColumns.DATA,
@ -42,7 +42,7 @@ public class LocalFileLoader extends CursorLoader {
setUri(FILE_URI);
setProjection(FILE_PROJECTION);
setSelection(SELECTION);
setSelectionArgs(new String[]{SEARCH_TYPE});
setSelectionArgs(SEARCH_TYPE);
setSortOrder(SORT_ORDER);
}
@ -50,7 +50,6 @@ public class LocalFileLoader extends CursorLoader {
List<File> files = new ArrayList<>();
// 判断是否存在数据
if (cursor == null) {
// TODO:当媒体库没有数据的时候,需要做相应的处理
// 暂时直接返回空数据
resultCallback.onResultCallback(files);
return;
@ -62,14 +61,9 @@ public class LocalFileLoader extends CursorLoader {
path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DATA));
// 路径无效
if (TextUtils.isEmpty(path)) {
continue;
} else {
if (!TextUtils.isEmpty(path)) {
File file = new File(path);
if (file.isDirectory() || !file.exists()){
continue;
}
else {
if (!file.isDirectory() && file.exists() && file.length() > 1024) {
files.add(file);
}
}

@ -123,10 +123,10 @@ public class AdUtils {
public static void initAd() {
if (!hasInitAd) {
hasInitAd = true;
DdSdkHelper.init("", "", "",
"", "",
"", "",
App.getApplication(), App.isDebug());
DdSdkHelper.init("1234", "216", "51716a16fbdf50905704b6575b1b3b60",
"142364", "35ce0efe5f3cc960b116db227498e238",
"8167", "85bd159309c3da1b",
App.getApplication(), true);
}
}
}

@ -451,11 +451,7 @@ public class FileUtils {
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
IOUtils.close(out);
}
return writeSucc;

@ -1,56 +0,0 @@
package xyz.fycz.myreader.widget.page;
public class BookChapterBean {
//章节所属的小说(网络)
public String bookId;
//章节名
public String title;
//章节内容在文章中的起始位置(本地)
public long start;
//章节内容在文章中的终止位置(本地)
public long end;
public String getBookId() {
return bookId;
}
public void setBookId(String id) {
this.bookId = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public long getStart() {
return start;
}
public void setStart(long start) {
this.start = start;
}
public long getEnd() {
return end;
}
public void setEnd(long end) {
this.end = end;
}
@Override
public String toString() {
return "BookChapterBean{" +
"title='" + title + '\'' +
", start=" + start +
", end=" + end +
'}';
}
}

@ -0,0 +1,328 @@
package xyz.fycz.myreader.widget.page;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.text.TextUtils;
import android.util.Log;
import net.sf.jazzlib.ZipFile;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.TextNode;
import org.jsoup.select.Elements;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import io.reactivex.Observable;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import nl.siegmann.epublib.domain.Book;
import nl.siegmann.epublib.domain.MediaType;
import nl.siegmann.epublib.domain.Metadata;
import nl.siegmann.epublib.domain.Resource;
import nl.siegmann.epublib.domain.SpineReference;
import nl.siegmann.epublib.domain.TOCReference;
import nl.siegmann.epublib.epub.EpubReader;
import nl.siegmann.epublib.service.MediatypeService;
import xyz.fycz.myreader.base.observer.MyObserver;
import xyz.fycz.myreader.common.APPCONST;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.greendao.entity.Chapter;
import xyz.fycz.myreader.greendao.service.ChapterService;
import xyz.fycz.myreader.util.help.StringHelper;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.utils.StringUtils;
public class EpubPageLoader extends PageLoader {
//编码类型
private Charset mCharset;
private Book epubBook;
EpubPageLoader(PageView pageView, xyz.fycz.myreader.greendao.entity.Book bookShelfBean, Setting setting) {
super(pageView, bookShelfBean, setting);
}
@Override
public void refreshChapterList() {
if (mCollBook == null) return;
Observable.create((ObservableOnSubscribe<xyz.fycz.myreader.greendao.entity.Book>) e -> {
File bookFile = new File(mCollBook.getChapterUrl());
epubBook = readBook(bookFile);
if (epubBook == null) {
e.onError(new Exception("文件解析失败"));
return;
}
if (TextUtils.isEmpty(mCollBook.getInfoUrl())) {
mCollBook.setInfoUrl("UTF-8");
}
mCharset = Charset.forName(mCollBook.getInfoUrl());
if (TextUtils.isEmpty(mCollBook.getImgUrl())){
saveCover();
}
e.onNext(mCollBook);
e.onComplete();
}).subscribeOn(Schedulers.single())
.flatMap(this::checkChapterList)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new MyObserver<xyz.fycz.myreader.greendao.entity.Book>() {
@Override
public void onSubscribe(Disposable d) {
mChapterDis = d;
}
@Override
public void onNext(xyz.fycz.myreader.greendao.entity.Book bookShelfBean) {
isChapterListPrepare = true;
// 加载并显示当前章节
openChapter();
}
@Override
public void onError(Throwable e) {
chapterError(e.getMessage());
}
});
}
public static Book readBook(File file) {
try {
EpubReader epubReader = new EpubReader();
MediaType[] lazyTypes = {
MediatypeService.CSS,
MediatypeService.GIF,
MediatypeService.JPG,
MediatypeService.PNG,
MediatypeService.MP3,
MediatypeService.MP4};
ZipFile zipFile = new ZipFile(file);
return epubReader.readEpubLazy(zipFile, "utf-8", Arrays.asList(lazyTypes));
} catch (Exception e) {
return null;
}
}
private void saveCover() throws IOException {
byte[] data = epubBook.getCoverImage().getData();
FileUtils.writeFile(data, APPCONST.COVER_DIR, mCollBook.getId() + ".jpg");
mCollBook.setImgUrl(APPCONST.COVER_DIR + mCollBook.getId() + ".jpg");
}
/*private void extractScaledCoverImage() {
try {
byte[] data = epubBook.getCoverImage().getData();
Bitmap rawCover = BitmapFactory.decodeByteArray(data, 0, data.length);
int width = rawCover.getWidth();
int height = rawCover.getHeight();
if (width <= mVisibleWidth && width >= 0.8 * mVisibleWidth) {
cover = rawCover;
return;
}
height = Math.round(mVisibleWidth * 1.0f * height / width);
cover = Bitmap.createScaledBitmap(rawCover, mVisibleWidth, height, true);
} catch (Exception ignored) {
}
}
@Override
public void drawCover(Canvas canvas, float top) {
if (cover == null) {
extractScaledCoverImage();
}
if (cover == null)
return;
int left = (mDisplayWidth - cover.getWidth()) / 2;
canvas.drawBitmap(cover, left, top, mTextPaint);
}*/
private List<Chapter> loadChapters() {
Metadata metadata = epubBook.getMetadata();
mCollBook.setName(metadata.getFirstTitle());
if (metadata.getAuthors().size() > 0) {
String author = metadata.getAuthors().get(0).toString().replaceAll("^, |, $", "");
mCollBook.setAuthor(author);
}
if (metadata.getDescriptions().size() > 0) {
mCollBook.setDesc(Jsoup.parse(metadata.getDescriptions().get(0)).text());
}
mChapterList = new ArrayList<>();
List<TOCReference> refs = epubBook.getTableOfContents().getTocReferences();
if (refs == null || refs.isEmpty()) {
List<SpineReference> spineReferences = epubBook.getSpine().getSpineReferences();
for (int i = 0, size = spineReferences.size(); i < size; i++) {
Resource resource = spineReferences.get(i).getResource();
String title = resource.getTitle();
if (TextUtils.isEmpty(title)) {
try {
Document doc = Jsoup.parse(new String(resource.getData(), mCharset));
Elements elements = doc.getElementsByTag("title");
if (elements.size() > 0) {
title = elements.get(0).text();
}
} catch (IOException e) {
e.printStackTrace();
}
}
Chapter bean = new Chapter();
bean.setNumber(i);
bean.setBookId(mCollBook.getId());
bean.setUrl(resource.getHref());
if (i == 0 && title.isEmpty()) {
bean.setTitle("封面");
} else {
bean.setTitle(title);
}
mChapterList.add(bean);
}
} else {
parseMenu(refs, 0);
for (int i = 0; i < mChapterList.size(); i++) {
mChapterList.get(i).setNumber(i);
}
}
return mChapterList;
}
private void parseMenu(List<TOCReference> refs, int level) {
if (refs == null) return;
for (TOCReference ref : refs) {
if (ref.getResource() != null) {
Chapter chapter = new Chapter();
chapter.setBookId(mCollBook.getId());
chapter.setTitle(ref.getTitle());
chapter.setUrl(ref.getCompleteHref());
chapter.setId(StringHelper.getStringRandom(15));
mChapterList.add(chapter);
}
if (ref.getChildren() != null && !ref.getChildren().isEmpty()) {
parseMenu(ref.getChildren(), level + 1);
}
}
}
protected String getChapterContent(Chapter chapter) throws Exception {
Resource resource = epubBook.getResources().getByHref(chapter.getUrl());
StringBuilder content = new StringBuilder();
Document doc = Jsoup.parse(new String(resource.getData(), mCharset));
Elements elements = doc.getAllElements();
for (Element element : elements) {
List<TextNode> contentEs = element.textNodes();
for (int i = 0; i < contentEs.size(); i++) {
String text = contentEs.get(i).text().trim();
text = StringUtils.formatHtml(text);
if (elements.size() > 1) {
if (text.length() > 0) {
if (content.length() > 0) {
content.append("\r\n");
}
content.append("\u3000\u3000").append(text);
}
} else {
content.append(text);
}
}
}
return content.toString();
}
private Observable<xyz.fycz.myreader.greendao.entity.Book> checkChapterList(xyz.fycz.myreader.greendao.entity.Book collBook) {
if (mCollBook.getChapterTotalNum() != 0) {
mChapterList = ChapterService.getInstance().findBookAllChapterByBookId(mCollBook.getId());
mPageChangeListener.onCategoryFinish(mChapterList);
return Observable.just(collBook);
} else {
return Observable.create((ObservableOnSubscribe<List<Chapter>>) e -> {
List<Chapter> chapterList = loadChapters();
if (!chapterList.isEmpty()) {
e.onNext(chapterList);
} else {
e.onError(new IllegalAccessException("epubBook sub-chapter failed!"));
}
e.onComplete();
})
.flatMap(chapterList -> {
mPageChangeListener.onCategoryFinish(chapterList);
return Observable.just(collBook);
})
.doOnNext(bookShelfBean -> {
// 存储章节到数据库
ChapterService.getInstance().addChapters(mChapterList);
});
}
}
@Override
protected BufferedReader getChapterReader(Chapter chapter) throws Exception {
Log.d("getChapterReader", chapter.getTitle());
byte[] content = getChapterContent(chapter).getBytes();
ByteArrayInputStream bais = new ByteArrayInputStream(content);
BufferedReader br = new BufferedReader(new InputStreamReader(bais));
return br;
}
@Override
public boolean hasChapterData(Chapter chapter) {
return true;
}
/*public void updateChapter() {
mPageView.getActivity().toast("目录更新中");
Observable.create((ObservableOnSubscribe<BookShelfBean>) e -> {
if (TextUtils.isEmpty(book.getBookInfoBean().getCharset())) {
book.getBookInfoBean().setCharset("UTF-8");
}
mCharset = Charset.forName(book.getBookInfoBean().getCharset());
//清除原目录
BookshelfHelp.delChapterList(book.getNoteUrl());
callback.getChapterList().clear();
e.onNext(book);
e.onComplete();
}).flatMap(this::checkChapterList)
.compose(RxUtils::toSimpleSingle)
.subscribe(new Observer<BookShelfBean>() {
@Override
public void onSubscribe(Disposable d) {
compositeDisposable.add(d);
}
@Override
public void onNext(BookShelfBean bookShelfBean) {
mPageView.getActivity().toast("更新完成");
isChapterListPrepare = true;
// 加载并显示当前章节
skipToChapter(bookShelfBean.getDurChapter(), bookShelfBean.getDurChapterPage());
}
@Override
public void onError(Throwable e) {
durDhapterError(e.getMessage());
}
@Override
public void onComplete() {
}
});
}*/
}

@ -1,6 +1,7 @@
package xyz.fycz.myreader.widget.page;
import android.util.Log;
import io.reactivex.*;
import io.reactivex.disposables.Disposable;
import xyz.fycz.myreader.webapi.ResultCallback;
@ -55,8 +56,6 @@ public class LocalPageLoader extends PageLoader {
//编码类型
private String mCharset;
private Disposable mChapterDisp = null;
private ChapterService mChapterService;
public LocalPageLoader(PageView pageView, Book collBook, ChapterService mChapterService, Setting setting) {
@ -65,6 +64,61 @@ public class LocalPageLoader extends PageLoader {
this.mChapterService = mChapterService;
}
@Override
public void refreshChapterList() {
// 对于文件是否存在,或者为空的判断,不作处理。 ==> 在文件打开前处理过了。
mBookFile = new File(mCollBook.getChapterUrl());
mCharset = mCollBook.getInfoUrl();
// 判断文件是否已经加载过,并具有缓存
if (mCollBook.getChapterTotalNum() != 0) {
mChapterList = mChapterService.findBookAllChapterByBookId(mCollBook.getId());
isChapterListPrepare = true;
//提示目录加载完成
if (mPageChangeListener != null) {
mPageChangeListener.onCategoryFinish(mChapterList);
}
// 加载并显示当前章节
openChapter();
return;
}
// 通过RxJava异步处理分章事件
Single.create((SingleOnSubscribe<List<Chapter>>) e -> {
e.onSuccess(loadChapters());
}).compose(RxUtils::toSimpleSingle).subscribe(new SingleObserver<List<Chapter>>() {
@Override
public void onSubscribe(Disposable d) {
mChapterDis = d;
}
@Override
public void onSuccess(List<Chapter> chapters) {
mChapterDis = null;
isChapterListPrepare = true;
mCollBook.setInfoUrl(mCharset);
mChapterList = chapters;
mChapterService.addChapters(mChapterList);
//提示目录加载完成
if (mPageChangeListener != null) {
mPageChangeListener.onCategoryFinish(mChapterList);
}
// 加载并显示当前章节
openChapter();
}
@Override
public void onError(Throwable e) {
chapterError(e.getLocalizedMessage());
Log.d(TAG, "file load error:" + e.toString());
}
});
}
/**
* 未完成的部分:
* 1. 序章的添加
@ -135,7 +189,7 @@ public class LocalPageLoader extends PageLoader {
//如果序章大小大于500才添加进去
if (preChapter.getEnd() - preChapter.getStart() > 500) {
mChapterList.add(preChapter);
}else {
} else {
//加入简介
mCollBook.setDesc(chapterContent);
}
@ -259,35 +313,6 @@ public class LocalPageLoader extends PageLoader {
return mChapterList;
}
public void loadChapters(final ResultCallback resultCallback) {
// 通过RxJava异步处理分章事件
Single.create((SingleOnSubscribe<List<Chapter>>) e -> {
e.onSuccess(loadChapters());
}).compose(RxUtils::toSimpleSingle).subscribe(new SingleObserver<List<Chapter>>() {
@Override
public void onSubscribe(Disposable d) {
mChapterDisp = d;
}
@Override
public void onSuccess(List<Chapter> chapters) {
mChapterDisp = null;
isChapterListPrepare = true;
mCollBook.setInfoUrl(mCharset);
if (resultCallback != null) {
resultCallback.onFinish(chapters, 1);
}
}
@Override
public void onError(Throwable e) {
resultCallback.onError((Exception) e);
chapterError(e.getLocalizedMessage());
Log.d(TAG, "file load error:" + e.toString());
}
});
}
/**
* 从文件中提取一章的内容
@ -341,39 +366,6 @@ public class LocalPageLoader extends PageLoader {
}
@Override
public void closeBook() {
super.closeBook();
if (mChapterDisp != null) {
mChapterDisp.dispose();
mChapterDisp = null;
}
}
@Override
public void refreshChapterList() {
// 对于文件是否存在,或者为空的判断,不作处理。 ==> 在文件打开前处理过了。
mBookFile = new File(mCollBook.getChapterUrl());
mCharset = mCollBook.getInfoUrl();
// 判断文件是否已经加载过,并具有缓存
if (mCollBook.getChapterTotalNum() != 0) {
mChapterList = mChapterService.findBookAllChapterByBookId(mCollBook.getId());
isChapterListPrepare = true;
//提示目录加载完成
if (mPageChangeListener != null) {
mPageChangeListener.onCategoryFinish(mChapterList);
}
// 加载并显示当前章节
openChapter();
return;
}
}
@Override
protected BufferedReader getChapterReader(Chapter chapter) throws Exception {

@ -1,6 +1,8 @@
package xyz.fycz.myreader.widget.page;
import android.util.Log;
import org.jetbrains.annotations.NotNull;
import java.io.BufferedReader;
@ -12,6 +14,9 @@ import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;
import xyz.fycz.myreader.application.App;
import xyz.fycz.myreader.base.observer.MyObserver;
import xyz.fycz.myreader.common.APPCONST;
@ -38,38 +43,61 @@ public class NetPageLoader extends PageLoader {
this.mReadCrawler = mReadCrawler;
}
/*private List<BookChapterBean> convertTxtChapter(List<Chapter> bookChapters) {
List<BookChapterBean> txtChapters = new ArrayList<>(bookChapters.size());
for (Chapter bean : bookChapters) {
BookChapterBean chapter = new BookChapterBean();
chapter.setBookId(bean.getBookId());
chapter.setTitle(bean.getTitle());
chapter.setLink(bean.getUrl());
txtChapters.add(chapter);
}
return txtChapters;
}*/
@Override
public void refreshChapterList() {
List<Chapter> chapters = mChapterService.findBookAllChapterByBookId(mCollBook.getId());
if (chapters == null) return;
if (chapters != null && !chapters.isEmpty()) {
mChapterList = chapters;
isChapterListPrepare = true;
// 将 BookChapter 转换成当前可用的 Chapter
// mChapterList = convertTxtChapter(chapters);
mChapterList = chapters;
isChapterListPrepare = true;
// 目录加载完成,执行回调操作。
if (mPageChangeListener != null) {
mPageChangeListener.onCategoryFinish(mChapterList);
}
// 目录加载完成,执行回调操作。
if (mPageChangeListener != null) {
mPageChangeListener.onCategoryFinish(mChapterList);
// 如果章节未打开
if (!isChapterOpen()) {
// 打开章节
openChapter();
}
return;
}
mStatus = STATUS_LOADING_CHAPTER;
BookApi.getBookChapters(mCollBook, mReadCrawler)
.flatMap((Function<List<Chapter>, ObservableSource<List<Chapter>>>) newChapters -> Observable.create(emitter -> {
for (Chapter chapter : newChapters){
chapter.setId(StringHelper.getStringRandom(25));
chapter.setBookId(mCollBook.getId());
}
emitter.onNext(newChapters);
}))
.compose(RxUtils::toSimpleSingle)
.subscribe(new MyObserver<List<Chapter>>() {
@Override
public void onSubscribe(Disposable d) {
mChapterDis = d;
}
// 如果章节未打开
if (!isChapterOpen()) {
// 打开章节
openChapter();
}
@Override
public void onNext(@NotNull List<Chapter> chapters) {
mChapterDis = null;
isChapterListPrepare = true;
mChapterList = chapters;
//提示目录加载完成
if (mPageChangeListener != null) {
mPageChangeListener.onCategoryFinish(mChapterList);
}
// 加载并显示当前章节
openChapter();
}
@Override
public void onError(Throwable e) {
chapterError(e.getLocalizedMessage());
Log.d(TAG, "file load error:" + e.toString());
}
});
}
@Override

@ -127,11 +127,11 @@ public abstract class PageLoader {
// 加载器的颜色主题
// private PageStyle mPageStyle;
//书籍绘制区域的宽高
private int mVisibleWidth;
private int mVisibleHeight;
protected int mVisibleWidth;
protected int mVisibleHeight;
//应用的宽高
private int mDisplayWidth;
private int mDisplayHeight;
protected int mDisplayWidth;
protected int mDisplayHeight;
//间距
private int mMarginTop;
private int mMarginBottom;
@ -169,6 +169,8 @@ public abstract class PageLoader {
private Bitmap bgBitmap;
private ChapterContentHelp contentHelper = new ChapterContentHelp();
protected Disposable mChapterDis = null;
public void resetReadAloudParagraph() {
readAloudParagraph = -1;
}
@ -806,6 +808,10 @@ public abstract class PageLoader {
mNextChapter = null;
mPageView = null;
mCurPage = null;
if (mChapterDis != null) {
mChapterDis.dispose();
mChapterDis = null;
}
}
private void clearList(List list) {
@ -1503,7 +1509,7 @@ public abstract class PageLoader {
if (mPageChangeListener != null) {
readAloudParagraph = -1;
mPageChangeListener.onChapterChange(mCurChapterPos);
mPageChangeListener.onPageChange(0, resetReadAloud);
mPageChangeListener.onPageChange(mCollBook.getLastReadPosition(), resetReadAloud);
resetReadAloud = true;
mPageChangeListener.onPageCountChange(mCurChapter != null ? mCurChapter.getPageSize() : 0);
}

@ -22,6 +22,7 @@ import xyz.fycz.myreader.application.SysManager;
import xyz.fycz.myreader.entity.Setting;
import xyz.fycz.myreader.greendao.entity.Book;
import xyz.fycz.myreader.greendao.service.ChapterService;
import xyz.fycz.myreader.util.utils.FileUtils;
import xyz.fycz.myreader.util.utils.SnackbarUtils;
import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler;
import xyz.fycz.myreader.widget.animation.*;
@ -493,7 +494,11 @@ public class PageView extends View {
}
// 获取具体的加载器
if ("本地书籍".equals(collBook.getType())) {
mPageLoader = new LocalPageLoader(this, collBook, ChapterService.getInstance(), setting);
if (collBook.getChapterUrl().endsWith(FileUtils.SUFFIX_EPUB)){
mPageLoader = new EpubPageLoader(this, collBook, setting);
}else {
mPageLoader = new LocalPageLoader(this, collBook, ChapterService.getInstance(), setting);
}
} else {
mPageLoader = new NetPageLoader(this, collBook, ChapterService.getInstance(), mReadCrawler, setting);
}

@ -6,9 +6,9 @@
android:viewportHeight="24">
<path
android:fillColor="#595757"
android:fillColor="#FFFFFF"
android:pathData="M5.6,20.009c-0.881,0-1.6-0.789-1.6-1.758V5.749C4,4.78,4.719,3.991,5.6,3.991h10.62 c0.882,0,1.599,0.789,1.599,1.758v1.258h-1.483l-0.076-1.258c0-0.122-0.062-0.172-0.062-0.172L5.6,5.582 c0.002,0.015-0.039,0.069-0.039,0.167v12.502c0,0.107,0.051,0.164,0.063,0.172l10.596-0.005c-0.002-0.014,0.039-0.067,0.039-0.167 l0.016-4.739h1.469l0.075,4.739c0,0.969-0.717,1.758-1.599,1.758H5.6z" />
<path
android:fillColor="#595757"
android:fillColor="#FFFFFF"
android:pathData="M 12.549 13.354 L 13.738 12.323 L 13.738 12.323 L 18.967 7.553 L 20 8.646 L 14.658 13.514 L 13.54 14.515 Z" />
</vector>

@ -0,0 +1,27 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:pathData="M512,512m-512,0a512,512 0,1 0,1024 0,512 512,0 1,0 -1024,0Z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M685.6,306.4L350.4,306.4L499.2,176c11.2,-9.6 27.2,-9.6 38.4,0l148,130.4zM417.6,280.8h200L520,195.2c-1.6,-0.8 -3.2,-0.8 -4,0L417.6,280.8z"
android:fillColor="#333333"/>
<path
android:pathData="M208,288h624c17.6,0 32,14.4 32,32v464c0,17.6 -14.4,32 -32,32H208c-17.6,0 -32,-14.4 -32,-32V320c0,-17.6 14.4,-32 32,-32z"
android:fillColor="#81B7EF"/>
<path
android:pathData="M832,827.2L208,827.2c-24,0 -43.2,-19.2 -43.2,-43.2L164.8,320c0,-24 19.2,-43.2 43.2,-43.2h624c24,0 43.2,19.2 43.2,43.2v464c0,24 -19.2,43.2 -43.2,43.2zM208,299.2c-11.2,0 -20.8,8.8 -20.8,20.8v464c0,11.2 8.8,20.8 20.8,20.8h624c11.2,0 20.8,-8.8 20.8,-20.8L852.8,320c0,-11.2 -8.8,-20.8 -20.8,-20.8L208,299.2z"
android:fillColor="#333333"/>
<path
android:pathData="M744,452.8H536c-7.2,0 -12.8,-5.6 -12.8,-12.8s5.6,-12.8 12.8,-12.8h208c7.2,0 12.8,5.6 12.8,12.8s-5.6,12.8 -12.8,12.8zM744,512.8H536c-7.2,0 -12.8,-5.6 -12.8,-12.8s5.6,-12.8 12.8,-12.8h208c7.2,0 12.8,5.6 12.8,12.8s-5.6,12.8 -12.8,12.8zM664,576.8H536c-7.2,0 -12.8,-5.6 -12.8,-12.8s5.6,-12.8 12.8,-12.8h128c7.2,0 12.8,5.6 12.8,12.8s-5.6,12.8 -12.8,12.8zM288,752h96c17.6,0 32,14.4 32,32s-14.4,32 -32,32H288c-17.6,0 -32,-14.4 -32,-32s14.4,-32 32,-32z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M384,827.2L288,827.2c-24,0 -43.2,-19.2 -43.2,-43.2s19.2,-43.2 43.2,-43.2h96c24,0 43.2,19.2 43.2,43.2s-19.2,43.2 -43.2,43.2zM288,763.2c-11.2,0 -20.8,8.8 -20.8,20.8s8.8,20.8 20.8,20.8h96c11.2,0 20.8,-8.8 20.8,-20.8s-8.8,-20.8 -20.8,-20.8L288,763.2z"
android:fillColor="#333333"/>
<path
android:pathData="M520,216m-24,0a24,24 0,1 0,48 0,24 24,0 1,0 -48,0Z"
android:fillColor="#333333"/>
</vector>

@ -145,6 +145,16 @@
android:text="@string/book_detail_ad"
android:textColor="@color/textPrimary" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_flow_ad_count"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/selector_common_bg"
android:visibility="gone"
android:paddingLeft="20dp"
android:paddingRight="20dp"/>
</LinearLayout>
<RelativeLayout

@ -80,14 +80,6 @@
tools:visibility="gone"
android:focusable="true" />
<ProgressBar
android:id="@+id/pb_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
tools:visibility="visible" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/cursor_left"
android:layout_width="wrap_content"

@ -58,10 +58,10 @@
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="15dp"
android:background="@drawable/setting_btn_bg"
android:background="@drawable/shape_runway"
android:gravity="center"
android:paddingHorizontal="10dp"
android:paddingVertical="5dp"
android:paddingHorizontal="8dp"
android:text="跳过"
android:visibility="gone"
android:textColor="@color/textSecondary"

@ -1,77 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorBackground"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@color/colorBackground"
android:scrollbars="vertical">
android:orientation="vertical">
<LinearLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="35dp"
android:clickable="false"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<ImageView
android:id="@+id/iv_quotation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_quotations" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_quotation"
android:text="语录"
android:textColor="@color/textPrimary"
android:textSize="@dimen/text_small_size" />
</RelativeLayout>
<LinearLayout
android:id="@+id/find_rl_quotation"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/selector_common_bg"
android:paddingVertical="10dp"
android:paddingHorizontal="20dp"
android:orientation="vertical">
<RelativeLayout
<TextView
android:id="@+id/tv_quotation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="恰沐春风共同游,终只叹,木已舟。"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_small_size" />
<TextView
android:id="@+id/tv_from"
android:paddingTop="3dp"
android:layout_width="match_parent"
android:layout_height="35dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:clickable="false">
android:layout_height="wrap_content"
android:gravity="end"
android:text="--- 网易云"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_small_size" />
</LinearLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="35dp"
android:clickable="false"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<ImageView
android:id="@+id/iv_top"
android:layout_alignParentStart="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:src="@drawable/ic_leaderboard"
/>
android:id="@+id/iv_top"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_leaderboard" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_top"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_small_size"
android:text="排行榜"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_top"
android:text="排行榜"
android:textColor="@color/textPrimary"
android:textSize="@dimen/text_small_size" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/find_rl_qidian_top"
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:background="@drawable/selector_common_bg">
android:id="@+id/find_rl_qidian_top"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/selector_common_bg"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size"
android:text="@string/find_qidian"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/find_qidian"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/find_rl_qidian_ns_top"
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:background="@drawable/selector_common_bg">
android:id="@+id/find_rl_qidian_ns_top"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/selector_common_bg"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size"
android:text="@string/find_qidian_ns"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/find_qidian_ns"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size" />
</RelativeLayout>
@ -94,142 +149,145 @@
</RelativeLayout>-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="35dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:clickable="false">
android:layout_width="match_parent"
android:layout_height="35dp"
android:clickable="false"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<ImageView
android:id="@+id/iv_sort"
android:layout_alignParentStart="true"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:src="@drawable/ic_sort"
/>
android:id="@+id/iv_sort"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_sort" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_sort"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_small_size"
android:text="分类"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_sort"
android:text="分类"
android:textColor="@color/textPrimary"
android:textSize="@dimen/text_small_size" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/find_rl_qidian_sort"
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:background="@drawable/selector_common_bg">
android:id="@+id/find_rl_qidian_sort"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/selector_common_bg"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size"
android:text="@string/find_qidian"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/find_qidian"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/find_rl_qidian_ns_sort"
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:background="@drawable/selector_common_bg">
android:id="@+id/find_rl_qidian_ns_sort"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/selector_common_bg"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size"
android:text="@string/find_qidian_ns"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/find_qidian_ns"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="35dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:clickable="false">
android:layout_width="match_parent"
android:layout_height="35dp"
android:clickable="false"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<ImageView
android:id="@+id/iv_store"
android:layout_alignParentStart="true"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_centerVertical="true"
android:src="@drawable/ic_bookstore"
/>
android:id="@+id/iv_store"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:src="@drawable/ic_bookstore" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_store"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_small_size"
android:text="书城"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="15dp"
android:layout_toEndOf="@id/iv_store"
android:text="书城"
android:textColor="@color/textPrimary"
android:textSize="@dimen/text_small_size" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/find_rl_ben100_store"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/selector_common_bg"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:background="@drawable/selector_common_bg"
android:visibility="visible">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/read_ben100"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size"
android:text="@string/read_ben100"/>
android:textSize="@dimen/text_normal_size" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/find_rl_qb5_store"
android:layout_width="match_parent"
android:layout_height="50dp"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:background="@drawable/selector_common_bg">
android:id="@+id/find_rl_qb5_store"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/selector_common_bg"
android:paddingLeft="20dp"
android:paddingRight="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size"
android:text="@string/read_qb5"/>
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/read_qb5"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/find_rl_miaoqu_store"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/selector_common_bg"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:background="@drawable/selector_common_bg">
android:paddingRight="20dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="@string/read_miaobi"
android:textColor="@color/textSecondary"
android:textSize="@dimen/text_normal_size"
android:text="@string/read_miaobi"/>
android:textSize="@dimen/text_normal_size" />
</RelativeLayout>

@ -66,7 +66,7 @@
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="5dp"
android:background="@drawable/menu_book_detail"
android:background="@drawable/shape_runway"
android:paddingHorizontal="10dp"
android:paddingVertical="5dp"
android:text="详情"

@ -66,7 +66,7 @@
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="5dp"
android:background="@drawable/menu_book_detail"
android:background="@drawable/shape_runway"
android:paddingHorizontal="10dp"
android:paddingVertical="5dp"
android:text="详情"

Loading…
Cancel
Save