diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml index 4a25623..256c54e 100644 --- a/.idea/assetWizardSettings.xml +++ b/.idea/assetWizardSettings.xml @@ -19,8 +19,8 @@ diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index ba535aa..b290ab3 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 5cd135a..9bba60d 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -14,6 +14,7 @@ diff --git a/.idea/modules.xml b/.idea/modules.xml index 53c4500..c9f6d5e 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + diff --git a/README.md b/README.md index a647bc9..9120666 100644 --- a/README.md +++ b/README.md @@ -1,204 +1,26 @@ # FYReader-master 风月读书,一款开源、无广告的小说阅读软件。 -demo下载:https://fycz.lanzoui.com/iaVu6i6ywlg - -#### 一、关于书源 - -* 软件内置了15个书源如下: - * 11个网络小说书源:天籁小说、笔趣阁44、品书网、笔趣阁、 + 11个网络小说书源:天籁小说、笔趣阁44、品书网、笔趣阁、 全本小说、米趣小说、九桃小说、云中书库、 搜小说网、全小说网、奇奇小说 - * 4个实体书书源:超星图书·实体、作品集·实体、99藏书·实体、100本·实体 - -* 如何自行制作并添加书源. - - * 基于面向接口开发的思想,对于书源我设计了如下接口: - - * ```java - // 这个接口位于xyz.fycz.myreader.webapi.crawler.base包下 - public interface ReadCrawler { - String getSearchLink(); // 书源的搜索url - String getCharset(); // 书源的字符编码 - String getSearchCharset(); // 书源搜索关键字的字符编码,和书源的字符编码就行 - String getNameSpace(); // 书源主页地址 - Boolean isPost(); // 是否以post请求搜索 - String getContentFormHtml(String html); // 获取书籍内容规则 - ArrayList getChaptersFromHtml(String html); // 获取书籍章节列表规则 - ConcurrentMultiValueMap getBooksFromSearchHtml(String html); // 搜索书籍规则 - } - ``` - - * 了解上述接口的方法,我们就可以开始创建书源了 - - * 第一步:创建一个书源类实现上述接口,下面以笔趣阁44为例进行说明 - - * ```java - // 注意:如果搜索书籍页没有图片、最新章节、书籍简介等信息,可以通过实现BookInfoCrawler接口,从书籍详情页获取 - public class BiQuGe44ReadCrawler implements ReadCrawler, BookInfoCrawler { - //网站主页地址 - public static final String NAME_SPACE = "https://www.wqge.cc"; - /* - 搜索url,搜索关键词以{key}进行占位 - 如果是post请求,以“,”分隔url,“,”前是搜索地址,“,”后是请求体,搜索关键词同样以{key}占位 - 例如:"https://www.9txs.com/search.html,searchkey={key}" - */ - public static final String NOVEL_SEARCH = "https://www.wqge.cc/modules/article/search.php?searchkey={key}"; - // 书源字符编码 - public static final String CHARSET = "GBK"; - // 书源搜索关键词编码 - public static final String SEARCH_CHARSET = "utf-8"; - @Override - public String getSearchLink() { - return NOVEL_SEARCH; - } - - @Override - public String getCharset() { - return CHARSET; - } - - @Override - public String getNameSpace() { - return NAME_SPACE; - } - @Override - public Boolean isPost() { - return false; - } - @Override - public String getSearchCharset() { - return SEARCH_CHARSET; - } - - /** - * 从html中获取章节正文 - * @param html - * @return - */ - public String getContentFormHtml(String html) { - Document doc = Jsoup.parse(html); - Element divContent = doc.getElementById("content"); - if (divContent != null) { - String content = Html.fromHtml(divContent.html()).toString(); - char c = 160; - String spaec = "" + c; - content = content.replace(spaec, " "); - return content; - } else { - return ""; - } - } - - /** - * 从html中获取章节列表 - * - * @param html - * @return - */ - public ArrayList getChaptersFromHtml(String html) { - ArrayList chapters = new ArrayList<>(); - Document doc = Jsoup.parse(html); - String readUrl = doc.select("meta[property=og:novel:read_url]").attr("content"); - Element divList = doc.getElementById("list"); - String lastTile = null; - int i = 0; - Elements elementsByTag = divList.getElementsByTag("dd"); - for (int j = 9; j < elementsByTag.size(); j++) { - Element dd = elementsByTag.get(j); - Elements as = dd.getElementsByTag("a"); - if (as.size() > 0) { - Element a = as.get(0); - String title = a.text() ; - if (!StringHelper.isEmpty(lastTile) && title.equals(lastTile)) { - continue; - } - Chapter chapter = new Chapter(); - chapter.setNumber(i++); - chapter.setTitle(title); - String url = readUrl + a.attr("href"); - chapter.setUrl(url); - chapters.add(chapter); - lastTile = title; - } - } - return chapters; - } - - /** - * 从搜索html中得到书列表 - * @param html - * @return - */ - public ConcurrentMultiValueMap getBooksFromSearchHtml(String html) { - ConcurrentMultiValueMap books = new ConcurrentMultiValueMap<>(); - Document doc = Jsoup.parse(html); - Elements divs = doc.getElementsByTag("table"); - Element div = divs.get(0); - Elements elementsByTag = div.getElementsByTag("tr"); - for (int i = 1; i < elementsByTag.size(); i++) { - Element element = elementsByTag.get(i); - Book book = new Book(); - Elements info = element.getElementsByTag("td"); - book.setName(info.get(0).text()); - book.setChapterUrl(NAME_SPACE + info.get(0).getElementsByTag("a").attr("href")); - book.setAuthor(info.get(2).text()); - book.setNewestChapterTitle(info.get(1).text()); - book.setSource(BookSource.biquge44.toString()); - // SearchBookBean用于合并相同书籍 - SearchBookBean sbb = new SearchBookBean(book.getName(), book.getAuthor()); - books.add(sbb, book); - } - return books; - } - - /** - * 获取书籍详细信息 - * @param book - */ - public Book getBookInfo(String html, Book book){ - Document doc = Jsoup.parse(html); - Element img = doc.getElementById("fmimg"); - book.setImgUrl(img.getElementsByTag("img").get(0).attr("src")); - Element desc = doc.getElementById("intro"); - book.setDesc(desc.getElementsByTag("p").get(0).text()); - Element type = doc.getElementsByClass("con_top").get(0); - book.setType(type.getElementsByTag("a").get(2).text()); - return book; - } - - } - ``` - - * 第二步:注册书源信息。有两个地方需要注册: - - * 1)在xyz.fycz.myreader.enums.BookSource类(这是个枚举类型)中添加你的书源的命名以及书源名称,例如: - - * ```java - biquge44("笔趣阁44") // biquge44是书源的命名,笔趣阁44是书源名称 - ``` - - * 2)在app/src/main/resources/crawler.properties配置文件中添加书源类信息,例如: - - * ```java - // biquge44书源的命名,与BookSource中的命名一致,xyz.fycz.myreader.webapi.crawler.read.BiQuGe44ReadCrawler是书源类的完整路径 - biquge44=xyz.fycz.myreader.webapi.crawler.read.BiQuGe44ReadCrawler - ``` - - * 第三步:启用书源(新增的书源默认禁用)。只需在软件内-我的-设置-禁用书源中取消该书源的禁用即可。 - -#### 二、关于发现界面 - -* 软件内置的两个发现源: - * 某点的排行榜、分类,全本小说网 - * 制作发现源方法与书源类似,在此不再赘述 - -![Image](./img/1.png) -![Image](./img/2.png) -![Image](./img/3.png) -![Image](./img/4.png) -![Image](./img/5.png) -![Image](./img/6.png) -![Image](./img/7.png) -![Image](./img/8.png) -![Image](./img/9.png) \ No newline at end of file + 4个实体书书源:超星图书·实体、作品集·实体、99藏书·实体、100本·实体 + + 发现界面:排行榜、分类、书城 + + 详细功能可查看图片或下载自行体验或自行编译 + +demo下载:https://fycz.lanzoui.com/iBofFh42pxg + +如有问题请加QQ群:1085028304 + +![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/1.png) +![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/2.png) +![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/3.png) +![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/4.png) +![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/5.png) +![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/6.png) +![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/7.png) +![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/8.png) +![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/9.png) +![Image](https://github.com/fengyuecanzhu/FYReader/tree/master/img/10.png) \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 81ffd48..9ca848e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -78,8 +78,20 @@ android { } } + +repositories { + flatDir { + dirs 'libs' + } +} + dependencies { + implementation fileTree(dir: 'libs', include: ['*.aar']) implementation fileTree(include: ['*.jar'], dir: 'libs') + testImplementation 'junit:junit:4.12' + androidTestImplementation 'androidx.test.ext:junit:1.1.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + api 'androidx.core:core-ktx:1.3.0' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" @@ -141,6 +153,7 @@ dependencies { //ImmersionBar implementation 'com.gyf.immersionbar:immersionbar:3.0.0' + } greendao { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1f4a8a7..b635103 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,28 +3,9 @@ xmlns:tools="http://schemas.android.com/tools" package="xyz.fycz.myreader"> - - - - - - - - - - - - - - - - - - - @@ -32,14 +13,11 @@ - - - @@ -54,7 +32,8 @@ - + + - - + + \ No newline at end of file diff --git a/app/src/main/assets/updatelog.fy b/app/src/main/assets/updatelog.fy index 5601be8..f154881 100644 --- a/app/src/main/assets/updatelog.fy +++ b/app/src/main/assets/updatelog.fy @@ -1,9 +1,13 @@ +2020.11.11 +风月读书v1.20.1111121 +1、新增语音朗读(听书) + 2020.11.08 -风月读书v1.20.1110816 +风月读书v1.20.1110811 1、修复安卓5闪退bug 2020.11.03 -风月读书v1.20.1110312 +风月读书v1.20.1110311 1、修复已知bug 2、新增WebDav备份教程 diff --git a/app/src/main/java/xyz/fycz/myreader/application/MyApplication.java b/app/src/main/java/xyz/fycz/myreader/application/MyApplication.java index ead050f..f1f119e 100644 --- a/app/src/main/java/xyz/fycz/myreader/application/MyApplication.java +++ b/app/src/main/java/xyz/fycz/myreader/application/MyApplication.java @@ -22,6 +22,7 @@ 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; @@ -160,11 +161,17 @@ public class MyApplication extends Application { @TargetApi(26) private void createNotificationChannel() { NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - NotificationChannel channel = new NotificationChannel(APPCONST.channelIdDownload, "下载通知", NotificationManager.IMPORTANCE_LOW); - channel.enableLights(true);//是否在桌面icon右上角展示小红点 - channel.setLightColor(Color.RED);//小红点颜色 - channel.setShowBadge(false); //是否在久按桌面图标时显示此渠道的通知 - notificationManager.createNotificationChannel(channel); + NotificationChannel downloadChannel = new NotificationChannel(APPCONST.channelIdDownload, "下载通知", NotificationManager.IMPORTANCE_LOW); + downloadChannel.enableLights(true);//是否在桌面icon右上角展示小红点 + downloadChannel.setLightColor(Color.RED);//小红点颜色 + downloadChannel.setShowBadge(false); //是否在久按桌面图标时显示此渠道的通知 + notificationManager.createNotificationChannel(downloadChannel); + + NotificationChannel readChannel = new NotificationChannel(APPCONST.channelIdRead, "朗读通知", NotificationManager.IMPORTANCE_LOW); + readChannel.enableLights(true);//是否在桌面icon右上角展示小红点 + readChannel.setLightColor(Color.RED);//小红点颜色 + readChannel.setShowBadge(false); //是否在久按桌面图标时显示此渠道的通知 + notificationManager.createNotificationChannel(readChannel); } @@ -271,6 +278,13 @@ public class MyApplication extends Application { updateContent = contents[3].substring(contents[3].indexOf(":") + 1); SharedPreUtils spu = SharedPreUtils.getInstance(); spu.putString(getmContext().getString(R.string.lanzousKeyStart), contents[4].substring(contents[4].indexOf(":") + 1)); + + String newSplashTime = contents[5].substring(contents[5].indexOf(":") + 1); + String oldSplashTime = spu.getString("splashTime"); + spu.putBoolean("needUdSI", !oldSplashTime.equals(newSplashTime)); + spu.putString("splashTime", contents[5].substring(contents[5].indexOf(":") + 1)); + spu.putString("splashImageUrl", contents[6].substring(contents[6].indexOf(":") + 1)); + if (!StringHelper.isEmpty(downloadLink)) { spu.putString(getmContext().getString(R.string.downloadLink), downloadLink); } else { @@ -388,5 +402,26 @@ public class MyApplication extends Application { public static boolean isDestroy(Activity mActivity) { return mActivity == null || mActivity.isFinishing() || mActivity.isDestroyed(); } - + + + /**************** + * + * 发起添加群流程。群号:风月读书交流群(1085028304) 的 key 为: 8PIOnHFuH6A38hgxvD_Rp2Bu-Ke1ToBn + * 调用 joinQQGroup(8PIOnHFuH6A38hgxvD_Rp2Bu-Ke1ToBn) 即可发起手Q客户端申请加群 风月读书交流群(1085028304) + * + * @param key 由官网生成的key + * @return 返回true表示呼起手Q成功,返回false表示呼起失败 + ******************/ + public static boolean joinQQGroup(Context context, String key) { + Intent intent = new Intent(); + intent.setData(Uri.parse("mqqopensdkapi://bizAgent/qm/qr?url=http%3A%2F%2Fqm.qq.com%2Fcgi-bin%2Fqm%2Fqr%3Ffrom%3Dapp%26p%3Dandroid%26jump_from%3Dwebapi%26k%3D" + key)); + // 此Flag可根据具体产品需要自定义,如设置,则在加群界面按返回,返回手Q主界面,不设置,按返回会返回到呼起产品界面 //intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + try { + context.startActivity(intent); + return true; + } catch (Exception e) { + // 未安装手Q或安装的版本不支持 + return false; + } + } } diff --git a/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java b/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java index a2f26bd..e5a1abb 100644 --- a/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/base/BaseActivity.java @@ -205,7 +205,7 @@ public abstract class BaseActivity extends AppCompatActivity { Drawable drawable = menu.getItem(i).getIcon(); if (drawable != null) { drawable.mutate(); - drawable.setColorFilter(getColor(R.color.textPrimary), PorterDuff.Mode.SRC_ATOP); + drawable.setColorFilter(getResources().getColor(R.color.textPrimary), PorterDuff.Mode.SRC_ATOP); } } return super.onCreateOptionsMenu(menu); diff --git a/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java b/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java index e864af9..d8771c2 100644 --- a/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java +++ b/app/src/main/java/xyz/fycz/myreader/common/APPCONST.java @@ -10,7 +10,7 @@ import java.io.File; public class APPCONST { - public static String publicKey = "";//服务端公钥 + public static String publicKey = "fyds1.0";//服务端公钥 public static String privateKey;//app私钥 public final static String s = "11940364935628058505"; public static final String KEY = "readerByFengyue"; @@ -58,6 +58,7 @@ public class APPCONST { public static final String FILE_NAME_SETTING = "setting"; public static final String FILE_NAME_UPDATE_INFO = "updateInfo"; + public static final String FILE_NAME_SPLASH_IMAGE = "splashImage"; public static final int REQUEST_FONT = 1001; public static final int REQUEST_CHAPTER_PAGE = 1002; @@ -85,6 +86,9 @@ public class APPCONST { public final static String channelIdDownload = "channel_download"; + public final static String channelIdRead = "channel_read_aloud"; + + public static final String DEFAULT_WEB_DAV_URL = "https://dav.jianguoyun.com/dav/"; } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java index a51b963..888f3d7 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/BookDetailedActivity.java @@ -572,6 +572,7 @@ public class BookDetailedActivity extends BaseActivity { */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { switch (requestCode) { case APPCONST.REQUEST_READ: diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/FontsActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/FontsActivity.java index d866887..4127c34 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/FontsActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/FontsActivity.java @@ -130,12 +130,13 @@ public class FontsActivity extends BaseActivity { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); String path; if (resultCode == Activity.RESULT_OK && requestCode == APPCONST.SELECT_FILE_CODE) { Uri uri = data.getData(); - if ("file".equalsIgnoreCase(uri.getScheme())){//使用第三方应用打开 + if ("file".equalsIgnoreCase(uri.getScheme())) {//使用第三方应用打开 path = uri.getPath(); - }else { + } else { path = getPath(this, uri); } saveLocalFont(path); diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java index 20f238d..8673655 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/ReadActivity.java @@ -18,12 +18,14 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.TextView; + import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.core.content.ContextCompat; import butterknife.BindView; import butterknife.OnClick; import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.gyf.immersionbar.ImmersionBar; import com.h6ah4i.android.widget.verticalseekbar.VerticalSeekBar; import xyz.fycz.myreader.ActivityManage; @@ -42,7 +44,9 @@ import xyz.fycz.myreader.greendao.entity.Chapter; import xyz.fycz.myreader.greendao.service.BookMarkService; import xyz.fycz.myreader.greendao.service.BookService; import xyz.fycz.myreader.greendao.service.ChapterService; +import xyz.fycz.myreader.model.audio.ReadAloudService; import xyz.fycz.myreader.model.storage.Backup; +import xyz.fycz.myreader.ui.dialog.AudioPlayerDialog; import xyz.fycz.myreader.ui.dialog.CopyContentDialog; import xyz.fycz.myreader.ui.dialog.DialogCreator; import xyz.fycz.myreader.ui.dialog.MyAlertDialog; @@ -111,6 +115,8 @@ public class ReadActivity extends BaseActivity { TextView readTvSetting; @BindView(R.id.read_ll_bottom_menu) LinearLayout readLlBottomMenu; + @BindView(R.id.iv_listen_book) + FloatingActionButton readBtnListenBook; /***************************variable*****************************/ private ImmersionBar immersionBar; @@ -164,6 +170,7 @@ public class ReadActivity extends BaseActivity { private SourceExchangeDialog mSourceDialog; private Dialog mSettingDialog; + private AudioPlayerDialog mAudioPlayerDialog; private boolean hasChangeSource; @@ -360,7 +367,6 @@ public class ReadActivity extends BaseActivity { preLoad(pos - 1 + i); } mBook.setHistoryChapterId(mChapters.get(pos).getTitle()); - mHandler.sendMessage(mHandler.obtainMessage(4)); MyApplication.getApplication().newThread(() -> { if (mPageLoader.getPageStatus() == PageLoader.STATUS_LOADING) { if (!NetworkUtils.isNetWorkAvailable()) { @@ -370,7 +376,6 @@ public class ReadActivity extends BaseActivity { } } }); - } @Override @@ -390,8 +395,18 @@ public class ReadActivity extends BaseActivity { } @Override - public void onPageChange(int pos) { + public void onPageChange(int pos, boolean resetRead) { mHandler.sendMessage(mHandler.obtainMessage(4)); + if ((ReadAloudService.running)) { + if (resetRead) { + mHandler.postDelayed(() ->mAudioPlayerDialog.readAloud(), 500); + return; + } + if (pos == 0) { + mHandler.postDelayed(() ->mAudioPlayerDialog.readAloud(), 500); + return; + } + } } } ); @@ -445,6 +460,23 @@ public class ReadActivity extends BaseActivity { finish(); startActivity(intent); }); + + readBtnListenBook.setOnClickListener(v -> { + if (mSetting.getPageMode() == PageMode.SCROLL) { + ToastUtils.showWarring("朗读暂不支持滚动翻页模式!"); + return; + } + toggleMenu(true); + if (mAudioPlayerDialog == null) { + mAudioPlayerDialog = new AudioPlayerDialog(this, mPageLoader); + mAudioPlayerDialog.setOnDismissListener(dialog -> hideSystemBar()); + } + if (!ReadAloudService.running) { + mAudioPlayerDialog.aloudStatus = ReadAloudService.Status.STOP; + SystemUtil.ignoreBatteryOptimization(this); + } + mAudioPlayerDialog.show(); + }); } @Override @@ -455,7 +487,6 @@ public class ReadActivity extends BaseActivity { intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); intentFilter.addAction(Intent.ACTION_TIME_TICK); registerReceiver(mReceiver, intentFilter); - //当书籍Collected且书籍id不为空的时候保存上次阅读信息 if (isCollected && !StringHelper.isEmpty(mBook.getId())) { //保存上次阅读信息 @@ -500,6 +531,10 @@ public class ReadActivity extends BaseActivity { } else if (mSettingDialog.isShowing()) { mSettingDialog.dismiss(); return; + } else if (ReadAloudService.running && mAudioPlayerDialog.aloudStatus == ReadAloudService.Status.PLAY) { + ReadAloudService.pause(this); + ToastUtils.showInfo("朗读暂停!"); + return; } finish(); } @@ -555,6 +590,7 @@ public class ReadActivity extends BaseActivity { if (autoPage) { autoPageStop(); } + ReadAloudService.stop(this); for (int i = 0; i < 9; i++) { mHandler.removeMessages(i + 1); } @@ -655,6 +691,7 @@ public class ReadActivity extends BaseActivity { break; } } + super.onActivityResult(requestCode, resultCode, data); } /**************************method*********************************/ /** @@ -1047,6 +1084,9 @@ public class ReadActivity extends BaseActivity { * 默认是隐藏的 */ private void toggleMenu(boolean hideStatusBar) { + toggleMenu(hideStatusBar, false); + } + public void toggleMenu(boolean hideStatusBar, boolean home) { initMenuAnim(); if (readAblTopMenu.getVisibility() == View.VISIBLE) { //关闭 @@ -1054,18 +1094,22 @@ public class ReadActivity extends BaseActivity { readLlBottomMenu.startAnimation(mBottomOutAnim); readAblTopMenu.setVisibility(GONE); readLlBottomMenu.setVisibility(GONE); - readTvPageTip.setVisibility(GONE); if (hideStatusBar) { hideSystemBar(); } - } else { - readTvPageTip.setVisibility(VISIBLE); - readAblTopMenu.setVisibility(View.VISIBLE); - readLlBottomMenu.setVisibility(View.VISIBLE); - readAblTopMenu.startAnimation(mTopInAnim); - readLlBottomMenu.startAnimation(mBottomInAnim); - showSystemBar(); + return; + } + if (ReadAloudService.running && !home){ + if (mAudioPlayerDialog != null) { + mAudioPlayerDialog.show(); + return; + } } + readAblTopMenu.setVisibility(View.VISIBLE); + readLlBottomMenu.setVisibility(View.VISIBLE); + readAblTopMenu.startAnimation(mTopInAnim); + readLlBottomMenu.startAnimation(mBottomInAnim); + showSystemBar(); } //初始化菜单动画 @@ -1085,8 +1129,10 @@ public class ReadActivity extends BaseActivity { private void hideSystemBar() { //隐藏 - SystemBarUtils.hideStableStatusBar(this); - SystemBarUtils.hideStableNavBar(this); + if (readAblTopMenu.getVisibility() != VISIBLE || (mAudioPlayerDialog != null && !mAudioPlayerDialog.isShowing())) { + SystemBarUtils.hideStableStatusBar(this); + SystemBarUtils.hideStableNavBar(this); + } } /******************设置相关*****************/ @@ -1112,7 +1158,11 @@ public class ReadActivity extends BaseActivity { mSettingDialog.dismiss(); }, this::showPageModeDialog, v -> { if (mSetting.getPageMode() == PageMode.SCROLL) { - ToastUtils.showWarring("滚动暂时不支持自动翻页"); + ToastUtils.showWarring("滚动暂时不支持自动翻页!"); + return; + } + if (ReadAloudService.running){ + ToastUtils.showWarring("请先关闭语音朗读!"); return; } mSettingDialog.dismiss(); @@ -1309,7 +1359,7 @@ public class ReadActivity extends BaseActivity { keepScreenOn(true); return; } - int screenOffTime = screenTimeOut * 1000 - ScreenHelper.getScreenOffTime(this); + int screenOffTime = screenTimeOut * 1000 - SystemUtil.getScreenOffTime(this); if (screenOffTime > 0) { mHandler.removeCallbacks(keepScreenRunnable); keepScreenOn(true); @@ -1508,7 +1558,7 @@ public class ReadActivity extends BaseActivity { /** * 停止自动翻页 */ - private void autoPageStop() { + public void autoPageStop() { autoPage = false; autoPage(); } @@ -1525,4 +1575,5 @@ public class ReadActivity extends BaseActivity { autoPage(); }); } + } diff --git a/app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java b/app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java index 1412969..a893cdb 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/activity/SplashActivity.java @@ -7,16 +7,40 @@ import android.os.Build; import android.os.Bundle; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.AppCompatImageView; import androidx.core.app.ActivityCompat; + +import android.util.Log; import android.view.Window; import android.view.WindowManager; + +import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.signature.ObjectKey; +import com.gyf.immersionbar.ImmersionBar; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import butterknife.BindView; import xyz.fycz.myreader.R; +import xyz.fycz.myreader.application.MyApplication; +import xyz.fycz.myreader.base.BaseActivity; +import xyz.fycz.myreader.common.APPCONST; +import xyz.fycz.myreader.util.DateHelper; +import xyz.fycz.myreader.util.IOUtils; import xyz.fycz.myreader.util.PermissionsChecker; +import xyz.fycz.myreader.util.SharedPreUtils; import xyz.fycz.myreader.util.ToastUtils; +import xyz.fycz.myreader.util.utils.ImageLoader; +import xyz.fycz.myreader.util.utils.OkHttpUtils; +import xyz.fycz.myreader.util.utils.SystemBarUtils; -public class SplashActivity extends AppCompatActivity { +public class SplashActivity extends BaseActivity { /*************Constant**********/ - private static final int WAIT_INTERVAL = 1000; + private static int WAIT_INTERVAL = 1000; private static final int PERMISSIONS_REQUEST_STORAGE = 1; static final String[] PERMISSIONS = { @@ -24,6 +48,8 @@ public class SplashActivity extends AppCompatActivity { Manifest.permission.WRITE_EXTERNAL_STORAGE }; + @BindView(R.id.iv_splash) + AppCompatImageView ivSplash; private PermissionsChecker mPermissionsChecker; private Thread myThread = new Thread() {//创建子线程 @@ -40,30 +66,89 @@ public class SplashActivity extends AppCompatActivity { } }; + @Override + protected int getContentId() { + return R.layout.activity_splash; + } + + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setFullScreen(); - setContentView(R.layout.activity_splash); + ImmersionBar.with(this) + .fullScreen(true) + .init(); + SystemBarUtils.hideStableStatusBar(this); + loadImage(); if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M){ mPermissionsChecker = new PermissionsChecker(this); requestPermission(); }else { myThread.start(); } + } - + private void loadImage() { + File imgFile = getFileStreamPath(APPCONST.FILE_NAME_SPLASH_IMAGE); + if (!imgFile.exists() || SharedPreUtils.getInstance().getBoolean("needUdSI")){ + downLoadImage(); + return; + } + String splashLoadDate = SharedPreUtils.getInstance().getString("splashTime", ""); + if (splashLoadDate.equals("")) { + return; + } + long startTime = 0; + long endTime = 0; + long curTime = DateHelper.getLongDate(); + if (splashLoadDate.contains("~")) { + String[] splashLoadDates = splashLoadDate.split("~"); + startTime = DateHelper.strDateToLong(splashLoadDates[0] + " 00:00:00"); + endTime = DateHelper.strDateToLong(splashLoadDates[1] + " 00:00:00"); + } + if (startTime == 0){ + startTime = DateHelper.strDateToLong(splashLoadDate + " 00:00:00"); + } + if (endTime == 0){ + endTime = startTime + 24 * 60 * 60 * 1000; + } + if (curTime >= startTime && curTime <= endTime){ + WAIT_INTERVAL = 1500; + ImageLoader.INSTANCE + .load(this, imgFile) + .error(R.drawable.start) + .signature(new ObjectKey(splashLoadDate)) + .into(ivSplash); + } } - // 全屏显示 - private void setFullScreen() { - // 如果该类是 extends Activity ,下面这句代码起作用 - // 去除ActionBar(因使用的是NoActionBar的主题,故此句有无皆可) - requestWindowFeature(Window.FEATURE_NO_TITLE); - // 如果该类是 extends AppCompatActivity, 下面这句代码起作用 - if (getSupportActionBar() != null){ getSupportActionBar().hide(); } - // 去除状态栏,如 电量、Wifi信号等 - getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN); + + private void downLoadImage() { + MyApplication.getApplication().newThread(() -> { + String url = SharedPreUtils.getInstance().getString("splashImageUrl", ""); + if (!url.equals("")) { + InputStream is = null; + FileOutputStream fos = null; + try { + is = OkHttpUtils.getInputStream(url); + fos = openFileOutput(APPCONST.FILE_NAME_SPLASH_IMAGE, MODE_PRIVATE); + byte[] bytes = new byte[1024]; + int len; + while ((len = is.read(bytes)) != -1){ + fos.write(bytes, 0, len); + } + fos.flush(); + Log.d("SplashActivity", "downLoadImage success!"); + } catch (IOException e) { + File data = getFileStreamPath(APPCONST.FILE_NAME_SPLASH_IMAGE); + if (data != null && data.exists()){ + data.delete(); + } + e.printStackTrace(); + }finally { + IOUtils.close(is, fos); + } + } + }); } private void requestPermission(){ diff --git a/app/src/main/java/xyz/fycz/myreader/ui/adapter/SuggestBookAdapter.java b/app/src/main/java/xyz/fycz/myreader/ui/adapter/SuggestBookAdapter.java index 191dc96..6afb2eb 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/adapter/SuggestBookAdapter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/adapter/SuggestBookAdapter.java @@ -13,9 +13,7 @@ import xyz.fycz.myreader.R; import java.util.ArrayList; -/** - * Created by zhao on 2017/7/26. - */ + public class SuggestBookAdapter extends ArrayAdapter { diff --git a/app/src/main/java/xyz/fycz/myreader/ui/dialog/SourceExchangeDialog.java b/app/src/main/java/xyz/fycz/myreader/ui/dialog/SourceExchangeDialog.java index c31332c..07594a4 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/dialog/SourceExchangeDialog.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/dialog/SourceExchangeDialog.java @@ -30,9 +30,6 @@ import java.util.ArrayList; import java.util.List; /** - * Created by Zhouas666 on 2019-04-11 - * Github: https://github.com/zas023 - *

