pull/159/head
yangyxd 5 years ago
commit de6500208c
  1. 14
      app/src/main/AndroidManifest.xml
  2. 127
      app/src/main/assets/help.md
  3. 14
      app/src/main/assets/updateLog.md
  4. 5
      app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
  5. 4
      app/src/main/java/io/legado/app/data/entities/Book.kt
  6. 8
      app/src/main/java/io/legado/app/help/LauncherIconHelp.kt
  7. 2
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  8. 3
      app/src/main/java/io/legado/app/lib/theme/ATH.kt
  9. 42
      app/src/main/java/io/legado/app/model/WebBook.kt
  10. 21
      app/src/main/java/io/legado/app/service/TTSReadAloudService.kt
  11. 73
      app/src/main/java/io/legado/app/service/help/ReadBook.kt
  12. 2
      app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
  13. 21
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  14. 52
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  15. 55
      app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt
  16. 4
      app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
  17. 2
      app/src/main/java/io/legado/app/ui/book/read/page/DataSource.kt
  18. 6
      app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
  19. 13
      app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt
  20. 18
      app/src/main/java/io/legado/app/ui/main/MainActivity.kt
  21. 2
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt
  22. 8
      app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt
  23. 12
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
  24. 41
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
  25. 6
      app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt
  26. 1
      app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt
  27. 8
      app/src/main/java/io/legado/app/ui/widget/dialog/TextDialog.kt
  28. 2
      app/src/main/java/io/legado/app/utils/DocumentUtils.kt
  29. 66
      app/src/main/java/io/legado/app/utils/JsoupExtensions.kt
  30. 0
      app/src/main/res/drawable-v24/ic_launcher_4.xml
  31. 4
      app/src/main/res/drawable/ic_bottom_books.xml
  32. 17
      app/src/main/res/drawable/ic_bottom_books_e.xml
  33. 45
      app/src/main/res/drawable/ic_bottom_books_s.xml
  34. 5
      app/src/main/res/drawable/ic_bottom_explore.xml
  35. 5
      app/src/main/res/drawable/ic_bottom_explore_black.xml
  36. 5
      app/src/main/res/drawable/ic_bottom_explore_black_e.xml
  37. 9
      app/src/main/res/drawable/ic_bottom_explore_black_s.xml
  38. 12
      app/src/main/res/drawable/ic_bottom_explore_e.xml
  39. 24
      app/src/main/res/drawable/ic_bottom_explore_s.xml
  40. 4
      app/src/main/res/drawable/ic_bottom_person.xml
  41. 15
      app/src/main/res/drawable/ic_bottom_person_e.xml
  42. 48
      app/src/main/res/drawable/ic_bottom_person_s.xml
  43. 4
      app/src/main/res/drawable/ic_bottom_rss_feed.xml
  44. 19
      app/src/main/res/drawable/ic_bottom_rss_feed_e.xml
  45. 26
      app/src/main/res/drawable/ic_bottom_rss_feed_s.xml
  46. 14
      app/src/main/res/drawable/ic_launch.xml
  47. 18
      app/src/main/res/drawable/ic_launcher.xml
  48. 2
      app/src/main/res/layout/activity_main.xml
  49. 6
      app/src/main/res/layout/dialog_read_aloud.xml
  50. 4
      app/src/main/res/layout/dialog_recycler_view.xml
  51. 13
      app/src/main/res/layout/item_bookshelf_grid.xml
  52. 28
      app/src/main/res/layout/popup_action_menu.xml
  53. 24
      app/src/main/res/menu/main_bnv.xml
  54. 12
      app/src/main/res/menu/main_my.xml
  55. 2
      app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  56. 5
      app/src/main/res/mipmap-anydpi-v26/launcher4.xml
  57. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.png
  58. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.png
  59. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.png
  60. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  61. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  62. 2
      app/src/main/res/values/array_values.xml
  63. 1
      app/src/main/res/values/arrays.xml
  64. 1
      app/src/main/res/values/dimens.xml
  65. 16
      app/src/main/res/values/strings.xml
  66. 2
      build.gradle
  67. 2
      gradle.properties

@ -79,6 +79,20 @@
android:resource="@xml/shortcuts" android:resource="@xml/shortcuts"
android:launchMode="singleTask" /> android:launchMode="singleTask" />
</activity> </activity>
<!--图标4-->
<activity
android:name=".ui.welcome.Launcher4"
android:icon="@mipmap/launcher4"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts"
android:launchMode="singleTask" />
</activity>
<!--主界面--> <!--主界面-->
<activity <activity
android:name=".ui.main.MainActivity" android:name=".ui.main.MainActivity"

@ -0,0 +1,127 @@
1.为什么第一次安装好之后什么东西都没有?
* 因为阅读只是一个转码工具,不提供内容,第一次安装app,需要自己手动导入书源,可以从QQ群、公众号“开源阅读软件”、酷安评论里获取由书友制作分享的书源。
2.如何导入本地书源文件?
* 下载群文件里的书源文件(书源格式后缀有txt、json,其中json文件某些情况下无法导入,需要修改后缀为txt格式才可导入);
* 打开阅读软件;
* 我的 - 点击“书源管理”;
* 点击右上角选择“本地导入”;
* 左下角选择书源文件所在的路径;
* 点击书源文件导入;
* 导入后返回书源管理界面;
* 新版qq下载路径:Android/data/com.tencent.mobileqq/Tencent/QQfile_recv/
3.如何新建大佬发的单独书源?
* 复制书源代码;
* 打开阅读软件;
* 我的 - 点击“书源管理”;
* 右上角选择“新建书源”;
* 进入新建书源后点击右上角“粘贴源”;
* 粘贴书源完成后点击上方保存;
* 本次新建单独书源操作完成。
* 注:如果书源有错误或者复制不全会显示格式错误,请重新复制。
4.为什么导入2.0书源后看不了书?
* 2.0部分书源并不适用3.0,建议导入后进行筛选。
5.阅读2.0数据如何导入阅读3.0?
* 先对阅读2.0的数据进行备份,然后进入阅读3.0,点击“我的”,选择“备份与恢复”,再点击“导入旧版本数据”。
6.如何给朋友分享我的书源?
* 打开阅读软件;
* 点击备份;
* 打开手机自带的文件管理;
* 手机自带内存根目录找到YueDu3.0文件夹;
* 找到myBookSource.json长按选择分享;
* 选择微信分享或者QQ分享;
* 选择你要分享的好友点击发送;
* 好友接收后在手机自带内存根目录找到myBookSource.json文件(QQ在tencent--QQfile_recv微信在Tencent--MicroMsg--Download);
* 复制该文件到手机自带内存根目录找到YueDu3.0文件夹(如已有该文件请先删除该文件或者备份到其他地方再复制到文件夹);
* 打开阅读软件点击恢复。
* 注:备份路径如已修改过请在修改后的路径下查找书源文件。
7.目前阅读支持哪些格式的本地书籍?
* 目前支持TXT、EPUB格式(只支持显示EPUB里的文本内容,还不支持显示图片)。
8.如何刷新书架?
* 在书架界面下拉即可刷新。
9.书架界面书籍右上角的红色或者灰色背景小数字代表什么?
* 红色代表书籍有更新,灰色代表无更新,数字代表未读章节。
10.如何查看书籍详情?
* 长按书籍。
11.如何对书架上的书进行删除、切换书架的操作?
* 书籍详情页操作即可。
12.如何禁止或允许某本书更新?
* 书籍详情页,点击右上角 - “允许更新”。
13.如何更换小说封面、名字、作者或简介?
* 书籍详情页,点击右上角修改按钮。
14.怎么使用自定义字体?
* 阅读界面 - 字体-点击右上角选择字体文件路径。
15.目前支持哪些格式的字体文件?
* 目前支持ttf、otf格式。
16.书籍经常“正在加载中”怎么办?
* 在线书籍出现这个问题通常是由于源质量不好或不兼容引起的,可以换其它源多试试;本地书籍出现这个问题大概率是目录规则问题,手动切换规则可以解决。
17.书籍内容只有标题,正文内容是路径怎么办?
* 通常是缓存路径引起的,更换缓存路径即可。
18.效验书源显示失效就说明书源不能用了吗?
* 效验书源只是测试书源,可以做个参考,失效了不代表书源不能用了。
19.发现和正版书源能不能使用?
* 发现和正版书源只能用来找书,看排行榜,不能用来看书,如需看书请切换书源。
20.替换净化是什么?
* 替换净化可以去除书籍内容里的广告、错别字、屏蔽词等。
21.如何自己填写净化替换规则?
* 第一行:替换规则名称 - 根据自己需求对替换净化规则进行命名;
* 第二行:分组 - 净化规则的分组组别;
* 第三行:替换规则 - 填写需要被替换的内容;
* 第四行:替换为 - 填写想替换成的内容(如不填则默认表示删除第二行里填写的内容);
* 第五行:替换范围,选填书名或者源名 - 填写此替换净化规则需要对哪本书籍或者哪个书源生效(如不填则对所有书籍和书源生效)。
* 注:如常规去除方法去除不掉,则需要勾选“使用正则表达式”,同时第二行里的替换规则也需要按照正则表达式来填写(正则表达式填写方法可自行百度学习)。
22.如何听书?
* 可以使用手机自带的朗读引擎,也可使用第三方如谷歌、小米等朗读引擎。
* 【具体操作:安装-系统设置-其他高级设置-辅助功能-TTS输出-选择安装的朗读引擎(不同品牌手机的操作方法及步骤也不同,视情况而定)。】
23.如何设置屏幕方向、屏幕显示时长、显示/隐藏状态栏、显示/隐藏导航栏、音量键翻页、长按选择文本、点击总是翻下一页、自定义翻页案件?
* 阅读界面,设置(可上划,下面还有其他设置)。
24.搜索的时候感觉手机卡顿,如何解决?
* 我的 - 其他设置 - “更新和搜索线程数”调低。
25.更新前有什么注意事项?
* 要做好备份。
26.看书时如遇到“目录为空”、“加载失败”和长串英文等情况怎么办?
* 一般是书源问题,切换书源即可。
27.为什么书源这么多,发现里却只有一点点?
* 书源想要在发现界面里显示需要在书源里添加发现规则,并不是所有书源都有发现规则。
28.云备份在哪?
* 我的 - 备份与恢复 - WebDav设置。
29.如何操作进行云备份?
* 侧栏设置,WebDav设置;
* 正确填写WebDAV 服务器地址、WebDAV 账号、WebDAV 密码;(要获得这三项的信息,需要注册一个坚果云账号,如果直接在手机上注册,坚果云会让你下载app,过程比较麻烦,为了一步到位,最好是在电脑上打开这个注册链接:https://www.jianguoyun.com/d/signup;注册后,进入坚果云;点击右上角账户名处选择 “账户信息”,然后选择“安全选项”;在“安全选项” 中找到“第三方应用管理”,并选择“添加应用”,输入名称如“阅读”后,会生成密码,选择完成;其中https://dav.jianguoyun.com/dav/就是填入“WebDAV 服务器地址”的内容,“使用情况”后面的邮箱地址就是你的“WebDAV 账号”,点击显示密码后得到的密码就是你的“WebDAV 密码”。)
* 无需操作,APP默认每天自动云备份一次。
30.关于云备份的相关说明
* 在正确设置好云备份的情况下,APP默认每天自动云备份一次,当日多次手动云备份会对当日的旧云备份文件进行覆盖,并不会覆盖之前及之后不同日期的备份文件,每天所自动云备份的文件会按照日期进行命名。
31.本地备份和云备份都能备份哪些东西?
* 书架、看书进度、搜索记录、书源、替换、APP设置等都会备份,基本涵盖所有内容。
32.出现某些未知bug怎么办?
* 清除软件数据试试看,不行再进行反馈。

@ -1,11 +1,23 @@
## 更新日志 ## 更新日志
* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
* 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 * 请关注[开源阅读]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。
**2020/03/15**
* 弄了个企业公众号[开源阅读](),后面弄好后会把原来的开源阅读软件迁移过来
* 修复滚动模式切换章节位置不归0的bug
* 修复文字选择更多菜单在部分手机上报错的bug
**2020/03/14**
* 加载正文无书源时自动换源
**2020/03/14**
* 修改导航栏图标
**2020/03/13** **2020/03/13**
* 更改书架控件,ViewPager2替换回2.0使用的ViewPager,解决下拉不流畅问题 * 更改书架控件,ViewPager2替换回2.0使用的ViewPager,解决下拉不流畅问题
* 修复点击作者搜索后,打开的详情页还是原来的书籍的bug * 修复点击作者搜索后,打开的详情页还是原来的书籍的bug
* 修改朗读菜单 * 修改朗读菜单
* 优化rss朗读
**2020/03/12** **2020/03/12**
* 导入本地添加需要权限模式 * 导入本地添加需要权限模式

@ -50,9 +50,12 @@ interface BookSourceDao {
@get:Query("select * from book_sources order by customOrder asc") @get:Query("select * from book_sources order by customOrder asc")
val all: List<BookSource> val all: List<BookSource>
@get:Query("select * from book_sources where enabled = 1 order by customOrder asc") @get:Query("select * from book_sources where enabled = 1 order by customOrder")
val allEnabled: List<BookSource> val allEnabled: List<BookSource>
@get:Query("select * from book_sources where enabled = 1 and bookSourceType = 0 order by customOrder")
val allTextEnabled: List<BookSource>
@Query("select * from book_sources where bookSourceUrl = :key") @Query("select * from book_sources where bookSourceUrl = :key")
fun getBookSource(key: String): BookSource? fun getBookSource(key: String): BookSource?

@ -31,8 +31,8 @@ data class Book(
var intro: String? = null, // 简介内容(书源获取) var intro: String? = null, // 简介内容(书源获取)
var customIntro: String? = null, // 简介内容(用户修改) var customIntro: String? = null, // 简介内容(用户修改)
var charset: String? = null, // 自定义字符集名称(仅适用于本地书籍) var charset: String? = null, // 自定义字符集名称(仅适用于本地书籍)
var type: Int = 0, // @BookType var type: Int = 0, // 0:text 1:audio
var group: Int = 0, // 自定义分组索引号 var group: Int = 1, // 自定义分组索引号
var latestChapterTitle: String? = null, // 最新章节标题 var latestChapterTitle: String? = null, // 最新章节标题
var latestChapterTime: Long = System.currentTimeMillis(), // 最新章节标题更新时间 var latestChapterTime: Long = System.currentTimeMillis(), // 最新章节标题更新时间
var lastCheckTime: Long = System.currentTimeMillis(), // 最近一次更新书籍信息的时间 var lastCheckTime: Long = System.currentTimeMillis(), // 最近一次更新书籍信息的时间

@ -5,10 +5,7 @@ import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import io.legado.app.App import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.ui.welcome.Launcher1 import io.legado.app.ui.welcome.*
import io.legado.app.ui.welcome.Launcher2
import io.legado.app.ui.welcome.Launcher3
import io.legado.app.ui.welcome.WelcomeActivity
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
/** /**
@ -20,7 +17,8 @@ object LauncherIconHelp {
private val componentNames = arrayListOf( private val componentNames = arrayListOf(
ComponentName(App.INSTANCE, Launcher1::class.java.name), ComponentName(App.INSTANCE, Launcher1::class.java.name),
ComponentName(App.INSTANCE, Launcher2::class.java.name), ComponentName(App.INSTANCE, Launcher2::class.java.name),
ComponentName(App.INSTANCE, Launcher3::class.java.name) ComponentName(App.INSTANCE, Launcher3::class.java.name),
ComponentName(App.INSTANCE, Launcher4::class.java.name)
) )
fun changeIcon(icon: String?) { fun changeIcon(icon: String?) {

@ -128,7 +128,7 @@ object Restore {
bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2) bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2)
} }
ChapterProvider.upStyle() ChapterProvider.upStyle()
ReadBook.loadContent() ReadBook.loadContent(resetPageOffset = false)
} }
withContext(Main) { withContext(Main) {
if (AppConfig.isNightTheme && AppCompatDelegate.getDefaultNightMode() != AppCompatDelegate.MODE_NIGHT_YES) { if (AppConfig.isNightTheme && AppCompatDelegate.getDefaultNightMode() != AppCompatDelegate.MODE_NIGHT_YES) {

@ -199,13 +199,12 @@ object ATH {
fun applyBottomNavigationColor(bottomBar: BottomNavigationView?) { fun applyBottomNavigationColor(bottomBar: BottomNavigationView?) {
bottomBar?.apply { bottomBar?.apply {
setBackgroundColor(ThemeStore.backgroundColor(context)) setBackgroundColor(ThemeStore.bottomBackground(context))
val colorStateList = Selector.colorBuild() val colorStateList = Selector.colorBuild()
.setDefaultColor(context.getCompatColor(R.color.btn_bg_press_tp)) .setDefaultColor(context.getCompatColor(R.color.btn_bg_press_tp))
.setSelectedColor(ThemeStore.accentColor(bottom_navigation_view.context)).create() .setSelectedColor(ThemeStore.accentColor(bottom_navigation_view.context)).create()
itemIconTintList = colorStateList itemIconTintList = colorStateList
itemTextColor = colorStateList itemTextColor = colorStateList
setBackgroundColor(ThemeStore.bottomBackground(context))
} }
} }

@ -29,26 +29,34 @@ class WebBook(val bookSource: BookSource) {
context: CoroutineContext = Dispatchers.IO context: CoroutineContext = Dispatchers.IO
): Coroutine<List<SearchBook>> { ): Coroutine<List<SearchBook>> {
return Coroutine.async(scope, context) { return Coroutine.async(scope, context) {
bookSource.searchUrl?.let { searchUrl -> searchBookSuspend(key, page)
val analyzeUrl = AnalyzeUrl(
ruleUrl = searchUrl,
key = key,
page = page,
baseUrl = sourceUrl,
headerMapF = bookSource.getHeaderMap()
)
val res = analyzeUrl.getResponseAwait(bookSource.bookSourceUrl)
BookList.analyzeBookList(
res.body,
bookSource,
analyzeUrl,
res.url,
true
)
} ?: arrayListOf()
} }
} }
suspend fun searchBookSuspend(
key: String,
page: Int? = 1
): ArrayList<SearchBook> {
bookSource.searchUrl?.let { searchUrl ->
val analyzeUrl = AnalyzeUrl(
ruleUrl = searchUrl,
key = key,
page = page,
baseUrl = sourceUrl,
headerMapF = bookSource.getHeaderMap()
)
val res = analyzeUrl.getResponseAwait(bookSource.bookSourceUrl)
return BookList.analyzeBookList(
res.body,
bookSource,
analyzeUrl,
res.url,
true
)
}
return arrayListOf()
}
/** /**
* 发现 * 发现
*/ */

@ -29,7 +29,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
} }
} }
private var ttsIsSuccess: Boolean = false private var ttsInitFinish = false
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -42,22 +42,23 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
clearTTS() clearTTS()
} }
@Synchronized
override fun onInit(status: Int) { override fun onInit(status: Int) {
launch { if (status == TextToSpeech.SUCCESS) {
if (status == TextToSpeech.SUCCESS) { textToSpeech?.language = Locale.CHINA
textToSpeech?.language = Locale.CHINA textToSpeech?.setOnUtteranceProgressListener(TTSUtteranceListener())
textToSpeech?.setOnUtteranceProgressListener(TTSUtteranceListener()) ttsInitFinish = true
ttsIsSuccess = true play()
play() } else {
} else { launch {
toast(R.string.tts_init_failed) toast(R.string.tts_init_failed)
} }
} }
} }
@Suppress("DEPRECATION") @Synchronized
override fun play() { override fun play() {
if (contentList.isEmpty() || !ttsIsSuccess) { if (contentList.isEmpty() || !ttsInitFinish) {
return return
} }
if (requestFocus()) { if (requestFocus()) {

@ -21,7 +21,6 @@ import org.jetbrains.anko.toast
object ReadBook { object ReadBook {
var titleDate = MutableLiveData<String>() var titleDate = MutableLiveData<String>()
var book: Book? = null var book: Book? = null
var inBookshelf = false var inBookshelf = false
@ -34,9 +33,10 @@ object ReadBook {
var curTextChapter: TextChapter? = null var curTextChapter: TextChapter? = null
var nextTextChapter: TextChapter? = null var nextTextChapter: TextChapter? = null
var webBook: WebBook? = null var webBook: WebBook? = null
var msg: String? = null
private val loadingChapters = arrayListOf<Int>() private val loadingChapters = arrayListOf<Int>()
fun resetData(book: Book) { fun resetData(book: Book, noSource: (name: String, author: String) -> Unit) {
this.book = book this.book = book
titleDate.postValue(book.name) titleDate.postValue(book.name)
durChapterIndex = book.durChapterIndex durChapterIndex = book.durChapterIndex
@ -46,12 +46,22 @@ object ReadBook {
prevTextChapter = null prevTextChapter = null
curTextChapter = null curTextChapter = null
nextTextChapter = null nextTextChapter = null
upWebBook(book.origin) upWebBook(book, noSource)
} }
fun upWebBook(origin: String) { fun upWebBook(book: Book?, noSource: (name: String, author: String) -> Unit) {
val bookSource = App.db.bookSourceDao().getBookSource(origin) book ?: return
webBook = if (bookSource != null) WebBook(bookSource) else null if (book.origin == BookType.local) {
webBook = null
} else {
val bookSource = App.db.bookSourceDao().getBookSource(book.origin)
if (bookSource != null) {
webBook = WebBook(bookSource)
} else {
webBook = null
noSource.invoke(book.name, book.author)
}
}
} }
fun moveToNextPage() { fun moveToNextPage() {
@ -69,11 +79,11 @@ object ReadBook {
nextTextChapter = null nextTextChapter = null
book?.let { book?.let {
if (curTextChapter == null) { if (curTextChapter == null) {
loadContent(durChapterIndex, upContent) loadContent(durChapterIndex, upContent, false)
} else if (upContent) { } else if (upContent) {
callBack?.upContent() callBack?.upContent()
} }
loadContent(durChapterIndex.plus(1), upContent) loadContent(durChapterIndex.plus(1), upContent, false)
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
for (i in 2..10) { for (i in 2..10) {
delay(100) delay(100)
@ -99,11 +109,11 @@ object ReadBook {
prevTextChapter = null prevTextChapter = null
book?.let { book?.let {
if (curTextChapter == null) { if (curTextChapter == null) {
loadContent(durChapterIndex, upContent) loadContent(durChapterIndex, upContent, false)
} else if (upContent) { } else if (upContent) {
callBack?.upContent() callBack?.upContent()
} }
loadContent(durChapterIndex.minus(1), upContent) loadContent(durChapterIndex.minus(1), upContent, false)
GlobalScope.launch(Dispatchers.IO) { GlobalScope.launch(Dispatchers.IO) {
for (i in -5..-2) { for (i in -5..-2) {
delay(100) delay(100)
@ -184,21 +194,21 @@ object ReadBook {
/** /**
* 加载章节内容 * 加载章节内容
*/ */
fun loadContent() { fun loadContent(resetPageOffset: Boolean) {
loadContent(durChapterIndex) loadContent(durChapterIndex, resetPageOffset = resetPageOffset)
loadContent(durChapterIndex + 1) loadContent(durChapterIndex + 1, resetPageOffset = resetPageOffset)
loadContent(durChapterIndex - 1) loadContent(durChapterIndex - 1, resetPageOffset = resetPageOffset)
} }
fun loadContent(index: Int, upContent: Boolean = true) { fun loadContent(index: Int, upContent: Boolean = true, resetPageOffset: Boolean) {
book?.let { book -> book?.let { book ->
if (addLoading(index)) { if (addLoading(index)) {
Coroutine.async { Coroutine.async {
App.db.bookChapterDao().getChapter(book.bookUrl, index)?.let { chapter -> App.db.bookChapterDao().getChapter(book.bookUrl, index)?.let { chapter ->
BookHelp.getContent(book, chapter)?.let { BookHelp.getContent(book, chapter)?.let {
contentLoadFinish(chapter, it, upContent) contentLoadFinish(chapter, it, upContent, resetPageOffset)
removeLoading(chapter.index) removeLoading(chapter.index)
} ?: download(chapter) } ?: download(chapter, resetPageOffset = resetPageOffset)
} ?: removeLoading(index) } ?: removeLoading(index)
}.onError { }.onError {
removeLoading(index) removeLoading(index)
@ -216,7 +226,7 @@ object ReadBook {
if (BookHelp.hasContent(book, chapter)) { if (BookHelp.hasContent(book, chapter)) {
removeLoading(chapter.index) removeLoading(chapter.index)
} else { } else {
download(chapter) download(chapter, false)
} }
} ?: removeLoading(index) } ?: removeLoading(index)
}.onError { }.onError {
@ -226,20 +236,28 @@ object ReadBook {
} }
} }
private fun download(chapter: BookChapter) { private fun download(chapter: BookChapter, resetPageOffset: Boolean) {
book?.let { book -> book?.let { book ->
webBook?.getContent(book, chapter) webBook?.getContent(book, chapter)
?.onSuccess(Dispatchers.IO) { content -> ?.onSuccess(Dispatchers.IO) { content ->
if (content.isNullOrEmpty()) { if (content.isNullOrEmpty()) {
contentLoadFinish(chapter, App.INSTANCE.getString(R.string.content_empty)) contentLoadFinish(
chapter,
App.INSTANCE.getString(R.string.content_empty),
resetPageOffset = resetPageOffset
)
removeLoading(chapter.index) removeLoading(chapter.index)
} else { } else {
BookHelp.saveContent(book, chapter, content) BookHelp.saveContent(book, chapter, content)
contentLoadFinish(chapter, content) contentLoadFinish(chapter, content, resetPageOffset = resetPageOffset)
removeLoading(chapter.index) removeLoading(chapter.index)
} }
}?.onError { }?.onError {
contentLoadFinish(chapter, it.localizedMessage ?: "未知错误") contentLoadFinish(
chapter,
it.localizedMessage ?: "未知错误",
resetPageOffset = resetPageOffset
)
removeLoading(chapter.index) removeLoading(chapter.index)
} }
} }
@ -265,7 +283,8 @@ object ReadBook {
private fun contentLoadFinish( private fun contentLoadFinish(
chapter: BookChapter, chapter: BookChapter,
content: String, content: String,
upContent: Boolean = true upContent: Boolean = true,
resetPageOffset: Boolean
) { ) {
Coroutine.async { Coroutine.async {
if (chapter.index in durChapterIndex - 1..durChapterIndex + 1) { if (chapter.index in durChapterIndex - 1..durChapterIndex + 1) {
@ -279,18 +298,18 @@ object ReadBook {
when (chapter.index) { when (chapter.index) {
durChapterIndex -> { durChapterIndex -> {
curTextChapter = ChapterProvider.getTextChapter(chapter, c, chapterSize) curTextChapter = ChapterProvider.getTextChapter(chapter, c, chapterSize)
if (upContent) callBack?.upContent() if (upContent) callBack?.upContent(resetPageOffset = resetPageOffset)
callBack?.upView() callBack?.upView()
curPageChanged() curPageChanged()
callBack?.contentLoadFinish() callBack?.contentLoadFinish()
} }
durChapterIndex - 1 -> { durChapterIndex - 1 -> {
prevTextChapter = ChapterProvider.getTextChapter(chapter, c, chapterSize) prevTextChapter = ChapterProvider.getTextChapter(chapter, c, chapterSize)
if (upContent) callBack?.upContent(-1) if (upContent) callBack?.upContent(-1, resetPageOffset)
} }
durChapterIndex + 1 -> { durChapterIndex + 1 -> {
nextTextChapter = ChapterProvider.getTextChapter(chapter, c, chapterSize) nextTextChapter = ChapterProvider.getTextChapter(chapter, c, chapterSize)
if (upContent) callBack?.upContent(1) if (upContent) callBack?.upContent(1, resetPageOffset)
} }
} }
} }
@ -316,7 +335,7 @@ object ReadBook {
} }
interface CallBack { interface CallBack {
fun upContent(relativePosition: Int = 0) fun upContent(relativePosition: Int = 0, resetPageOffset: Boolean = true)
fun upView() fun upView()
fun upPageProgress() fun upPageProgress()
fun contentLoadFinish() fun contentLoadFinish()

@ -33,6 +33,7 @@ import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel import io.legado.app.utils.getViewModel
import io.legado.app.utils.requestInputMethod import io.legado.app.utils.requestInputMethod
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.dialog_edit_text.view.* import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.* import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_group_manage.view.* import kotlinx.android.synthetic.main.item_group_manage.view.*
@ -74,6 +75,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
recycler_view.addItemDecoration(VerticalDivider(requireContext())) recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter recycler_view.adapter = adapter
tv_ok.setTextColor(requireContext().accentColor) tv_ok.setTextColor(requireContext().accentColor)
tv_ok.visible()
tv_ok.onClick { dismiss() } tv_ok.onClick { dismiss() }
App.db.bookGroupDao().liveDataAll().observe(viewLifecycleOwner, Observer { App.db.bookGroupDao().liveDataAll().observe(viewLifecycleOwner, Observer {
val diffResult = val diffResult =

@ -25,6 +25,7 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.ReadBookConfig import io.legado.app.help.ReadBookConfig
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.storage.Backup
import io.legado.app.help.storage.SyncBookProgress import io.legado.app.help.storage.SyncBookProgress
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.noButton import io.legado.app.lib.dialogs.noButton
@ -115,7 +116,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
ReadBook.loadContent() ReadBook.loadContent(resetPageOffset = false)
} }
override fun onResume() { override fun onResume() {
@ -402,11 +403,10 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
*/ */
override fun showTextActionMenu() { override fun showTextActionMenu() {
textActionMenu ?: let { textActionMenu ?: let {
textActionMenu = TextActionMenu(this, this).apply { textActionMenu = TextActionMenu(this, this)
contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
}
} }
textActionMenu?.let { popup -> textActionMenu?.let { popup ->
popup.contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
val popupHeight = popup.contentView.measuredHeight val popupHeight = popup.contentView.measuredHeight
val x = text_menu_position.x.toInt() val x = text_menu_position.x.toInt()
var y = text_menu_position.y.toInt() - popupHeight var y = text_menu_position.y.toInt() - popupHeight
@ -481,9 +481,9 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
/** /**
* 更新内容 * 更新内容
*/ */
override fun upContent(relativePosition: Int) { override fun upContent(relativePosition: Int, resetPageOffset: Boolean) {
launch { launch {
page_view.upContent(relativePosition) page_view.upContent(relativePosition, resetPageOffset)
} }
} }
@ -579,7 +579,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
override fun onReplaceRuleSave() { override fun onReplaceRuleSave() {
Coroutine.async { Coroutine.async {
BookHelp.upReplaceRules() BookHelp.upReplaceRules()
ReadBook.loadContent() ReadBook.loadContent(resetPageOffset = false)
} }
} }
@ -684,6 +684,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
page_view.onDestroy() page_view.onDestroy()
if (!BuildConfig.DEBUG) { if (!BuildConfig.DEBUG) {
SyncBookProgress.uploadBookProgress() SyncBookProgress.uploadBookProgress()
Backup.autoBack(this)
} }
} }
@ -707,9 +708,9 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
page_view.upBg() page_view.upBg()
page_view.upStyle() page_view.upStyle()
if (it) { if (it) {
ReadBook.loadContent() ReadBook.loadContent(resetPageOffset = false)
} else { } else {
page_view.upContent() page_view.upContent(resetPageOffset = false)
} }
} }
observeEvent<Int>(EventBus.ALOUD_STATE) { observeEvent<Int>(EventBus.ALOUD_STATE) {
@ -718,7 +719,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
val page = textChapter.page(ReadBook.durPageIndex) val page = textChapter.page(ReadBook.durPageIndex)
if (page != null) { if (page != null) {
page.removePageAloudSpan() page.removePageAloudSpan()
page_view.upContent() page_view.upContent(resetPageOffset = false)
} }
} }
} }

@ -39,7 +39,9 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
private fun initBook(book: Book) { private fun initBook(book: Book) {
if (ReadBook.book?.bookUrl != book.bookUrl) { if (ReadBook.book?.bookUrl != book.bookUrl) {
ReadBook.resetData(book) ReadBook.resetData(book) { name, author ->
autoChangeSource(name, author)
}
isInitFinish = true isInitFinish = true
ReadBook.chapterSize = App.db.bookChapterDao().getChapterCount(book.bookUrl) ReadBook.chapterSize = App.db.bookChapterDao().getChapterCount(book.bookUrl)
if (ReadBook.chapterSize == 0) { if (ReadBook.chapterSize == 0) {
@ -52,7 +54,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
if (ReadBook.durChapterIndex > ReadBook.chapterSize - 1) { if (ReadBook.durChapterIndex > ReadBook.chapterSize - 1) {
ReadBook.durChapterIndex = ReadBook.chapterSize - 1 ReadBook.durChapterIndex = ReadBook.chapterSize - 1
} }
ReadBook.loadContent() ReadBook.loadContent(resetPageOffset = true)
} }
if (ReadBook.inBookshelf) { if (ReadBook.inBookshelf) {
ReadBook.saveRead() ReadBook.saveRead()
@ -60,7 +62,9 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
} else { } else {
isInitFinish = true isInitFinish = true
ReadBook.titleDate.postValue(book.name) ReadBook.titleDate.postValue(book.name)
ReadBook.upWebBook(book.origin) ReadBook.upWebBook(book) { name, author ->
autoChangeSource(name, author)
}
ReadBook.chapterSize = App.db.bookChapterDao().getChapterCount(book.bookUrl) ReadBook.chapterSize = App.db.bookChapterDao().getChapterCount(book.bookUrl)
if (ReadBook.chapterSize == 0) { if (ReadBook.chapterSize == 0) {
if (book.tocUrl.isEmpty()) { if (book.tocUrl.isEmpty()) {
@ -70,7 +74,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
} }
} else { } else {
if (ReadBook.curTextChapter != null) { if (ReadBook.curTextChapter != null) {
ReadBook.callBack?.upContent() ReadBook.callBack?.upContent(resetPageOffset = false)
} }
} }
} }
@ -103,7 +107,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
App.db.bookChapterDao().insert(*it.toTypedArray()) App.db.bookChapterDao().insert(*it.toTypedArray())
App.db.bookDao().update(book) App.db.bookDao().update(book)
ReadBook.chapterSize = it.size ReadBook.chapterSize = it.size
ReadBook.loadContent() ReadBook.loadContent(resetPageOffset = true)
} }
} else { } else {
ReadBook.webBook?.getChapterList(book, this) ReadBook.webBook?.getChapterList(book, this)
@ -113,7 +117,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
App.db.bookChapterDao().insert(*cList.toTypedArray()) App.db.bookChapterDao().insert(*cList.toTypedArray())
App.db.bookDao().update(book) App.db.bookDao().update(book)
ReadBook.chapterSize = cList.size ReadBook.chapterSize = cList.size
ReadBook.loadContent() ReadBook.loadContent(resetPageOffset = true)
} else { } else {
changeDruChapterIndex(cList) changeDruChapterIndex(cList)
} }
@ -122,7 +126,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
} }
}?.onError { }?.onError {
toast(R.string.error_load_toc) toast(R.string.error_load_toc)
} ?: autoChangeSource() } ?: autoChangeSource(book.name, book.author)
} }
} }
} }
@ -151,8 +155,28 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
} }
} }
private fun autoChangeSource() { private fun autoChangeSource(name: String, author: String) {
execute {
App.db.bookSourceDao().allTextEnabled.forEach { source ->
try {
val searchBooks = WebBook(source).searchBookSuspend(name)
searchBooks.getOrNull(0)?.let {
if (it.name == name && (it.author == author || author == "")) {
changeTo(it.toBook())
return@forEach
}
}
} catch (e: Exception) {
//nothing
}
}
}.onStart {
ReadBook.msg = "正在自动换源"
ReadBook.callBack?.upContent()
}.onFinally {
ReadBook.msg = null
ReadBook.callBack?.upContent()
}
} }
private fun upChangeDurChapterIndex(book: Book, chapters: List<BookChapter>) { private fun upChangeDurChapterIndex(book: Book, chapters: List<BookChapter>) {
@ -167,7 +191,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
App.db.bookDao().update(book) App.db.bookDao().update(book)
App.db.bookChapterDao().insert(*chapters.toTypedArray()) App.db.bookChapterDao().insert(*chapters.toTypedArray())
ReadBook.chapterSize = chapters.size ReadBook.chapterSize = chapters.size
ReadBook.loadContent() ReadBook.loadContent(resetPageOffset = true)
} }
} }
@ -181,7 +205,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
ReadBook.durPageIndex = pageIndex ReadBook.durPageIndex = pageIndex
} }
ReadBook.saveRead() ReadBook.saveRead()
ReadBook.loadContent() ReadBook.loadContent(resetPageOffset = true)
} }
fun removeFromBookshelf(success: (() -> Unit)?) { fun removeFromBookshelf(success: (() -> Unit)?) {
@ -208,9 +232,9 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
execute { execute {
App.db.bookChapterDao().getChapter(book.bookUrl, ReadBook.durChapterIndex) App.db.bookChapterDao().getChapter(book.bookUrl, ReadBook.durChapterIndex)
?.let { chapter -> ?.let { chapter ->
BookHelp.delContent(book, chapter) BookHelp.delContent(book, chapter)
ReadBook.loadContent(ReadBook.durChapterIndex) ReadBook.loadContent(ReadBook.durChapterIndex, resetPageOffset = false)
} }
} }
} }

@ -15,13 +15,11 @@ import androidx.annotation.RequiresApi
import androidx.appcompat.view.SupportMenuInflater import androidx.appcompat.view.SupportMenuInflater
import androidx.appcompat.view.menu.MenuBuilder import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.view.menu.MenuItemImpl import androidx.appcompat.view.menu.MenuItemImpl
import androidx.appcompat.widget.PopupMenu import androidx.core.view.isVisible
import androidx.core.view.size
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.utils.gone
import io.legado.app.utils.isAbsUrl import io.legado.app.utils.isAbsUrl
import io.legado.app.utils.sendToClip import io.legado.app.utils.sendToClip
import io.legado.app.utils.visible import io.legado.app.utils.visible
@ -35,6 +33,10 @@ import org.jetbrains.anko.toast
class TextActionMenu(private val context: Context, private val callBack: CallBack) : class TextActionMenu(private val context: Context, private val callBack: CallBack) :
PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) { PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) {
private val adapter = Adapter(context)
private val menu = MenuBuilder(context)
private val moreMenu = MenuBuilder(context)
init { init {
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
contentView = LayoutInflater.from(context).inflate(R.layout.popup_action_menu, null) contentView = LayoutInflater.from(context).inflate(R.layout.popup_action_menu, null)
@ -44,33 +46,38 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
isFocusable = false isFocusable = false
initRecyclerView() initRecyclerView()
setOnDismissListener {
contentView.apply {
iv_menu_more.setImageResource(R.drawable.ic_more_vert)
recycler_view_more.gone()
adapter.setItems(menu.visibleItems)
recycler_view.visible()
}
}
} }
private fun initRecyclerView() = with(contentView) { private fun initRecyclerView() = with(contentView) {
val adapter = Adapter(context)
recycler_view.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
recycler_view.adapter = adapter recycler_view.adapter = adapter
val menu = MenuBuilder(context) recycler_view_more.adapter = adapter
SupportMenuInflater(context).inflate(R.menu.content_select_action, menu) SupportMenuInflater(context).inflate(R.menu.content_select_action, menu)
adapter.setItems(menu.visibleItems) adapter.setItems(menu.visibleItems)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val popupMenu = PopupMenu(context, iv_menu_more) onInitializeMenu(moreMenu)
onInitializeMenu(popupMenu.menu) }
if (popupMenu.menu.size > 0) { if (moreMenu.size() > 0) {
iv_menu_more.visible() iv_menu_more.visible()
popupMenu.setOnMenuItemClickListener { item -> }
item.intent?.let { iv_menu_more.onClick {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (recycler_view.isVisible) {
it.putExtra(Intent.EXTRA_PROCESS_TEXT, callBack.selectedText) iv_menu_more.setImageResource(R.drawable.ic_arrow_back)
context.startActivity(it) adapter.setItems(moreMenu.visibleItems)
} recycler_view.gone()
} recycler_view_more.visible()
this@TextActionMenu.dismiss() } else {
true iv_menu_more.setImageResource(R.drawable.ic_more_vert)
} recycler_view_more.gone()
} adapter.setItems(menu.visibleItems)
iv_menu_more.onClick { recycler_view.visible()
popupMenu.show()
} }
} }
} }

@ -108,8 +108,10 @@ class ContentView(context: Context) : FrameLayout(context) {
} }
} }
fun setContent(textPage: TextPage) { fun setContent(textPage: TextPage, resetPageOffset: Boolean = true) {
setProgress(textPage) setProgress(textPage)
if (resetPageOffset)
resetPageOffset()
content_text_view.setContent(textPage) content_text_view.setContent(textPage)
} }

@ -17,5 +17,5 @@ interface DataSource {
fun hasPrevChapter(): Boolean fun hasPrevChapter(): Boolean
fun upContent(relativePosition: Int = 0) fun upContent(relativePosition: Int = 0, resetPageOffset: Boolean = true)
} }

@ -38,7 +38,7 @@ class PageView(context: Context, attrs: AttributeSet) :
prevPage.x = -w.toFloat() prevPage.x = -w.toFloat()
pageDelegate?.setViewSize(w, h) pageDelegate?.setViewSize(w, h)
if (oldw != 0 && oldh != 0) { if (oldw != 0 && oldh != 0) {
ReadBook.loadContent() ReadBook.loadContent(resetPageOffset = false)
} }
} }
@ -93,9 +93,9 @@ class PageView(context: Context, attrs: AttributeSet) :
upContent() upContent()
} }
override fun upContent(relativePosition: Int) { override fun upContent(relativePosition: Int, resetPageOffset: Boolean) {
if (ReadBookConfig.isScroll) { if (ReadBookConfig.isScroll) {
curPage.setContent(pageFactory.currentPage) curPage.setContent(pageFactory.currentPage, resetPageOffset)
} else { } else {
when (relativePosition) { when (relativePosition) {
-1 -> prevPage.setContent(pageFactory.prevPage) -1 -> prevPage.setContent(pageFactory.prevPage)

@ -38,7 +38,7 @@ class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(dataSource
} else { } else {
ReadBook.setPageIndex(pageIndex.plus(1)) ReadBook.setPageIndex(pageIndex.plus(1))
} }
if (upContent) upContent() if (upContent) upContent(resetPageOffset = false)
true true
} else } else
false false
@ -51,7 +51,7 @@ class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(dataSource
} else { } else {
ReadBook.setPageIndex(pageIndex.minus(1)) ReadBook.setPageIndex(pageIndex.minus(1))
} }
if (upContent) upContent() if (upContent) upContent(resetPageOffset = false)
true true
} else } else
false false
@ -59,6 +59,9 @@ class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(dataSource
override val currentPage: TextPage override val currentPage: TextPage
get() = with(dataSource) { get() = with(dataSource) {
ReadBook.msg?.let {
return@with TextPage(text = it).format()
}
currentChapter?.let { currentChapter?.let {
return@with it.page(pageIndex) return@with it.page(pageIndex)
?: TextPage(title = it.title).format() ?: TextPage(title = it.title).format()
@ -68,6 +71,9 @@ class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(dataSource
override val nextPage: TextPage override val nextPage: TextPage
get() = with(dataSource) { get() = with(dataSource) {
ReadBook.msg?.let {
return@with TextPage(text = it).format()
}
currentChapter?.let { currentChapter?.let {
if (pageIndex < it.pageSize() - 1) { if (pageIndex < it.pageSize() - 1) {
return@with it.page(pageIndex + 1)?.removePageAloudSpan() return@with it.page(pageIndex + 1)?.removePageAloudSpan()
@ -86,6 +92,9 @@ class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(dataSource
override val prevPage: TextPage override val prevPage: TextPage
get() = with(dataSource) { get() = with(dataSource) {
ReadBook.msg?.let {
return@with TextPage(text = it).format()
}
if (pageIndex > 0) { if (pageIndex > 0) {
currentChapter?.let { currentChapter?.let {
return@with it.page(pageIndex - 1)?.removePageAloudSpan() return@with it.page(pageIndex - 1)?.removePageAloudSpan()

@ -3,8 +3,6 @@ package io.legado.app.ui.main
import android.os.Bundle import android.os.Bundle
import android.view.KeyEvent import android.view.KeyEvent
import android.view.MenuItem import android.view.MenuItem
import androidx.core.view.forEach
import androidx.core.view.forEachIndexed
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter import androidx.fragment.app.FragmentStatePagerAdapter
@ -17,7 +15,6 @@ import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.storage.Backup
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.service.BaseReadAloudService import io.legado.app.service.BaseReadAloudService
import io.legado.app.service.help.ReadAloud import io.legado.app.service.help.ReadAloud
@ -96,14 +93,6 @@ class MainActivity : VMBaseActivity<MainViewModel>(R.layout.activity_main),
bottom_navigation_view.menu.getItem(3).isChecked = true bottom_navigation_view.menu.getItem(3).isChecked = true
} }
} }
// bottom_navigation_view.menu.forEachIndexed { index, item ->
// if (item.isChecked)
// item.icon = getDrawable(res[1][index])
// else
// item.icon = getDrawable(res[0][index])
// }
} }
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
@ -124,13 +113,6 @@ class MainActivity : VMBaseActivity<MainViewModel>(R.layout.activity_main),
return super.onKeyUp(keyCode, event) return super.onKeyUp(keyCode, event)
} }
override fun finish() {
if (!BuildConfig.DEBUG) {
Backup.autoBack(this)
}
super.finish()
}
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
ReadAloud.stop(this) ReadAloud.stop(this)

@ -21,7 +21,6 @@ class BooksAdapterGrid(context: Context, private val callBack: CallBack) :
if (bundle == null) { if (bundle == null) {
ATH.applyBackgroundTint(this) ATH.applyBackgroundTint(this)
tv_name.text = item.name tv_name.text = item.name
bv_author.text = item.author
iv_cover.load(item.getDisplayCover(), item.name, item.author) iv_cover.load(item.getDisplayCover(), item.name, item.author)
if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) { if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) {
bv_unread.invisible() bv_unread.invisible()
@ -35,7 +34,6 @@ class BooksAdapterGrid(context: Context, private val callBack: CallBack) :
bundle.keySet().map { bundle.keySet().map {
when (it) { when (it) {
"name" -> tv_name.text = item.name "name" -> tv_name.text = item.name
"author" -> bv_author.text = item.author
"cover" -> iv_cover.load(item.getDisplayCover(), item.name, item.author) "cover" -> iv_cover.load(item.getDisplayCover(), item.name, item.author)
"refresh" -> if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) { "refresh" -> if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) {
bv_unread.invisible() bv_unread.invisible()

@ -24,6 +24,7 @@ import io.legado.app.ui.config.ConfigActivity
import io.legado.app.ui.config.ConfigViewModel import io.legado.app.ui.config.ConfigViewModel
import io.legado.app.ui.filechooser.FileChooserDialog import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.replacerule.ReplaceRuleActivity import io.legado.app.ui.replacerule.ReplaceRuleActivity
import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.ui.widget.prefs.NameListPreference import io.legado.app.ui.widget.prefs.NameListPreference
import io.legado.app.ui.widget.prefs.PreferenceCategory import io.legado.app.ui.widget.prefs.PreferenceCategory
import io.legado.app.ui.widget.prefs.SwitchPreference import io.legado.app.ui.widget.prefs.SwitchPreference
@ -48,9 +49,10 @@ class MyFragment : BaseFragment(R.layout.fragment_my_config), FileChooserDialog.
override fun onCompatOptionsItemSelected(item: MenuItem) { override fun onCompatOptionsItemSelected(item: MenuItem) {
when (item.itemId) { when (item.itemId) {
R.id.menu_help -> startActivity<AboutActivity>() R.id.menu_help -> {
R.id.menu_backup -> BackupRestoreUi.backup(this) val text = String(requireContext().assets.open("help.md").readBytes())
R.id.menu_restore -> BackupRestoreUi.restore(this) TextDialog.show(childFragmentManager, text, TextDialog.MD)
}
} }
} }

@ -22,7 +22,6 @@ import org.apache.commons.text.StringEscapeUtils
import org.jetbrains.anko.share import org.jetbrains.anko.share
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
import org.jsoup.Jsoup import org.jsoup.Jsoup
import org.jsoup.safety.Whitelist
class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_read), class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_read),
@ -246,18 +245,15 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_r
@SuppressLint("SetJavaScriptEnabled") @SuppressLint("SetJavaScriptEnabled")
private fun readAloud() { private fun readAloud() {
if (viewModel.textToSpeech.isSpeaking) { if (viewModel.textToSpeech?.isSpeaking == true) {
viewModel.textToSpeech.stop() viewModel.textToSpeech?.stop()
upTtsMenu(false) upTtsMenu(false)
} else { } else {
web_view.settings.javaScriptEnabled = true web_view.settings.javaScriptEnabled = true
web_view.evaluateJavascript("document.documentElement.outerHTML") { web_view.evaluateJavascript("document.documentElement.outerHTML") {
val html = StringEscapeUtils.unescapeJson(it) val html = StringEscapeUtils.unescapeJson(it)
val text = Jsoup.clean(html, Whitelist.none()) Jsoup.parse(html).text()
.replace(Regex("""&\w+;"""), "") viewModel.readAloud(Jsoup.parse(html).textArray())
.trim()//朗读过程中总是听到一些杂音,清理一下
//longToast(需读内容)调试一下
viewModel.readAloud(text)
} }
} }
} }

@ -35,7 +35,9 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application),
val contentLiveData = MutableLiveData<String>() val contentLiveData = MutableLiveData<String>()
val urlLiveData = MutableLiveData<AnalyzeUrl>() val urlLiveData = MutableLiveData<AnalyzeUrl>()
var star = false var star = false
var textToSpeech: TextToSpeech = TextToSpeech(context, this) var textToSpeech: TextToSpeech? = null
private var ttsInitFinish = false
private var ttsTextList = arrayListOf<String>()
fun initData(intent: Intent) { fun initData(intent: Intent) {
execute { execute {
@ -143,28 +145,43 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application),
} }
} }
@Synchronized
override fun onInit(status: Int) { override fun onInit(status: Int) {
launch { if (status == TextToSpeech.SUCCESS) {
if (status == TextToSpeech.SUCCESS) { textToSpeech?.language = Locale.CHINA
textToSpeech.language = Locale.CHINA textToSpeech?.setOnUtteranceProgressListener(TTSUtteranceListener())
textToSpeech.setOnUtteranceProgressListener(TTSUtteranceListener()) ttsInitFinish = true
} else { play()
} else {
launch {
toast(R.string.tts_init_failed) toast(R.string.tts_init_failed)
} }
} }
} }
fun readAloud(text: String) { @Synchronized
textToSpeech.stop() private fun play() {
text.split("\n", " ", "  ").forEach { if (!ttsInitFinish) return
textToSpeech.speak(it, TextToSpeech.QUEUE_ADD, null, "rss") textToSpeech?.stop()
ttsTextList.forEach {
textToSpeech?.speak(it, TextToSpeech.QUEUE_ADD, null, "rss")
}
}
fun readAloud(textArray: Array<String>) {
ttsTextList.clear()
ttsTextList.addAll(textArray)
textToSpeech?.let {
play()
} ?: let {
textToSpeech = TextToSpeech(context, this)
} }
} }
override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()
textToSpeech.stop() textToSpeech?.stop()
textToSpeech.shutdown() textToSpeech?.shutdown()
} }
/** /**

@ -24,10 +24,7 @@ import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint import io.legado.app.utils.*
import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.requestInputMethod
import io.legado.app.utils.splitNotBlank
import kotlinx.android.synthetic.main.dialog_edit_text.view.* import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.* import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_group_manage.view.* import kotlinx.android.synthetic.main.item_group_manage.view.*
@ -68,6 +65,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
recycler_view.addItemDecoration(VerticalDivider(requireContext())) recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter recycler_view.adapter = adapter
tv_ok.setTextColor(requireContext().accentColor) tv_ok.setTextColor(requireContext().accentColor)
tv_ok.visible()
tv_ok.onClick { dismiss() } tv_ok.onClick { dismiss() }
App.db.rssSourceDao().liveGroup().observe(viewLifecycleOwner, Observer { App.db.rssSourceDao().liveGroup().observe(viewLifecycleOwner, Observer {
val groups = linkedSetOf<String>() val groups = linkedSetOf<String>()

@ -59,3 +59,4 @@ open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) {
class Launcher1 : WelcomeActivity() class Launcher1 : WelcomeActivity()
class Launcher2 : WelcomeActivity() class Launcher2 : WelcomeActivity()
class Launcher3 : WelcomeActivity() class Launcher3 : WelcomeActivity()
class Launcher4 : WelcomeActivity()

@ -77,10 +77,16 @@ class TextDialog : BaseDialogFragment() {
time -= 1000 time -= 1000
badge_view.setBadgeCount((time / 1000).toInt()) badge_view.setBadgeCount((time / 1000).toInt())
if (time <= 0) { if (time <= 0) {
dialog?.setCancelable(true) view.post {
dialog?.setCancelable(true)
}
} }
} }
} }
} else {
view.post {
dialog?.setCancelable(true)
}
} }
} }

@ -98,7 +98,7 @@ object DocumentUtils {
DocumentsContract.Document.COLUMN_LAST_MODIFIED, DocumentsContract.Document.COLUMN_LAST_MODIFIED,
DocumentsContract.Document.COLUMN_SIZE, DocumentsContract.Document.COLUMN_SIZE,
DocumentsContract.Document.COLUMN_MIME_TYPE DocumentsContract.Document.COLUMN_MIME_TYPE
), null, null, null ), null, null, DocumentsContract.Document.COLUMN_DISPLAY_NAME
) )
c?.let { c?.let {
val ici = c.getColumnIndex(DocumentsContract.Document.COLUMN_DOCUMENT_ID) val ici = c.getColumnIndex(DocumentsContract.Document.COLUMN_DOCUMENT_ID)

@ -0,0 +1,66 @@
package io.legado.app.utils
import org.jsoup.internal.StringUtil
import org.jsoup.nodes.CDataNode
import org.jsoup.nodes.Element
import org.jsoup.nodes.Node
import org.jsoup.nodes.TextNode
import org.jsoup.select.NodeTraversor
import org.jsoup.select.NodeVisitor
fun Element.textArray(): Array<String> {
val accum = StringUtil.borrowBuilder()
NodeTraversor.traverse(object : NodeVisitor {
override fun head(node: Node, depth: Int) {
if (node is TextNode) {
appendNormalisedText(accum, node)
} else if (node is Element) {
if (accum.isNotEmpty() &&
(node.isBlock || node.tag().name == "br") &&
!lastCharIsWhitespace(accum)
) accum.append("\n")
}
}
override fun tail(node: Node, depth: Int) {
if (node is Element) {
if (node.isBlock && node.nextSibling() is TextNode && !lastCharIsWhitespace(
accum
)
) accum.append("\n")
}
}
}, this)
val text = StringUtil.releaseBuilder(accum).trim { it <= ' ' }
return text.splitNotBlank("\n")
}
private fun appendNormalisedText(accum: StringBuilder, textNode: TextNode) {
val text = textNode.wholeText
if (preserveWhitespace(textNode.parentNode()) || textNode is CDataNode)
accum.append(text)
else StringUtil.appendNormalisedWhitespace(
accum,
text,
lastCharIsWhitespace(accum)
)
}
private fun preserveWhitespace(node: Node?): Boolean {
if (node is Element) {
var el = node as Element?
var i = 0
do {
if (el!!.tag().preserveWhitespace()) return true
el = el.parent()
i++
} while (i < 6 && el != null)
}
return false
}
private fun lastCharIsWhitespace(sb: java.lang.StringBuilder): Boolean {
return sb.isNotEmpty() && sb[sb.length - 1] == ' '
}

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_bottom_books_e" android:state_checked="false"></item> <item android:drawable="@drawable/ic_bottom_books_e" android:state_checked="false" />
<item android:drawable="@drawable/ic_bottom_books_s" android:state_checked="true"></item> <item android:drawable="@drawable/ic_bottom_books_s" android:state_checked="true" />
</selector> </selector>

@ -1,7 +1,12 @@
<vector android:height="24dp" android:viewportHeight="18" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="18" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:width="24dp"
<path android:fillColor="#2F45A6" android:pathData="M15.8,18h-0.5c-1.2,0 -2.2,-1 -2.2,-2.2v-8.5c0,-1.2 1,-2.2 2.2,-2.2h0.5c1.2,0 2.2,1 2.2,2.2v8.5C18,17 17,18 15.8,18zM15.2,6.5c-0.4,0 -0.8,0.3 -0.8,0.8v8.5c0,0.4 0.3,0.8 0.8,0.8h0.5c0.4,0 0.8,-0.3 0.8,-0.8v-8.5c0,-0.4 -0.3,-0.8 -0.8,-0.8L15.2,6.5z"/> android:height="24dp"
<path android:fillColor="#2F45A6" android:pathData="M9.2,18h-0.5c-1.2,0 -2.2,-1 -2.2,-2.2v-13.5c0,-1.2 1,-2.2 2.2,-2.2h0.5c1.2,0 2.2,1 2.2,2.2v13.5C11.5,17 10.5,18 9.2,18zM8.8,1.5c-0.4,0 -0.8,0.3 -0.8,0.8v13.5c0,0.4 0.3,0.8 0.8,0.8h0.5c0.4,0 0.8,-0.3 0.8,-0.8v-13.5c0,-0.4 -0.3,-0.8 -0.8,-0.8L8.8,1.5z"/> android:viewportWidth="24"
<path android:fillColor="#2F45A6" android:pathData="M2.8,18h-0.5c-1.2,0 -2.2,-1 -2.2,-2.2v-10.5c0,-1.2 1,-2.2 2.2,-2.2h0.5c1.2,0 2.2,1 2.2,2.2v10.5C5,17 4,18 2.8,18zM2.2,4.5c-0.4,0 -0.8,0.3 -0.8,0.8v10.5c0,0.4 0.3,0.8 0.8,0.8h0.5c0.4,0 0.8,-0.3 0.8,-0.8v-10.5c0,-0.4 -0.3,-0.8 -0.8,-0.8L2.2,4.5z"/> android:viewportHeight="24">
<path android:fillColor="#2F45A6" android:pathData="M8.5,13h1c0.3,0 0.5,0.2 0.5,0.5v0.5c0,0.3 -0.2,0.5 -0.5,0.5h-1c-0.3,0 -0.5,-0.2 -0.5,-0.5v-0.5C8,13.2 8.2,13 8.5,13z"/> <path
android:pathData="M20.5,5a2.54,2.54 0,0 1,1.1 -0.48,0.49 0.49,0 0,0 0.4,-0.48V3.5a0.5,0.5 0,0 0,-0.5 -0.5H6.17A4.12,4.12 0,0 0,2 6.61,4 4,0 0,0 6,11H21.5a0.5,0.5 0,0 0,0.5 -0.5V10a0.49,0.49 0,0 0,-0.4 -0.48A2.49,2.49 0,0 1,20.5 5ZM6,9.5A2.51,2.51 0,0 1,3.51 6.74,2.61 2.61,0 0,1 6.15,4.5H18.9a3.92,3.92 0,0 0,0 5Z"
android:fillColor="#2f45a6" />
<path
android:pathData="M3.5,19a2.54,2.54 0,0 1,-1.1 0.48A0.49,0.49 0,0 0,2 20v0.55a0.5,0.5 0,0 0,0.5 0.5H17.83A4.12,4.12 0,0 0,22 17.39,4 4,0 0,0 18,13H2.5a0.5,0.5 0,0 0,-0.5 0.5v0.55a0.49,0.49 0,0 0,0.4 0.48A2.54,2.54 0,0 1,3.5 15a2.48,2.48 0,0 1,0 4ZM18,14.5a2.51,2.51 0,0 1,2.49 2.76,2.61 2.61,0 0,1 -2.64,2.24H5.1a3.92,3.92 0,0 0,0 -5Z"
android:fillColor="#2f45a6" />
</vector> </vector>

@ -1,6 +1,41 @@
<vector android:height="24dp" android:viewportHeight="18" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="18" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:aapt="http://schemas.android.com/aapt"
<path android:fillColor="#C8D0D9" android:pathData="M14.5,5h2C17.3,5 18,5.7 18,6.5v10c0,0.8 -0.7,1.5 -1.5,1.5h-2c-0.8,0 -1.5,-0.7 -1.5,-1.5v-10C13,5.7 13.7,5 14.5,5z"/> android:width="24dp"
<path android:fillColor="#C8D0D9" android:pathData="M1.5,3h2C4.3,3 5,3.7 5,4.5v12C5,17.3 4.3,18 3.5,18h-2C0.7,18 0,17.3 0,16.5v-12C0,3.7 0.7,3 1.5,3z"/> android:height="24dp"
<path android:fillColor="#C8D0D9" android:pathData="M10,0H8C7.2,0 6.5,0.7 6.5,1.5v15C6.5,17.3 7.2,18 8,18h2c0.8,0 1.5,-0.7 1.5,-1.5v-15C11.5,0.7 10.8,0 10,0zM10,14c0,0.3 -0.2,0.5 -0.5,0.5h-1C8.2,14.5 8,14.3 8,14v-0.5C8,13.2 8.2,13 8.5,13h1c0.3,0 0.5,0.2 0.5,0.5V14z"/> android:viewportWidth="24"
android:viewportHeight="24">
<path android:pathData="M20.5,5a2.54,2.54 0,0 1,1.1 -0.48,0.49 0.49,0 0,0 0.4,-0.48V3.5a0.5,0.5 0,0 0,-0.5 -0.5H6.17A4.12,4.12 0,0 0,2 6.61,4 4,0 0,0 6,11H21.5a0.5,0.5 0,0 0,0.5 -0.5V10a0.49,0.49 0,0 0,-0.4 -0.48A2.49,2.49 0,0 1,20.5 5Z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="7"
android:startX="2"
android:endY="7"
android:endX="22"
android:type="linear">
<item
android:offset="0"
android:color="#FF2F45A6" />
<item
android:offset="1"
android:color="#FF5771CE" />
</gradient>
</aapt:attr>
</path>
<path android:pathData="M18,13H2.5a0.5,0.5 0,0 0,-0.5 0.5v0.55a0.49,0.49 0,0 0,0.4 0.48A2.54,2.54 0,0 1,3.5 15a2.48,2.48 0,0 1,0 4,2.54 2.54,0 0,1 -1.1,0.48A0.49,0.49 0,0 0,2 20v0.55a0.5,0.5 0,0 0,0.5 0.5H17.83A4.12,4.12 0,0 0,22 17.39,4 4,0 0,0 18,13Z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="17"
android:startX="2"
android:endY="17"
android:endX="22"
android:type="linear">
<item
android:offset="0"
android:color="#FF5771CE" />
<item
android:offset="1"
android:color="#FF2F45A6" />
</gradient>
</aapt:attr>
</path>
</vector> </vector>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_bottom_explore_e" android:state_checked="false" />
<item android:drawable="@drawable/ic_bottom_explore_s" android:state_checked="true" />
</selector>

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_bottom_explore_black_e" android:state_checked="false"></item>
<item android:drawable="@drawable/ic_bottom_explore_black_s" android:state_checked="true"></item>
</selector>

@ -1,5 +0,0 @@
<vector android:height="24dp" android:viewportHeight="18"
android:viewportWidth="18" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#959ECA" android:pathData="M9,18c-5,0 -9,-4 -9,-9c0,-5 4,-9 9,-9c5,0 9,4 9,9C18,14 14,18 9,18zM9,1.5C4.9,1.5 1.5,4.9 1.5,9s3.4,7.5 7.5,7.5s7.5,-3.4 7.5,-7.5S13.1,1.5 9,1.5z"/>
<path android:fillColor="#959ECA" android:pathData="M10.5,10c0.3,-0.5 0.7,-2.1 1.4,-4.8l0,0c0,-0.1 0,-0.2 -0.1,-0.2c-0.1,0 -0.1,0 -0.2,0C9.3,6.8 8,8 7.8,8.5c-0.4,0.7 -0.2,1.7 0.6,2.1C9.1,11 10,10.8 10.5,10z"/>
</vector>

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="18"
android:viewportHeight="18">
<path
android:pathData="M9,0C4,0 0,4 0,9s4,9 9,9s9,-4 9,-9S14,0 9,0zM10.5,10c-0.4,0.7 -1.4,1 -2.1,0.6c-0.7,-0.4 -1,-1.4 -0.6,-2.1C8,8 9.3,6.8 11.6,5c0,0 0.1,-0.1 0.2,0c0.1,0 0.2,0.1 0.1,0.2C11.2,7.9 10.7,9.5 10.5,10z"
android:fillColor="#C8D0D9"/>
</vector>

@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,3.5A8.5,8.5 0,1 1,3.5 12,8.51 8.51,0 0,1 12,3.5M12,2A10,10 0,1 0,22 12,10 10,0 0,0 12,2Z"
android:fillColor="#2f45a6" />
<path
android:pathData="M14.23,9.82 L13,12.91 9.87,14.18l1.27,-3.09 3.09,-1.27m2.38,-2.57h-0.08L10.06,9.9A0.2,0.2 0,0 0,10 10L7.31,16.48a0.2,0.2 0,0 0,0.18 0.27h0.08L14,14.1a0.2,0.2 0,0 0,0.11 -0.11l2.64,-6.47a0.2,0.2 0,0 0,-0.18 -0.27Z"
android:fillColor="#2f45a6" />
</vector>

@ -0,0 +1,24 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10s10,-4.5 10,-10S17.5,2 12,2zM16.8,7.5L14.1,14c0,0.1 -0.1,0.1 -0.1,0.1l-6.5,2.6c-0.1,0 -0.2,0 -0.3,-0.1c0,0 0,-0.1 0,-0.1L10,10c0,-0.1 0.1,-0.1 0.1,-0.1l6.5,-2.6C16.7,7.2 16.8,7.3 16.8,7.5C16.8,7.5 16.8,7.5 16.8,7.5z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="12"
android:startX="2"
android:endY="12"
android:endX="22"
android:type="linear">
<item
android:offset="0"
android:color="#FF5771CE" />
<item
android:offset="0.99"
android:color="#FF2F45A6" />
</gradient>
</aapt:attr>
</path>
</vector>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_bottom_person_e" android:state_checked="false"></item> <item android:drawable="@drawable/ic_bottom_person_e" android:state_checked="false" />
<item android:drawable="@drawable/ic_bottom_person_s" android:state_checked="true"></item> <item android:drawable="@drawable/ic_bottom_person_s" android:state_checked="true" />
</selector> </selector>

@ -1,5 +1,12 @@
<vector android:height="24dp" android:viewportHeight="18" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="16" android:width="21.333334dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:width="24dp"
<path android:fillColor="#959ECA" android:pathData="M8,8C5.8,8 4,6.2 4,4s1.8,-4 4,-4c2.2,0 4,1.8 4,4S10.2,8 8,8zM8,1.5C6.6,1.5 5.5,2.6 5.5,4S6.6,6.5 8,6.5c1.4,0 2.5,-1.1 2.5,-2.5S9.4,1.5 8,1.5z"/> android:height="24dp"
<path android:fillColor="#959ECA" android:pathData="M14.5,18h-13c-0.4,0 -0.8,-0.2 -1.1,-0.4S0,16.9 0,16.5V14c0,-1.1 0.4,-2.1 1.2,-2.8C1.9,10.4 2.9,10 4,10h8c1.1,0 2.1,0.4 2.8,1.2c0.8,0.8 1.2,1.8 1.2,2.8v2.5c0,0.4 -0.2,0.8 -0.4,1.1S14.9,18 14.5,18zM4,11.5c-0.7,0 -1.3,0.3 -1.8,0.7c-0.5,0.5 -0.7,1.1 -0.7,1.8v2.5h13V14c0,-0.7 -0.3,-1.3 -0.7,-1.8c-0.5,-0.5 -1.1,-0.7 -1.8,-0.7H4z"/> android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M4.5,20A3.5,3.5 0,0 1,8 16.5h8A3.5,3.5 0,0 1,19.5 20v2h0.7a0.8,0.8 0,0 0,0.8 -0.8V20a5,5 0,0 0,-5 -5H8a5,5 0,0 0,-5 5v1.2a0.8,0.8 0,0 0,0.8 0.8h0.7Z"
android:fillColor="#2f45a6" />
<path
android:pathData="M12,3.5a4,4 0,1 1,-4 4,4 4,0 0,1 4,-4M12,2a5.5,5.5 0,1 0,5.5 5.5A5.5,5.5 0,0 0,12 2Z"
android:fillColor="#2f45a6" />
</vector> </vector>

@ -1,9 +1,41 @@
<vector android:height="24dp" android:viewportHeight="18" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="16" android:width="21.333334dp" xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:aapt="http://schemas.android.com/aapt"
<path android:fillColor="#C8D0D9" android:fillType="evenOdd" android:width="24dp"
android:pathData="M8,4m-4,0a4,4 0,1 1,8 0a4,4 0,1 1,-8 0" android:height="24dp"
android:strokeColor="#00000000" android:strokeWidth="1"/> android:viewportWidth="24"
<path android:fillColor="#C8D0D9" android:fillType="evenOdd" android:viewportHeight="24">
android:pathData="M4,10L12,10C14.2091,10 16,11.7909 16,14L16,16.5C16,17.3284 15.3284,18 14.5,18L1.5,18C0.6716,18 0,17.3284 0,16.5L0,14C-0,11.7909 1.7909,10 4,10Z" <path android:pathData="M12,7.5m-5.5,0a5.5,5.5 0,1 1,11 0a5.5,5.5 0,1 1,-11 0">
android:strokeColor="#00000000" android:strokeWidth="1"/> <aapt:attr name="android:fillColor">
<gradient
android:startY="7.5"
android:startX="6.5"
android:endY="7.5"
android:endX="17.5"
android:type="linear">
<item
android:offset="0"
android:color="#FF5771CE" />
<item
android:offset="0.99"
android:color="#FF2F45A6" />
</gradient>
</aapt:attr>
</path>
<path android:pathData="M16,15H8c-2.8,0 -5,2.2 -5,5v1.2C3,21.6 3.4,22 3.8,22h16.4c0.4,0 0.8,-0.4 0.8,-0.8V20C21,17.2 18.8,15 16,15z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="18.5"
android:startX="3"
android:endY="18.5"
android:endX="21"
android:type="linear">
<item
android:offset="0"
android:color="#FF5771CE" />
<item
android:offset="0.99"
android:color="#FF2F45A6" />
</gradient>
</aapt:attr>
</path>
</vector> </vector>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/ic_bottom_rss_feed_e" android:state_checked="false"></item> <item android:drawable="@drawable/ic_bottom_rss_feed_e" android:state_checked="false" />
<item android:drawable="@drawable/ic_bottom_rss_feed_s" android:state_checked="true"></item> <item android:drawable="@drawable/ic_bottom_rss_feed_s" android:state_checked="true" />
</selector> </selector>

@ -1,9 +1,12 @@
<vector android:height="24dp" android:viewportHeight="18" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="16" android:width="21.333334dp" xmlns:android="http://schemas.android.com/apk/res/android"> android:width="24dp"
<path android:fillColor="#5A5F64" android:fillType="nonZero" android:height="24dp"
android:pathData="M13.9991,0L2.0009,0C0.896,0 0,0.907 0,2.025L0,17.1C0,17.6133 0.4143,18 0.8871,18C0.9792,18 1.0748,17.9842 1.1704,17.9525L6.8756,15.6797C7.2404,15.5566 7.6211,15.4951 8,15.4951C8.3789,15.4951 8.7596,15.5566 9.1244,15.6797L14.8296,17.9525C14.9234,17.9842 15.019,18 15.1129,18C15.5857,18 16,17.6133 16,17.1L16,2.025C16,0.907 15.104,0 13.9991,0ZM14.3993,16.0699L9.7211,14.2049L9.6804,14.1891L9.6397,14.175C9.1102,13.9957 8.5595,13.9061 8,13.9061C7.4405,13.9061 6.8898,13.9975 6.3603,14.175L6.3196,14.1891L6.2789,14.2049L1.6007,16.0699L1.6007,2.025C1.6007,1.7842 1.7795,1.5891 2.0009,1.5891L13.9991,1.5891C14.2205,1.5891 14.3993,1.7842 14.3993,2.025L14.3993,16.0699Z" android:viewportWidth="24"
android:strokeColor="#00000000" android:strokeWidth="1"/> android:viewportHeight="24">
<path android:fillColor="#5A5F64" android:fillType="nonZero" <path
android:pathData="M10.947,6.4961L8.7863,6.4961L8.7863,4.1914C8.7863,3.7417 8.4349,3.375 8,3.375C7.5668,3.375 7.2137,3.7399 7.2137,4.1914L7.2137,6.4961L5.053,6.4961C4.6198,6.4961 4.2667,6.861 4.2667,7.3125C4.2667,7.7622 4.6181,8.1289 5.053,8.1289L7.2137,8.1289L7.2137,10.4336C7.2137,10.8833 7.5651,11.25 8,11.25C8.4332,11.25 8.7863,10.8851 8.7863,10.4336L8.7863,8.1289L10.947,8.1289C11.3802,8.1289 11.7333,7.764 11.7333,7.3125C11.7333,6.8628 11.3819,6.4961 10.947,6.4961L10.947,6.4961Z" android:pathData="M18.5,3.5V19.57l-5.6,-2.8a2,2 0,0 0,-1.8 0l-5.6,2.8V3.5h13m1,-1.5H4.5a0.51,0.51 0,0 0,-0.5 0.5V21.18a0.5,0.5 0,0 0,0.5 0.51,0.59 0.59,0 0,0 0.23,-0.05l7,-3.53a0.55,0.55 0,0 1,0.46 0l7,3.53a0.59,0.59 0,0 0,0.23 0.05,0.5 0.5,0 0,0 0.5,-0.51V2.5a0.51,0.51 0,0 0,-0.5 -0.5Z"
android:strokeColor="#00000000" android:strokeWidth="1"/> android:fillColor="#2f45a6" />
<path
android:pathData="M15.5,9.25H12.75V6.5a0.5,0.5 0,0 0,-0.5 -0.5h-0.5a0.5,0.5 0,0 0,-0.5 0.5V9.25H8.5a0.5,0.5 0,0 0,-0.5 0.5v0.5a0.5,0.5 0,0 0,0.5 0.5h2.75V13.5a0.5,0.5 0,0 0,0.5 0.5h0.5a0.5,0.5 0,0 0,0.5 -0.5V10.75H15.5a0.5,0.5 0,0 0,0.5 -0.5v-0.5A0.5,0.5 0,0 0,15.5 9.25Z"
android:fillColor="#2f45a6" />
</vector> </vector>

@ -1,4 +1,24 @@
<vector android:height="24dp" android:viewportHeight="18" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:viewportWidth="16" android:width="21.333334dp" xmlns:android="http://schemas.android.com/apk/res/android"> xmlns:aapt="http://schemas.android.com/aapt"
<path android:fillColor="#C8D0D9" android:pathData="M14,0H2C0.9,0 0,0.9 0,2v15.1C0,17.6 0.4,18 0.9,18c0.1,0 0.2,0 0.3,0l5.7,-2.3c0.4,-0.1 0.7,-0.2 1.1,-0.2s0.8,0.1 1.1,0.2l5.7,2.3c0.1,0 0.2,0 0.3,0c0.5,0 0.9,-0.4 0.9,-0.9V2C16,0.9 15.1,0 14,0zM11,8.8H8.8V11c0,0.4 -0.3,0.8 -0.8,0.8S7.2,11.4 7.2,11V8.8H5C4.6,8.8 4.2,8.4 4.2,8S4.6,7.2 5,7.2h2.2V5c0,-0.4 0.3,-0.8 0.8,-0.8S8.8,4.6 8.8,5v2.2H11c0.4,0 0.8,0.3 0.8,0.8S11.4,8.8 11,8.8z"/> android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:pathData="M19.5,2H4.5a0.51,0.51 0,0 0,-0.5 0.5V21.18a0.51,0.51 0,0 0,0.73 0.46l7,-3.53a0.53,0.53 0,0 1,0.46 0l7,3.53a0.51,0.51 0,0 0,0.73 -0.46V2.5A0.51,0.51 0,0 0,19.5 2ZM16,10.25a0.5,0.5 0,0 1,-0.5 0.5H12.75V13.5a0.5,0.5 0,0 1,-0.5 0.5h-0.5a0.5,0.5 0,0 1,-0.5 -0.5V10.75H8.5a0.5,0.5 0,0 1,-0.5 -0.5v-0.5a0.5,0.5 0,0 1,0.5 -0.5h2.75V6.5a0.5,0.5 0,0 1,0.5 -0.5h0.5a0.5,0.5 0,0 1,0.5 0.5V9.25H15.5a0.5,0.5 0,0 1,0.5 0.5Z">
<aapt:attr name="android:fillColor">
<gradient
android:startY="11.84"
android:startX="4"
android:endY="11.84"
android:endX="20"
android:type="linear">
<item
android:offset="0"
android:color="#FF5771CE" />
<item
android:offset="0.99"
android:color="#FF2F45A6" />
</gradient>
</aapt:attr>
</path>
</vector> </vector>

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#595757"
android:pathData="M18.545,18.304c0,0.131-0.11,0.242-0.241,0.242H5.696c-0.131,0-0.242-0.111-0.242-0.242V5.697 c0-0.131,0.111-0.243,0.242-0.243H12V4H5.696C4.76,4,4,4.762,4,5.697v12.606C4,19.239,4.76,20,5.696,20h12.607 C19.239,20,20,19.239,20,18.304v-6.303h-1.455V18.304z" />
<path
android:fillColor="#595757"
android:pathData="M 19.272 4 L 13.843 4 L 13.843 5.454 L 17.517 5.454 L 7.464 15.508 L 8.491 16.536 L 18.545 6.482 L 18.545 10.157 L 20 10.157 L 20 4.727 L 20 4 Z" />
</vector>

@ -0,0 +1,18 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:pathData="M50.8,56.9c-0.2,2.4 -2.4,4.2 -4.8,4c-1.2,-0.1 -2.3,-0.7 -3.1,-1.7l8.8,-5.1l0,0l0.5,-0.3c-0.1,-0.2 -0.2,-0.4 -0.3,-0.6c-1.8,-3.1 -5.7,-4.1 -8.7,-2.3c-3.1,1.8 -4.1,5.7 -2.3,8.7c1.8,3.1 5.7,4.1 8.7,2.3c1.8,-1.1 3,-3 3.2,-5.1H50.8zM44.1,52.6c1.7,-1 3.9,-0.7 5.3,0.6L42,57.5C41.6,55.6 42.4,53.6 44.1,52.6z"
android:fillColor="#333333" />
<path
android:pathData="M34.1,55c2.7,-0.6 4.4,-3.4 3.8,-6.1C37.4,46.6 35.4,45 33,45h-7v17.8h1.9V46.9H33c1.8,0 3.1,1.5 3.1,3.3c0,1.6 -1.3,3 -2.9,3.1h-2.1l5.1,9.5h2.2L34.1,55z"
android:fillColor="#2F45A6" />
<path
android:pathData="M61,50.1c-3.5,0.1 -6.3,3.1 -6.1,6.6c0.1,3.3 2.8,6 6.1,6.1h3.2v-1.9H61c-2.5,0 -4.5,-2 -4.5,-4.4c0,-2.5 2,-4.5 4.4,-4.5c2.5,0 4.5,2 4.5,4.4c0,0 0,0 0,0v6.4h1.9v-6.4C67.4,52.9 64.5,50.1 61,50.1L61,50.1z"
android:fillColor="#333333" />
<path
android:pathData="M80.1,45v11.5c0,2.5 -2,4.5 -4.5,4.5c-2.5,0 -4.5,-2 -4.5,-4.5c0,-2.5 2,-4.5 4.4,-4.5c0,0 0,0 0,0h3.2v-1.9h-3.2c-3.5,0 -6.4,2.8 -6.4,6.4c0,3.5 2.8,6.4 6.4,6.4c1.7,0 3.3,-0.7 4.5,-1.8v1.8H82V45H80.1z"
android:fillColor="#333333" />
</vector>

@ -10,7 +10,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:elevation="10dp" android:elevation="10dp"
android:background="@color/background" android:background="@color/background"
app:labelVisibilityMode="labeled" app:labelVisibilityMode="unlabeled"
app:menu="@menu/main_bnv" app:menu="@menu/main_bnv"
app:layout_constraintBottom_toBottomOf="parent" /> app:layout_constraintBottom_toBottomOf="parent" />

@ -221,6 +221,7 @@
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:text="@string/chapter_list" android:text="@string/chapter_list"
android:maxLines="1"
android:textColor="@color/tv_text_default" android:textColor="@color/tv_text_default"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
@ -256,6 +257,7 @@
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:text="@string/main_menu" android:text="@string/main_menu"
android:maxLines="1"
android:textColor="@color/tv_text_default" android:textColor="@color/tv_text_default"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
@ -291,6 +293,7 @@
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:text="@string/to_backstage" android:text="@string/to_backstage"
android:maxLines="1"
android:textColor="@color/tv_text_default" android:textColor="@color/tv_text_default"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>
@ -325,7 +328,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp" android:layout_marginTop="3dp"
android:text="@string/aloud_config" android:text="@string/setting"
android:maxLines="1"
android:textColor="@color/tv_text_default" android:textColor="@color/tv_text_default"
android:textSize="12sp" /> android:textSize="12sp" />
</LinearLayout> </LinearLayout>

@ -15,7 +15,7 @@
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
android:background="@color/background_card" android:background="@color/background_card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="0dp"
android:layout_weight="1" /> android:layout_weight="1" />
<LinearLayout <LinearLayout
@ -33,8 +33,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
android:padding="12dp" android:padding="12dp"
android:clickable="true"
android:text="@string/ok" android:text="@string/ok"
android:visibility="gone"
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />
</LinearLayout> </LinearLayout>

@ -48,19 +48,6 @@
tools:ignore="RtlHardcoded" /> tools:ignore="RtlHardcoded" />
</FrameLayout> </FrameLayout>
<io.legado.app.ui.widget.text.BadgeView
android:id="@+id/bv_author"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:includeFontPadding="false"
android:singleLine="true"
app:radius="1dp"
app:up_flat_angle="true"
app:layout_constraintLeft_toLeftOf="@id/iv_cover"
app:layout_constraintBottom_toBottomOf="@id/iv_cover"
tools:ignore="RtlHardcoded" />
<TextView <TextView
android:id="@+id/tv_name" android:id="@+id/tv_name"
android:layout_width="0dp" android:layout_width="0dp"

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/shape_card_view" android:background="@drawable/shape_card_view"
android:padding="5dp" android:padding="5dp"
android:orientation="horizontal"> android:orientation="horizontal">
@ -10,7 +11,13 @@
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" /> android:layout_gravity="center"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="@+id/iv_menu_more"
app:layout_constraintRight_toLeftOf="@+id/iv_menu_more"
app:layout_constraintBottom_toBottomOf="@+id/iv_menu_more" />
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_menu_more" android:id="@+id/iv_menu_more"
@ -21,7 +28,20 @@
android:tint="@color/tv_text_default" android:tint="@color/tv_text_default"
android:visibility="gone" android:visibility="gone"
android:contentDescription="@string/more_menu" android:contentDescription="@string/more_menu"
android:layout_gravity="center_vertical" /> android:layout_gravity="center_vertical"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.appcompat.widget.LinearLayoutCompat> <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view_more"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/iv_menu_more"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -4,20 +4,20 @@
tools:showIn="bottom_navigation_view"> tools:showIn="bottom_navigation_view">
<item <item
android:id="@+id/menu_bookshelf" android:id="@+id/menu_bookshelf"
android:icon="@drawable/ic_bottom_books" android:icon="@drawable/ic_bottom_books"
android:title="@string/bookshelf"/> android:title="@string/bookshelf" />
<item <item
android:id="@+id/menu_find_book" android:id="@+id/menu_find_book"
android:icon="@drawable/ic_bottom_explore_black" android:icon="@drawable/ic_bottom_explore"
android:title="@string/find"/> android:title="@string/find" />
<item <item
android:id="@+id/menu_rss" android:id="@+id/menu_rss"
android:icon="@drawable/ic_bottom_rss_feed" android:icon="@drawable/ic_bottom_rss_feed"
android:title="@string/rss" /> android:title="@string/rss" />
<item <item
android:id="@+id/menu_my_config" android:id="@+id/menu_my_config"
android:icon="@drawable/ic_bottom_person" android:icon="@drawable/ic_bottom_person"
android:title="@string/my"/> android:title="@string/my" />
</menu> </menu>

@ -10,16 +10,4 @@
app:showAsAction="always" app:showAsAction="always"
tools:ignore="AlwaysShowAction" /> tools:ignore="AlwaysShowAction" />
<item
android:id="@+id/menu_backup"
android:title="@string/backup"
android:icon="@drawable/ic_backup"
app:showAsAction="always" />
<item
android:id="@+id/menu_restore"
android:title="@string/restore"
android:icon="@drawable/ic_restore"
app:showAsAction="always" />
</menu> </menu>

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_grey_100" /> <background android:drawable="@color/md_grey_100" />
<foreground android:drawable="@drawable/ic_launcher_0" /> <foreground android:drawable="@drawable/ic_launcher" />
</adaptive-icon> </adaptive-icon>

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/md_grey_100" />
<foreground android:drawable="@drawable/ic_launcher_4" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 12 KiB

@ -6,9 +6,9 @@
<item>launcher1</item> <item>launcher1</item>
<item>launcher2</item> <item>launcher2</item>
<item>launcher3</item> <item>launcher3</item>
<item>launcher4</item>
</string-array> </string-array>
<string-array name="screen_time_out_value"> <string-array name="screen_time_out_value">
<item>0</item> <item>0</item>
<item>60</item> <item>60</item>

@ -79,6 +79,7 @@
<item>icon1</item> <item>icon1</item>
<item>icon2</item> <item>icon2</item>
<item>icon3</item> <item>icon3</item>
<item>icon4</item>
</string-array> </string-array>
<string-array name="chinese_mode"> <string-array name="chinese_mode">

@ -6,7 +6,6 @@
<dimen name="nav_header_height">176dp</dimen> <dimen name="nav_header_height">176dp</dimen>
<dimen name="fab_margin">16dp</dimen> <dimen name="fab_margin">16dp</dimen>
<dimen name="font_size_normal">14sp</dimen> <dimen name="font_size_normal">14sp</dimen>
<dimen name="font_size_middle">16sp</dimen> <dimen name="font_size_middle">16sp</dimen>
<dimen name="font_size_large">18sp</dimen> <dimen name="font_size_large">18sp</dimen>

@ -81,7 +81,7 @@
<string name="retry">重试</string> <string name="retry">重试</string>
<string name="web_service">Web 服务</string> <string name="web_service">Web 服务</string>
<string name="web_edit_source">web编辑书源</string> <string name="web_edit_source">web编辑书源</string>
<string name="http_ip">http://%s:%d</string> <string name="http_ip">http://%1$s:%2$d</string>
<string name="download_offline">离线下载</string> <string name="download_offline">离线下载</string>
<string name="download_offline_t">离线下载</string> <string name="download_offline_t">离线下载</string>
<string name="download_offline_s">下载选择的章节到本地</string> <string name="download_offline_s">下载选择的章节到本地</string>
@ -207,14 +207,14 @@
<string name="replace_rule_invalid">替换规则为空或者不满足正则表达式要求</string> <string name="replace_rule_invalid">替换规则为空或者不满足正则表达式要求</string>
<string name="select_action">选择操作</string> <string name="select_action">选择操作</string>
<string name="select_all">全选</string> <string name="select_all">全选</string>
<string name="select_all_count">全选(%d/%d)</string> <string name="select_all_count">全选(%1$d/%2$d)</string>
<string name="select_cancel_count">取消(%d/%d)</string> <string name="select_cancel_count">取消(%1$d/%2$d)</string>
<string name="dark_theme">深色模式</string> <string name="dark_theme">深色模式</string>
<string name="welcome">启动页</string> <string name="welcome">启动页</string>
<string name="download_start">开始下载</string> <string name="download_start">开始下载</string>
<string name="download_cancel">取消下载</string> <string name="download_cancel">取消下载</string>
<string name="no_download">暂无任务</string> <string name="no_download">暂无任务</string>
<string name="download_count">已下载 %d/%d</string> <string name="download_count">已下载 %1$d/%2$d</string>
<string name="import_select_book">导入选择书籍</string> <string name="import_select_book">导入选择书籍</string>
<string name="threads_num_title">更新和搜索线程数,太多会卡顿</string> <string name="threads_num_title">更新和搜索线程数,太多会卡顿</string>
<string name="change_icon">切换图标</string> <string name="change_icon">切换图标</string>
@ -225,7 +225,7 @@
<string name="book_intro">内容简介</string> <string name="book_intro">内容简介</string>
<string name="intro_show">简介:%s</string> <string name="intro_show">简介:%s</string>
<string name="open_from_other">打开外部书籍</string> <string name="open_from_other">打开外部书籍</string>
<string name="origin_show">来源:%s</string> <string name="origin_show">来源: %s</string>
<string name="import_replace_rule">本地导入</string> <string name="import_replace_rule">本地导入</string>
<string name="import_replace_rule_on_line">导入在线规则</string> <string name="import_replace_rule_on_line">导入在线规则</string>
<string name="check_update_interval">检查更新间隔</string> <string name="check_update_interval">检查更新间隔</string>
@ -289,7 +289,7 @@
<string name="padding_right">右边距</string> <string name="padding_right">右边距</string>
<string name="check_book_source">校验书源</string> <string name="check_book_source">校验书源</string>
<string name="check_select_source">校验所选</string> <string name="check_select_source">校验所选</string>
<string name="progress_show">进度 %d/%d</string> <string name="progress_show">进度 %1$d/%2$d</string>
<string name="tts_fix">请安装并选择中文TTS!</string> <string name="tts_fix">请安装并选择中文TTS!</string>
<string name="tts_init_failed">TTS初始化失败!</string> <string name="tts_init_failed">TTS初始化失败!</string>
<string name="jf_convert">简繁转换</string> <string name="jf_convert">简繁转换</string>
@ -581,7 +581,7 @@
<string name="book_type">类型:</string> <string name="book_type">类型:</string>
<string name="book_type_text">文本</string> <string name="book_type_text">文本</string>
<string name="book_type_audio">音频</string> <string name="book_type_audio">音频</string>
<string name="to_backstage">转到后台</string> <string name="to_backstage">后台</string>
<string name="importing">正在导入</string> <string name="importing">正在导入</string>
<string name="exporting">正在导出</string> <string name="exporting">正在导出</string>
<string name="custom_page_key">自定义翻页按键</string> <string name="custom_page_key">自定义翻页按键</string>
@ -599,7 +599,7 @@
<string name="auto_dark_mode_s">夜间模式跟随系统</string> <string name="auto_dark_mode_s">夜间模式跟随系统</string>
<string name="go_back">上级</string> <string name="go_back">上级</string>
<string name="tone_colour">在线朗读音色</string> <string name="tone_colour">在线朗读音色</string>
<string name="select_count">(%d/%d)</string> <string name="select_count">(%1$d/%2$d)</string>
<string name="show_rss">显示订阅</string> <string name="show_rss">显示订阅</string>
<string name="service_stop">服务已停止</string> <string name="service_stop">服务已停止</string>
<string name="service_start">正在启动服务\n具体信息查看通知栏</string> <string name="service_start">正在启动服务\n具体信息查看通知栏</string>

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.61' ext.kotlin_version = '1.3.70'
repositories { repositories {
google() google()
jcenter() jcenter()

@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html # http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process. # Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings. # The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx1536m org.gradle.jvmargs=-Xmx2048m
# When configured, Gradle will run in incubating parallel mode. # When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit # This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects

Loading…
Cancel
Save