* 换源dialog */ diff --git a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookStorePresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookStorePresenter.java index 65f6c25..8b2ddfb 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookStorePresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookStorePresenter.java @@ -26,9 +26,6 @@ import xyz.fycz.myreader.webapi.crawler.find.QiDianMobileRank; import java.util.ArrayList; import java.util.List; -/** - * Created by zhao on 2017/7/25. - */ public class BookStorePresenter implements BasePresenter { diff --git a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java index d7402b3..c65cfd7 100644 --- a/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java +++ b/app/src/main/java/xyz/fycz/myreader/ui/presenter/BookcasePresenter.java @@ -39,6 +39,7 @@ 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.ui.activity.*; import xyz.fycz.myreader.webapi.crawler.base.ReadCrawler; import xyz.fycz.myreader.webapi.crawler.ReadCrawlerUtil; @@ -79,6 +80,7 @@ public class BookcasePresenter implements BasePresenter { private Setting mSetting; private final List errorLoadingBooks = new ArrayList<>(); private int finishLoadBookCount = 0; + private final BackupAndRestore mBackupAndRestore; // private int notifyId = 11; private ExecutorService es = Executors.newFixedThreadPool(1);//更新/下载线程池 @@ -174,6 +176,7 @@ public class BookcasePresenter implements BasePresenter { mMainActivity = (MainActivity) (mBookcaseFragment.getActivity()); // mChapterService = new ChapterService(); mSetting = SysManager.getSetting(); + mBackupAndRestore = new BackupAndRestore(); } //启动 diff --git a/app/src/main/java/xyz/fycz/myreader/util/ScreenHelper.java b/app/src/main/java/xyz/fycz/myreader/util/ScreenHelper.java deleted file mode 100644 index 0e900d8..0000000 --- a/app/src/main/java/xyz/fycz/myreader/util/ScreenHelper.java +++ /dev/null @@ -1,30 +0,0 @@ -package xyz.fycz.myreader.util; - -import android.content.Context; -import android.provider.Settings; -import android.util.DisplayMetrics; - -import androidx.appcompat.app.AppCompatActivity; - -/** - */ - -public class ScreenHelper { - - public static double getScreenPhysicalSize(AppCompatActivity ctx) { - DisplayMetrics dm = new DisplayMetrics(); - ctx.getWindowManager().getDefaultDisplay().getMetrics(dm); - double diagonalPixels = Math.sqrt(Math.pow(dm.widthPixels, 2) + Math.pow(dm.heightPixels, 2)); - return diagonalPixels / (160 * dm.density); - } - public static int getScreenOffTime(Context context) { - int screenOffTime = 0; - try { - screenOffTime = Settings.System.getInt(context.getContentResolver(), - Settings.System.SCREEN_OFF_TIMEOUT); - } catch (Exception e) { - e.printStackTrace(); - } - return screenOffTime; - } -} diff --git a/app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java b/app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java index f4f5cf4..2b30e21 100644 --- a/app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java +++ b/app/src/main/java/xyz/fycz/myreader/util/utils/OkHttpUtils.java @@ -9,6 +9,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import java.io.IOException; +import java.io.InputStream; import java.security.SecureRandom; public class OkHttpUtils { @@ -32,7 +33,7 @@ public class OkHttpUtils { .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"); + .addHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4168.3 Safari/537.36"); if (requestBody != null) { builder.post(requestBody); Log.d("HttpPost URl", url); @@ -66,4 +67,19 @@ public class OkHttpUtils { return ssfFactory; } + + public static InputStream getInputStream(String url) throws IOException { + Request.Builder builder = new Request.Builder() + .addHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4168.3 Safari/537.36"); + Request request = builder + .url(url) + .build(); + Response response = okHttpClient + .newCall(request) + .execute(); + if (response.body() == null){ + return null; + } + return response.body().byteStream(); + } } diff --git a/app/src/main/java/xyz/fycz/myreader/widget/page/LocalPageLoader.java b/app/src/main/java/xyz/fycz/myreader/widget/page/LocalPageLoader.java index c48cb16..c77827b 100644 --- a/app/src/main/java/xyz/fycz/myreader/widget/page/LocalPageLoader.java +++ b/app/src/main/java/xyz/fycz/myreader/widget/page/LocalPageLoader.java @@ -2,15 +2,10 @@ package xyz.fycz.myreader.widget.page; import android.util.Log; import io.reactivex.*; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.annotations.NonNull; import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; -import xyz.fycz.myreader.application.MyApplication; import xyz.fycz.myreader.webapi.callback.ResultCallback; import xyz.fycz.myreader.common.APPCONST; import xyz.fycz.myreader.entity.Setting; -import xyz.fycz.myreader.entity.Void; import xyz.fycz.myreader.greendao.entity.Book; import xyz.fycz.myreader.greendao.entity.Chapter; import xyz.fycz.myreader.greendao.service.ChapterService; @@ -68,10 +63,10 @@ public class LocalPageLoader extends PageLoader { } - /*private List convertTxtChapter(List bookChapters) { - List txtChapters = new ArrayList<>(bookChapters.size()); + /*private List convertTxtChapter(List bookChapters) { + List txtChapters = new ArrayList<>(bookChapters.size()); for (BookChapterBean bean : bookChapters) { - TxtChapter chapter = new TxtChapter(); + BookChapterBean chapter = new BookChapterBean(); chapter.title = bean.getTitle(); chapter.start = bean.getStart(); chapter.end = bean.getEnd(); @@ -85,11 +80,11 @@ public class LocalPageLoader extends PageLoader { * 1. 序章的添加 * 2. 章节存在的书本的虚拟分章效果 */ - public List loadChapters() throws IOException { + public List loadChapters() throws IOException { mBookFile = new File(mCollBook.getChapterUrl()); //获取文件编码 mCharset = FileUtils.getFileEncode(mBookFile.getAbsolutePath()); - List chapters = new ArrayList<>(); + List chapters = new ArrayList<>(); RandomAccessFile bookStream = null; boolean hasChapter = false; //获取文件流 @@ -132,7 +127,7 @@ public class LocalPageLoader extends PageLoader { //如果当前对整个文件的偏移位置为0的话,那么就是序章 if (curOffset == 0) { //创建序章 - TxtChapter preChapter = new TxtChapter(); + BookChapterBean preChapter = new BookChapterBean(); preChapter.title = "序章"; preChapter.start = 0; preChapter.end = chapterContent.getBytes(mCharset).length; //获取String的byte值,作为最终值 @@ -143,7 +138,7 @@ public class LocalPageLoader extends PageLoader { } //创建当前章节 - TxtChapter curChapter = new TxtChapter(); + BookChapterBean curChapter = new BookChapterBean(); curChapter.title = matcher.group(); curChapter.start = preChapter.end; chapters.add(curChapter); @@ -151,7 +146,7 @@ public class LocalPageLoader extends PageLoader { //否则就block分割之后,上一个章节的剩余内容 else { //获取上一章节 - TxtChapter lastChapter = chapters.get(chapters.size() - 1); + BookChapterBean lastChapter = chapters.get(chapters.size() - 1); //将当前段落添加上一章去 lastChapter.end += chapterContent.getBytes(mCharset).length; @@ -161,7 +156,7 @@ public class LocalPageLoader extends PageLoader { } //创建当前章节 - TxtChapter curChapter = new TxtChapter(); + BookChapterBean curChapter = new BookChapterBean(); curChapter.title = matcher.group(); curChapter.start = lastChapter.end; chapters.add(curChapter); @@ -174,7 +169,7 @@ public class LocalPageLoader extends PageLoader { seekPos += chapterContent.length(); //获取上一章节 - TxtChapter lastChapter = chapters.get(chapters.size() - 1); + BookChapterBean lastChapter = chapters.get(chapters.size() - 1); lastChapter.end = lastChapter.start + chapterContent.getBytes(mCharset).length; //如果章节内容太小,则移除 @@ -183,14 +178,14 @@ public class LocalPageLoader extends PageLoader { } //创建当前章节 - TxtChapter curChapter = new TxtChapter(); + BookChapterBean curChapter = new BookChapterBean(); curChapter.title = matcher.group(); curChapter.start = lastChapter.end; chapters.add(curChapter); } //如果章节不存在则创建章节 else { - TxtChapter curChapter = new TxtChapter(); + BookChapterBean curChapter = new BookChapterBean(); curChapter.title = matcher.group(); curChapter.start = 0; chapters.add(curChapter); @@ -220,7 +215,7 @@ public class LocalPageLoader extends PageLoader { break; } } - TxtChapter chapter = new TxtChapter(); + BookChapterBean chapter = new BookChapterBean(); chapter.title = "第" + blockPos + "章" + "(" + chapterPos + ")"; chapter.start = curOffset + chapterOffset + 1; chapter.end = curOffset + end; @@ -230,7 +225,7 @@ public class LocalPageLoader extends PageLoader { //设置偏移的位置 chapterOffset = end; } else { - TxtChapter chapter = new TxtChapter(); + BookChapterBean chapter = new BookChapterBean(); chapter.title = "第" + blockPos + "章" + "(" + chapterPos + ")"; chapter.start = curOffset + chapterOffset + 1; chapter.end = curOffset + length; @@ -245,7 +240,7 @@ public class LocalPageLoader extends PageLoader { if (hasChapter) { //设置上一章的结尾 - TxtChapter lastChapter = chapters.get(chapters.size() - 1); + BookChapterBean lastChapter = chapters.get(chapters.size() - 1); lastChapter.end = curOffset; } @@ -264,26 +259,26 @@ public class LocalPageLoader extends PageLoader { public void loadChapters(final ResultCallback resultCallback) { // 通过RxJava异步处理分章事件 - Single.create((SingleOnSubscribe>) e -> { + Single.create((SingleOnSubscribe>) e -> { e.onSuccess(loadChapters()); - }).compose(RxUtils::toSimpleSingle).subscribe(new SingleObserver>() { + }).compose(RxUtils::toSimpleSingle).subscribe(new SingleObserver>() { @Override public void onSubscribe(Disposable d) { mChapterDisp = d; } @Override - public void onSuccess(List chapters) { + public void onSuccess(List chapters) { mChapterDisp = null; isChapterListPrepare = true; List mChapters = new ArrayList<>(); int i = 0; - for (TxtChapter txtChapter : chapters) { + for (BookChapterBean bookChapterBean : chapters) { Chapter chapter = new Chapter(); chapter.setBookId(mCollBook.getId()); - chapter.setTitle(txtChapter.getTitle().replace("\n", "").replaceAll("^\\s+|\\s+$", "")); + chapter.setTitle(bookChapterBean.getTitle().replace("\n", "").replaceAll("^\\s+|\\s+$", "")); chapter.setId(StringHelper.getStringRandom(25)); - String content = getChapterContent(txtChapter); + String content = getChapterContent(bookChapterBean); if (StringHelper.isEmpty(content)) { continue; } @@ -312,7 +307,7 @@ public class LocalPageLoader extends PageLoader { * @param chapter * @return */ - private String getChapterContent(TxtChapter chapter) { + private String getChapterContent(BookChapterBean chapter) { RandomAccessFile bookStream = null; try { bookStream = new RandomAccessFile(mBookFile, "r"); diff --git a/app/src/main/java/xyz/fycz/myreader/widget/page/NetPageLoader.java b/app/src/main/java/xyz/fycz/myreader/widget/page/NetPageLoader.java index 6cef6bb..2b7c008 100644 --- a/app/src/main/java/xyz/fycz/myreader/widget/page/NetPageLoader.java +++ b/app/src/main/java/xyz/fycz/myreader/widget/page/NetPageLoader.java @@ -32,10 +32,10 @@ public class NetPageLoader extends PageLoader { this.mReadCrawler = mReadCrawler; } - /*private List convertTxtChapter(List bookChapters) { - List txtChapters = new ArrayList<>(bookChapters.size()); + /*private List convertTxtChapter(List bookChapters) { + List txtChapters = new ArrayList<>(bookChapters.size()); for (Chapter bean : bookChapters) { - TxtChapter chapter = new TxtChapter(); + BookChapterBean chapter = new BookChapterBean(); chapter.setBookId(bean.getBookId()); chapter.setTitle(bean.getTitle()); chapter.setLink(bean.getUrl()); diff --git a/app/src/main/java/xyz/fycz/myreader/widget/page/PageLoader.java b/app/src/main/java/xyz/fycz/myreader/widget/page/PageLoader.java index 8f6436d..6a2bbfd 100644 --- a/app/src/main/java/xyz/fycz/myreader/widget/page/PageLoader.java +++ b/app/src/main/java/xyz/fycz/myreader/widget/page/PageLoader.java @@ -5,6 +5,8 @@ import android.graphics.*; import android.text.TextUtils; import androidx.core.content.ContextCompat; import android.text.TextPaint; +import android.util.Log; + import io.reactivex.Single; import io.reactivex.SingleObserver; import io.reactivex.SingleOnSubscribe; @@ -17,11 +19,11 @@ import xyz.fycz.myreader.enums.Language; import xyz.fycz.myreader.greendao.entity.Book; import xyz.fycz.myreader.entity.Setting; import xyz.fycz.myreader.greendao.entity.Chapter; +import xyz.fycz.myreader.model.audio.ReadAloudService; import xyz.fycz.myreader.util.ToastUtils; import xyz.fycz.myreader.util.utils.RxUtils; import xyz.fycz.myreader.util.utils.ScreenUtils; import xyz.fycz.myreader.util.utils.StringUtils; -import xyz.fycz.myreader.widget.animation.PageAnimation; import java.io.BufferedReader; import java.io.File; @@ -67,11 +69,11 @@ public abstract class PageLoader { // 当前显示的页 private TxtPage mCurPage; // 上一章的页面列表缓存 - private List mPrePageList; + private TxtChapter mPreChapter; // 当前章节的页面列表 - private List mCurPageList; + private TxtChapter mCurChapter; // 下一章的页面列表缓存 - private List mNextPageList; + private TxtChapter mNextChapter; // 绘制电池的画笔 private Paint mBatteryPaint; @@ -143,6 +145,9 @@ public abstract class PageLoader { protected int mCurChapterPos = 0; //上一章的记录 private int mLastChapterPos = 0; + private int readTextLength; //已读字符数 + private boolean resetReadAloud; //是否重新朗读 + private int readAloudParagraph; //正在朗读章节 /*****************************init params*******************************/ public PageLoader(PageView pageView, Book collBook, Setting setting) { @@ -306,13 +311,13 @@ public abstract class PageLoader { mCurChapterPos = pos; // 将上一章的缓存设置为null - mPrePageList = null; + mPreChapter = null; // 如果当前下一章缓存正在执行,则取消 if (mPreLoadDisp != null) { mPreLoadDisp.dispose(); } // 将下一章缓存设置为null - mNextPageList = null; + mNextChapter = null; // 打开指定章节 openChapter(); @@ -349,6 +354,16 @@ public abstract class PageLoader { public boolean skipToNextPage() { return mPageView.autoNextPage(); } + /** + * 翻到下一页,无动画 + */ + private void noAnimationToNextPage() { + if (getPagePos() < mCurChapter.getPageSize() - 1) { + skipToPage(getPagePos() + 1); + return; + } + skipNextChapter(); + } /** * 更新时间 @@ -399,8 +414,8 @@ public abstract class PageLoader { /*// 存储文字大小 mSettingManager.setReadWordSize(mTextSize);*/ // 取消缓存 - mPrePageList = null; - mNextPageList = null; + mPreChapter = null; + mNextChapter = null; // 如果当前已经显示数据 if (isChapterListPrepare && mStatus == STATUS_FINISH) { @@ -408,12 +423,12 @@ public abstract class PageLoader { dealLoadPageList(mCurChapterPos); // 防止在最后一页,通过修改字体,以至于页面数减少导致崩溃的问题 - if (mCurPage.position >= mCurPageList.size()) { - mCurPage.position = mCurPageList.size() - 1; + if (mCurPage.position >= mCurChapter.getPageSize()) { + mCurPage.position = mCurChapter.getPageSize() - 1; } // 重新获取指定页面 - mCurPage = mCurPageList.get(mCurPage.position); + mCurPage = mCurChapter.getPage(mCurPage.position); } mPageView.drawCurPage(false); @@ -522,8 +537,8 @@ public abstract class PageLoader { mTextPaint.setTypeface(mTypeFace); mTitlePaint.setTypeface(mTypeFace); // 取消缓存 - mPrePageList = null; - mNextPageList = null; + mPreChapter = null; + mNextChapter = null; // 如果当前已经显示数据 if (isChapterListPrepare && mStatus == STATUS_FINISH) { @@ -531,12 +546,12 @@ public abstract class PageLoader { dealLoadPageList(mCurChapterPos); // 防止在最后一页,通过修改字体,以至于页面数减少导致崩溃的问题 - if (mCurPage.position >= mCurPageList.size()) { - mCurPage.position = mCurPageList.size() - 1; + if (mCurPage.position >= mCurChapter.getPageSize()) { + mCurPage.position = mCurChapter.getPageSize() - 1; } // 重新获取指定页面 - mCurPage = mCurPageList.get(mCurPage.position); + mCurPage = mCurChapter.getPage(mCurPage.position); } mPageView.drawCurPage(false); @@ -658,10 +673,10 @@ public abstract class PageLoader { * @return */ public int getAllPagePos() { - if (mCurPageList == null) { + if (mCurChapter == null) { return 0; } - return mCurPageList.size(); + return mCurChapter.getPageSize(); } /** @@ -743,8 +758,8 @@ public abstract class PageLoader { int position = mCollBook.getLastReadPosition(); // 防止记录页的页号,大于当前最大页号 - if (position >= mCurPageList.size()) { - position = mCurPageList.size() - 1; + if (position >= mCurChapter.getPageSize()) { + position = mCurChapter.getPageSize() - 1; } mCurPage = getCurPage(position); mCancelPage = mCurPage; @@ -778,12 +793,11 @@ public abstract class PageLoader { } clearList(mChapterList); - clearList(mCurPageList); - clearList(mNextPageList); mChapterList = null; - mCurPageList = null; - mNextPageList = null; + mPreChapter = null; + mCurChapter = null; + mNextChapter = null; mPageView = null; mCurPage = null; } @@ -808,7 +822,7 @@ public abstract class PageLoader { * @param chapterPos:章节序号 * @return */ - private List loadPageList(int chapterPos) throws Exception { + private TxtChapter loadPageList(int chapterPos) throws Exception { // 获取章节 Chapter chapter = mChapterList.get(chapterPos); // 判断章节是否存在 @@ -817,9 +831,9 @@ public abstract class PageLoader { } // 获取章节的文本流 BufferedReader reader = getChapterReader(chapter); - List chapters = loadPages(chapter, reader); + TxtChapter txtChapter = loadPages(chapter, reader); - return chapters; + return txtChapter; } @@ -896,7 +910,7 @@ public abstract class PageLoader { float y = mDisplayHeight - mTipPaint.getFontMetrics().bottom - tipMarginHeight; // 只有finish的时候采用页码 if (mStatus == STATUS_FINISH) { - String percent = (mCurPage.position + 1) + "/" + mCurPageList.size(); + String percent = (mCurPage.position + 1) + "/" + mCurChapter.getPageSize(); canvas.drawText(percent, mMarginWidth, y, mTipPaint); } @@ -1012,8 +1026,11 @@ public abstract class PageLoader { String str = null; //对标题进行绘制 + boolean isLight; for (int i = 0; i < mCurPage.titleLines; ++i) { str = mCurPage.lines.get(i); + isLight = ReadAloudService.running && readAloudParagraph == 0; + mTitlePaint.setColor(isLight ? mContext.getResources().getColor(R.color.sys_color) : mTextColor); //设置顶部间距 if (i == 0) { @@ -1035,8 +1052,13 @@ public abstract class PageLoader { } //对内容进行绘制 + int strLength = 0; for (int i = mCurPage.titleLines; i < mCurPage.lines.size(); ++i) { str = mCurPage.lines.get(i); + strLength = strLength + str.length(); + int paragraphLength = mCurPage.position == 0 ? strLength : mCurChapter.getPageLength(mCurPage.position - 1) + strLength; + isLight = ReadAloudService.running && readAloudParagraph == mCurChapter.getParagraphIndex(paragraphLength); + mTextPaint.setColor(isLight ? mContext.getResources().getColor(R.color.sys_color) : mTextColor); canvas.drawText(str, mMarginWidth, top, mTextPaint); if (str.endsWith("\n")) { top += para; @@ -1128,19 +1150,19 @@ public abstract class PageLoader { mCurChapterPos = prevChapter; // 当前章缓存为下一章 - mNextPageList = mCurPageList; + mNextChapter = mCurChapter; // 判断是否具有上一章缓存 - if (mPrePageList != null) { - mCurPageList = mPrePageList; - mPrePageList = null; + if (mPreChapter != null) { + mCurChapter = mPreChapter; + mPreChapter = null; // 回调 chapterChangeCallback(); } else { dealLoadPageList(prevChapter); } - return mCurPageList != null; + return mCurChapter != null; } private boolean hasPrevChapter() { @@ -1180,7 +1202,7 @@ public abstract class PageLoader { mCancelPage = mCurPage; // 解析下一章数据 if (parseNextChapter()) { - mCurPage = mCurPageList.get(0); + mCurPage = mCurChapter.getPage(0); } else { mCurPage = new TxtPage(); } @@ -1201,7 +1223,7 @@ public abstract class PageLoader { dealLoadPageList(mCurChapterPos); // 预加载下一页面 preLoadNextChapter(); - return mCurPageList != null; + return mCurChapter != null; } /** @@ -1216,12 +1238,12 @@ public abstract class PageLoader { mCurChapterPos = nextChapter; // 将当前章的页面列表,作为上一章缓存 - mPrePageList = mCurPageList; + mPreChapter = mCurChapter; // 是否下一章数据已经预加载了 - if (mNextPageList != null) { - mCurPageList = mNextPageList; - mNextPageList = null; + if (mNextChapter != null) { + mCurChapter = mNextChapter; + mNextChapter = null; // 回调 chapterChangeCallback(); } else { @@ -1230,20 +1252,20 @@ public abstract class PageLoader { } // 预加载下一页面 preLoadNextChapter(); - return mCurPageList != null; + return mCurChapter != null; } private void dealLoadPageList(int chapterPos) { try { - mCurPageList = loadPageList(chapterPos); - if (mCurPageList != null) { - if (mCurPageList.isEmpty()) { + mCurChapter = loadPageList(chapterPos); + if (mCurChapter != null) { + if (mCurChapter.getTxtPageList().isEmpty()) { mStatus = STATUS_EMPTY; // 添加一个空数据 TxtPage page = new TxtPage(); page.lines = new ArrayList<>(1); - mCurPageList.add(page); + mCurChapter.addPage(page); } else { mStatus = STATUS_FINISH; } @@ -1252,7 +1274,7 @@ public abstract class PageLoader { } } catch (Exception e) { e.printStackTrace(); - mCurPageList = null; + mCurChapter = null; mStatus = STATUS_ERROR; } @@ -1263,7 +1285,9 @@ public abstract class PageLoader { private void chapterChangeCallback() { if (mPageChangeListener != null) { mPageChangeListener.onChapterChange(mCurChapterPos); - mPageChangeListener.onPageCountChange(mCurPageList != null ? mCurPageList.size() : 0); + mPageChangeListener.onPageChange(0, resetReadAloud); + resetReadAloud = true; + mPageChangeListener.onPageCountChange(mCurChapter != null ? mCurChapter.getPageSize() : 0); } } @@ -1282,17 +1306,17 @@ public abstract class PageLoader { } //调用异步进行预加载加载 - Single.create((SingleOnSubscribe>) e -> e.onSuccess(loadPageList(nextChapter))) + Single.create((SingleOnSubscribe) e -> e.onSuccess(loadPageList(nextChapter))) .compose(RxUtils::toSimpleSingle) - .subscribe(new SingleObserver>() { + .subscribe(new SingleObserver() { @Override public void onSubscribe(Disposable d) { mPreLoadDisp = d; } @Override - public void onSuccess(List pages) { - mNextPageList = pages; + public void onSuccess(TxtChapter txtChapter) { + mNextChapter = txtChapter; } @Override @@ -1305,7 +1329,7 @@ public abstract class PageLoader { // 取消翻页 void pageCancel() { if (mCurPage.position == 0 && mCurChapterPos > mLastChapterPos) { // 加载到下一章取消了 - if (mPrePageList != null) { + if (mPreChapter != null) { cancelNextChapter(); } else { if (parsePrevChapter()) { @@ -1314,15 +1338,15 @@ public abstract class PageLoader { mCurPage = new TxtPage(); } } - } else if (mCurPageList == null - || (mCurPage.position == mCurPageList.size() - 1 + } else if (mCurChapter == null + || (mCurPage.position == mCurChapter.getPageSize() - 1 && mCurChapterPos < mLastChapterPos)) { // 加载上一章取消了 - if (mNextPageList != null) { + if (mNextChapter != null) { cancelPreChapter(); } else { if (parseNextChapter()) { - mCurPage = mCurPageList.get(0); + mCurPage = mCurChapter.getPage(0); } else { mCurPage = new TxtPage(); } @@ -1338,9 +1362,9 @@ public abstract class PageLoader { mLastChapterPos = mCurChapterPos; mCurChapterPos = temp; - mNextPageList = mCurPageList; - mCurPageList = mPrePageList; - mPrePageList = null; + mNextChapter = mCurChapter; + mCurChapter = mPreChapter; + mPreChapter = null; chapterChangeCallback(); @@ -1354,9 +1378,9 @@ public abstract class PageLoader { mLastChapterPos = mCurChapterPos; mCurChapterPos = temp; // 重置页面列表 - mPrePageList = mCurPageList; - mCurPageList = mNextPageList; - mNextPageList = null; + mPreChapter = mCurChapter; + mCurChapter = mNextChapter; + mNextChapter = null; chapterChangeCallback(); @@ -1372,20 +1396,20 @@ public abstract class PageLoader { * @param br:章节的文本流 * @return */ - private List loadPages(Chapter chapter, BufferedReader br) { - //生成的页面 - List pages = new ArrayList<>(); + private TxtChapter loadPages(Chapter chapter, BufferedReader br) { + TxtChapter txtChapter = new TxtChapter(chapter.getNumber()); //使用流的方式加载 List lines = new ArrayList<>(); float rHeight = mVisibleHeight; int titleLinesCount = 0; boolean showTitle = true; // 是否展示标题 String paragraph = chapter.getTitle();//默认展示标题 + paragraph = paragraph.trim() + "\n"; try { while (showTitle || (paragraph = br.readLine()) != null) { // 重置段落 if (!showTitle) { - paragraph = paragraph.replaceAll("\\s", ""); + paragraph = paragraph.replaceAll("\\s", "").trim(); // 如果只有换行符,那么就不执行 if (paragraph.equals("")) continue; paragraph = StringUtils.halfToFull(" " + paragraph + "\n"); @@ -1393,6 +1417,7 @@ public abstract class PageLoader { //设置 title 的顶部间距 rHeight -= mTitlePara; } + addParagraphLength(txtChapter, paragraph.length()); int wordCount = 0; String subStr = null; while (paragraph.length() > 0) { @@ -1406,11 +1431,12 @@ public abstract class PageLoader { if (rHeight <= 0) { // 创建Page TxtPage page = new TxtPage(); - page.position = pages.size(); + page.position = txtChapter.getTxtPageList().size(); page.title = chapter.getTitle(); page.lines = new ArrayList<>(lines); page.titleLines = titleLinesCount; - pages.add(page); + txtChapter.addPage(page); + addTxtPageLength(txtChapter, page.getContent().length()); // 重置Lines lines.clear(); rHeight = mVisibleHeight; @@ -1458,11 +1484,12 @@ public abstract class PageLoader { if (lines.size() != 0) { //创建Page TxtPage page = new TxtPage(); - page.position = pages.size(); + page.position = txtChapter.getTxtPageList().size(); page.title = chapter.getTitle(); page.lines = new ArrayList<>(lines); page.titleLines = titleLinesCount; - pages.add(page); + txtChapter.addPage(page); + addTxtPageLength(txtChapter, page.getContent().length()); //重置Lines lines.clear(); } @@ -1477,18 +1504,44 @@ public abstract class PageLoader { } } } - return pages; + return txtChapter; } + /** + * 添加TxtChapter的长度 + * @param txtChapter + * @param length + */ + private void addTxtPageLength(TxtChapter txtChapter, int length) { + if (txtChapter.getTxtPageLengthList().isEmpty()) { + txtChapter.addTxtPageLength(length); + } else { + txtChapter.addTxtPageLength(txtChapter.getTxtPageLengthList().get(txtChapter.getTxtPageLengthList().size() - 1) + length); + } + } + + /** + * 添加TxtChapter段落长度 + * @param txtChapter + * @param length + */ + private void addParagraphLength(TxtChapter txtChapter, int length) { + if (txtChapter.getParagraphLengthList().isEmpty()) { + txtChapter.addParagraphLength(length); + } else { + txtChapter.addParagraphLength(txtChapter.getParagraphLengthList().get(txtChapter.getParagraphLengthList().size() - 1) + length); + } + } /** * @return:获取初始显示的页面 */ private TxtPage getCurPage(int pos) { if (mPageChangeListener != null) { - mPageChangeListener.onPageChange(pos); + mPageChangeListener.onPageChange(pos, resetReadAloud); + resetReadAloud = true; } - return mCurPageList.get(pos); + return mCurChapter.getPage(pos); } /** @@ -1500,9 +1553,10 @@ public abstract class PageLoader { return null; } if (mPageChangeListener != null) { - mPageChangeListener.onPageChange(pos); + mPageChangeListener.onPageChange(pos,resetReadAloud); + resetReadAloud = true; } - return mCurPageList.get(pos); + return mCurChapter.getPage(pos); } /** @@ -1510,26 +1564,28 @@ public abstract class PageLoader { */ private TxtPage getNextPage() { int pos = mCurPage.position + 1; - if (pos >= mCurPageList.size()) { + if (pos >= mCurChapter.getPageSize()) { return null; } if (mPageChangeListener != null) { - mPageChangeListener.onPageChange(pos); + mPageChangeListener.onPageChange(pos, resetReadAloud); + resetReadAloud = true; } - return mCurPageList.get(pos); + return mCurChapter.getPage(pos); } /** * @return:获取上一个章节的最后一页 */ private TxtPage getPrevLastPage() { - int pos = mCurPageList.size() - 1; + int pos = mCurChapter.getPageSize() - 1; if (mPageChangeListener != null) { - mPageChangeListener.onPageChange(pos); + mPageChangeListener.onPageChange(pos, resetReadAloud); + resetReadAloud = true; } - return mCurPageList.get(pos); + return mCurChapter.getPage(pos); } /** @@ -1596,8 +1652,8 @@ public abstract class PageLoader { * @return 本页内容 */ public String getContent() { - if (mCurPageList == null) return null; - if (mCurPageList.size() == 0) return null; + if (mCurChapter == null) return null; + if (mCurChapter.getPageSize() == 0) return null; TxtPage txtPage = mCurPage; StringBuilder s = new StringBuilder(); int size = txtPage.lines.size(); @@ -1608,6 +1664,80 @@ public abstract class PageLoader { } return s.toString(); } + + /** + * @return 本章未读内容 + */ + public String getUnReadContent() { + if (mCurPage == null) return null; + if (mCurChapter == null || mCurChapter.getPageSize() == 0) return null; + StringBuilder s = new StringBuilder(); + String content = getContent(); + if (content != null) { + s.append(content); + } + int mCurPagePos = getPagePos(); + content = getContentStartPage(mCurPagePos + 1); + if (content != null) { + s.append(content); + } + readTextLength = mCurPagePos > 0 ? mCurChapter.getPageLength(mCurPagePos - 1) : 0; + /*if (mPageMode == PageAnimation.Mode.SCROLL) { + for (int i = 0; i < Math.min(Math.max(0, linePos), curChapter().txtChapter.getPage(mCurPagePos).size() - 1); i++) { + readTextLength += curChapter().txtChapter.getPage(mCurPagePos).getLine(i).length(); + } + }*/ + return s.toString(); + } + + + /** + * @param page 开始页数 + * @return 从page页开始的的当前章节所有内容 + */ + private String getContentStartPage(int page) { + if (mCurChapter == null) return null; + if (mCurChapter.getTxtPageList().isEmpty()) return null; + StringBuilder s = new StringBuilder(); + if (mCurChapter.getPageSize() > page) { + for (int i = page; i < mCurChapter.getPageSize(); i++) { + s.append(mCurChapter.getPage(i).getContent()); + } + } + return s.toString(); + } + + /** + * @param start 开始朗读字数 + */ + public void readAloudStart(int start) { + start = readTextLength + start; + int x = mCurChapter.getParagraphIndex(start); + if (readAloudParagraph != x) { + readAloudParagraph = x; + mPageView.drawCurPage(false); + //mPageView.invalidate(); + /*mPageView.drawPage(-1); + mPageView.drawPage(1); + mPageView.invalidate();*/ + } + } + + /** + * @param readAloudLength 已朗读字数 + */ + public void readAloudLength(int readAloudLength) { + if (mCurChapter == null) return; + if (getPageStatus() != STATUS_FINISH) return; + if (mCurChapter.getPageLength(getPagePos()) < 0) return; + if (mPageView.isRunning()) return; + readAloudLength = readTextLength + readAloudLength; + if (readAloudLength >= mCurChapter.getPageLength(getPagePos())) { + resetReadAloud = false; + noAnimationToNextPage(); + mPageView.invalidate(); + } + } /*****************************************interface*****************************************/ public interface OnPageChangeListener { @@ -1644,7 +1774,7 @@ public abstract class PageLoader { * * @param pos:当前的页面的序号 */ - void onPageChange(int pos); + void onPageChange(int pos, boolean resetRead); } } diff --git a/app/src/main/java/xyz/fycz/myreader/widget/page/TxtChapter.java b/app/src/main/java/xyz/fycz/myreader/widget/page/TxtChapter.java index f8cd90d..f31319e 100644 --- a/app/src/main/java/xyz/fycz/myreader/widget/page/TxtChapter.java +++ b/app/src/main/java/xyz/fycz/myreader/widget/page/TxtChapter.java @@ -1,59 +1,99 @@ package xyz.fycz.myreader.widget.page; +import java.util.ArrayList; +import java.util.List; + /** - * Created by newbiechen on 17-7-1. + * Created by zhouas666 on 18-1-23. + * 书籍chapter */ -public class TxtChapter { +public class TxtChapter{ - //章节所属的小说(网络) - String bookId; - //章节名 - String title; + private int position; + private List txtPageList = new ArrayList<>(); + private List txtPageLengthList = new ArrayList<>(); + private List paragraphLengthList = new ArrayList<>(); + private Status status = Status.LOADING; + private String msg; - //章节内容在文章中的起始位置(本地) - long start; - //章节内容在文章中的终止位置(本地) - long end; + TxtChapter(int position) { + this.position = position; + } - public String getBookId() { - return bookId; + public int getPosition() { + return position; } - public void setBookId(String id) { - this.bookId = id; + List getTxtPageList() { + return txtPageList; } - public String getTitle() { - return title; + void addPage(TxtPage txtPage) { + txtPageList.add(txtPage); } - public void setTitle(String title) { - this.title = title; + int getPageSize() { + return txtPageList.size(); } - public long getStart() { - return start; + TxtPage getPage(int page) { + if (!txtPageList.isEmpty()) { + return txtPageList.get(Math.max(0, Math.min(page, txtPageList.size() - 1))); + } + return null; } - public void setStart(long start) { - this.start = start; + Status getStatus() { + return status; } - public long getEnd() { - return end; + void setStatus(Status mStatus) { + this.status = mStatus; } - public void setEnd(long end) { - this.end = end; + public String getMsg() { + return msg; } - @Override - public String toString() { - return "TxtChapter{" + - "title='" + title + '\'' + - ", start=" + start + - ", end=" + end + - '}'; + public void setMsg(String msg) { + this.msg = msg; } + + int getPageLength(int position) { + if (txtPageLengthList != null && position >= 0 && position < txtPageLengthList.size()) { + return txtPageLengthList.get(position); + } + return -1; + } + + void addTxtPageLength(int length) { + txtPageLengthList.add(length); + } + + List getTxtPageLengthList() { + return txtPageLengthList; + } + + List getParagraphLengthList() { + return paragraphLengthList; + } + + void addParagraphLength(int length) { + paragraphLengthList.add(length); + } + + int getParagraphIndex(int length) { + for (int i = 0; i < paragraphLengthList.size(); i++) { + if ((i == 0 || paragraphLengthList.get(i - 1) < length) && length <= paragraphLengthList.get(i)) { + return i; + } + } + return -1; + } + + public enum Status { + LOADING, FINISH, ERROR, EMPTY, CATEGORY_EMPTY, CHANGE_SOURCE, + } + } diff --git a/app/src/main/java/xyz/fycz/myreader/widget/page/TxtPage.java b/app/src/main/java/xyz/fycz/myreader/widget/page/TxtPage.java index aaf12d7..85b7e7f 100644 --- a/app/src/main/java/xyz/fycz/myreader/widget/page/TxtPage.java +++ b/app/src/main/java/xyz/fycz/myreader/widget/page/TxtPage.java @@ -2,16 +2,13 @@ package xyz.fycz.myreader.widget.page; import java.util.List; -/** - * Created by newbiechen on 17-7-1. - */ - public class TxtPage { int position; String title; int titleLines; //当前 lines 中为 title 的行数。 List lines; + public int getTitleLines() { return titleLines; } @@ -23,4 +20,14 @@ public class TxtPage { public String getLine(int i) { return lines.get(i); } + + public String getContent() { + StringBuilder s = new StringBuilder(); + if (lines != null) { + for (int i = 0; i < lines.size(); i++) { + s.append(lines.get(i)); + } + } + return s.toString(); + } } diff --git a/app/src/main/res/layout/activity_read.xml b/app/src/main/res/layout/activity_read.xml index 7831df3..a2faaba 100644 --- a/app/src/main/res/layout/activity_read.xml +++ b/app/src/main/res/layout/activity_read.xml @@ -1,199 +1,239 @@ + 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:orientation="vertical"> - + android:layout_height="45dp" + android:gravity="center_vertical" + android:minHeight="?attr/actionBarSize" + android:theme="?attr/actionBarStyle" + android:transitionName="sharedView" + app:layout_scrollFlags="scroll|enterAlways" + app:popupTheme="@style/AppTheme.PopupOverlay" + app:title="大主宰" /> - - + android:layout_width="fill_parent" + android:layout_height="0.5dp" + android:background="@color/sys_dialog_setting_line" /> + + + - - - - + android:layout_height="wrap_content" + android:layout_marginStart="14dp" + android:layout_marginEnd="14dp" + android:ellipsize="end" + android:maxLines="1" + android:text="章节名称" + android:textColor="@color/read_menu_text" + android:textSize="15sp" /> + + + android:id="@+id/read_pv_content" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clickable="true" + tools:visibility="gone" /> + + + - + android:splitTrack="false" + android:visibility="invisible" + app:seekBarRotation="CW270" + tools:visibility="visible" /> + + 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" /> + - - + + android:layout_height="wrap_content"> + + + + + + android:layout_width="match_parent" + android:layout_height="60dp" + android:background="@color/read_menu_bg" + android:gravity="center_vertical" + android:orientation="horizontal"> + android:id="@+id/read_tv_pre_chapter" + style="@style/MAppTheme.TextAppearance.Read.BottomMenu" + android:layout_width="80dp" + android:layout_height="match_parent" + android:gravity="center" + android:clickable="true" + android:focusable="true" + android:text="上一章" /> + android:id="@+id/read_sb_chapter_progress" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_weight="1" + android:max="100" + android:maxHeight="3dp" + android:minHeight="3dp" + android:progressDrawable="@drawable/seekbar_bg" + android:thumb="@mipmap/thumb" + android:thumbOffset="10dp" /> + android:id="@+id/read_tv_next_chapter" + style="@style/MAppTheme.TextAppearance.Read.BottomMenu" + android:layout_width="80dp" + android:layout_height="match_parent" + android:gravity="center" + android:clickable="true" + android:focusable="true" + android:text="下一章" /> + + android:layout_width="fill_parent" + android:layout_height="0.5dp" + android:background="@color/sys_dialog_setting_line" /> + android:layout_width="match_parent" + android:layout_height="72dp" + android:background="@color/read_menu_bg" + android:orientation="horizontal" + android:paddingTop="12dp" + android:paddingBottom="12dp"> + android:id="@+id/read_tv_category" + style="@style/AppTheme.TextAppearance.Read.BottomMenu.Button" + android:text="目录" + android:clickable="true" + android:focusable="true" + app:drawableTopCompat="@mipmap/t3" /> + android:id="@+id/read_tv_download" + style="@style/AppTheme.TextAppearance.Read.BottomMenu.Button" + android:text="下载" + android:clickable="true" + android:focusable="true" + app:drawableTopCompat="@mipmap/download" /> + android:id="@+id/read_tv_night_mode" + style="@style/AppTheme.TextAppearance.Read.BottomMenu.Button" + android:text="夜间" + android:clickable="true" + android:focusable="true" + app:drawableTopCompat="@mipmap/ao" /> + android:id="@+id/read_tv_setting" + style="@style/AppTheme.TextAppearance.Read.BottomMenu.Button" + android:text="设置" + android:clickable="true" + android:focusable="true" + app:drawableTopCompat="@mipmap/t6" /> diff --git a/app/src/main/res/layout/activity_splash.xml b/app/src/main/res/layout/activity_splash.xml index 69ccdf5..5bbb23e 100644 --- a/app/src/main/res/layout/activity_splash.xml +++ b/app/src/main/res/layout/activity_splash.xml @@ -1,6 +1,9 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 89b70de..096ca63 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -152,7 +152,7 @@ 分组管理 设置分组 搜索线程数 - 当前线程数:%1$s + 当前线程数:%1$d 最新章节:%1$s 书源:%1$s 共%2$d个源 @@ -179,6 +179,26 @@ 拷贝内容 + + 朗读 + 正在朗读 + 点击返回阅读界面 + 返回 + 重新加载 + 开始 + 停止 + 暂停 + 继续 + 上一段 + 下一段 + 定时 + 朗读暂停 + 正在朗读(还剩%d分钟) + "正在朗读(还剩%d小时%d分)" + %d分钟 + "%d小时%d分" + "计时已取消" + 常亮 1分钟 diff --git a/app/version_code.properties b/app/version_code.properties index bc20e25..de04051 100644 --- a/app/version_code.properties +++ b/app/version_code.properties @@ -1,2 +1,2 @@ -#Mon Nov 09 21:09:26 CST 2020 -VERSION_CODE=155 +#Wed Nov 11 20:37:05 CST 2020 +VERSION_CODE=154 diff --git a/gradle.properties b/gradle.properties index af6dcbe..01ac58d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,4 +16,5 @@ org.gradle.jvmargs=-Xmx1536m # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true android.useAndroidX=true -android.enableJetifier=true \ No newline at end of file +android.enableJetifier=true +android.overridePathCheck=true \ No newline at end of file diff --git a/img/1.png b/img/1.png index 4bb07bd..c9fb5a1 100644 Binary files a/img/1.png and b/img/1.png differ diff --git a/img/2.png b/img/2.png index 6840c77..4f8b9ac 100644 Binary files a/img/2.png and b/img/2.png differ diff --git a/img/3.png b/img/3.png index 228462a..ea94fa6 100644 Binary files a/img/3.png and b/img/3.png differ diff --git a/img/4.png b/img/4.png index e94c278..ea3b8f0 100644 Binary files a/img/4.png and b/img/4.png differ diff --git a/img/5.png b/img/5.png index 044b4a6..a63aba8 100644 Binary files a/img/5.png and b/img/5.png differ diff --git a/img/6.png b/img/6.png index 2e2ab34..cb62656 100644 Binary files a/img/6.png and b/img/6.png differ diff --git a/img/7.png b/img/7.png index 97c73ec..d8551fc 100644 Binary files a/img/7.png and b/img/7.png differ diff --git a/img/8.png b/img/8.png index 43990ca..c361ef4 100644 Binary files a/img/8.png and b/img/8.png differ diff --git a/img/9.png b/img/9.png index 76ac0c5..a6b20c1 100644 Binary files a/img/9.png and b/img/9.png differ