Merge pull request #475 from gedoor/master

添加字体引用规则
pull/481/head
Antecer 5 years ago committed by GitHub
commit 0c8182ea3a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      app/build.gradle
  2. 2
      app/src/main/assets/help/appHelp.md
  3. 25
      app/src/main/assets/help/bookSourcesHelp.md
  4. 0
      app/src/main/assets/help/httpTTSHelp.md
  5. 42
      app/src/main/assets/help/readMenuHelp.md
  6. 2
      app/src/main/assets/help/regexHelp.md
  7. 19
      app/src/main/assets/help/webDavHelp.md
  8. 14
      app/src/main/assets/updateLog.md
  9. 2
      app/src/main/java/io/legado/app/constant/AppConst.kt
  10. 12
      app/src/main/java/io/legado/app/constant/PreferKey.kt
  11. 5
      app/src/main/java/io/legado/app/data/entities/BookChapter.kt
  12. 3
      app/src/main/java/io/legado/app/data/entities/rule/ContentRule.kt
  13. 14
      app/src/main/java/io/legado/app/help/AppConfig.kt
  14. 40
      app/src/main/java/io/legado/app/help/BookHelp.kt
  15. 25
      app/src/main/java/io/legado/app/help/JsExtensions.kt
  16. 56
      app/src/main/java/io/legado/app/help/LocalConfig.kt
  17. 2
      app/src/main/java/io/legado/app/help/ReadBookConfig.kt
  18. 70
      app/src/main/java/io/legado/app/lib/dialogs/AlertBuilder.kt
  19. 40
      app/src/main/java/io/legado/app/lib/dialogs/AndroidAlertBuilder.kt
  20. 16
      app/src/main/java/io/legado/app/lib/dialogs/AndroidDialogs.kt
  21. 5
      app/src/main/java/io/legado/app/lib/dialogs/AndroidSelectors.kt
  22. 16
      app/src/main/java/io/legado/app/lib/dialogs/Dialogs.kt
  23. 16
      app/src/main/java/io/legado/app/lib/dialogs/Selectors.kt
  24. 22
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeByJSonPath.kt
  25. 20
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeByJSoup.kt
  26. 9
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeByXPath.kt
  27. 80
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
  28. 2
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
  29. 36
      app/src/main/java/io/legado/app/model/webBook/BookContent.kt
  30. 17
      app/src/main/java/io/legado/app/service/AudioPlayService.kt
  31. 6
      app/src/main/java/io/legado/app/service/CacheBookService.kt
  32. 7
      app/src/main/java/io/legado/app/service/help/CacheBook.kt
  33. 3
      app/src/main/java/io/legado/app/ui/about/AboutFragment.kt
  34. 7
      app/src/main/java/io/legado/app/ui/about/ReadRecordActivity.kt
  35. 188
      app/src/main/java/io/legado/app/ui/association/ImportBookSourceActivity.kt
  36. 156
      app/src/main/java/io/legado/app/ui/association/ImportBookSourceDialog.kt
  37. 39
      app/src/main/java/io/legado/app/ui/association/ImportBookSourceViewModel.kt
  38. 19
      app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleActivity.kt
  39. 161
      app/src/main/java/io/legado/app/ui/association/ImportRssSourceActivity.kt
  40. 155
      app/src/main/java/io/legado/app/ui/association/ImportRssSourceDialog.kt
  41. 37
      app/src/main/java/io/legado/app/ui/association/ImportRssSourceViewModel.kt
  42. 6
      app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt
  43. 9
      app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt
  44. 2
      app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt
  45. 4
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt
  46. 6
      app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
  47. 7
      app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt
  48. 2
      app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
  49. 2
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  50. 2
      app/src/main/java/io/legado/app/ui/book/local/ImportBookAdapter.kt
  51. 149
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  52. 165
      app/src/main/java/io/legado/app/ui/book/read/ReadBookBaseActivity.kt
  53. 10
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  54. 36
      app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt
  55. 8
      app/src/main/java/io/legado/app/ui/book/read/config/AutoReadDialog.kt
  56. 10
      app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt
  57. 3
      app/src/main/java/io/legado/app/ui/book/read/config/ChineseConverter.kt
  58. 132
      app/src/main/java/io/legado/app/ui/book/read/config/ClickActionConfigDialog.kt
  59. 16
      app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt
  60. 1
      app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudConfigDialog.kt
  61. 8
      app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt
  62. 4
      app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
  63. 8
      app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt
  64. 3
      app/src/main/java/io/legado/app/ui/book/read/config/TextFontWeightConverter.kt
  65. 7
      app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt
  66. 46
      app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
  67. 14
      app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
  68. 8
      app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt
  69. 87
      app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
  70. 52
      app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt
  71. 2
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt
  72. 6
      app/src/main/java/io/legado/app/ui/book/read/page/entities/PageData.kt
  73. 5
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChapter.kt
  74. 152
      app/src/main/java/io/legado/app/ui/book/read/page/provider/ChapterProvider.kt
  75. 4
      app/src/main/java/io/legado/app/ui/book/searchContent/SearchContentActivity.kt
  76. 32
      app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt
  77. 8
      app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditAdapter.kt
  78. 37
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  79. 7
      app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt
  80. 6
      app/src/main/java/io/legado/app/ui/book/toc/BookmarkFragment.kt
  81. 4
      app/src/main/java/io/legado/app/ui/book/toc/ChapterListAdapter.kt
  82. 2
      app/src/main/java/io/legado/app/ui/book/toc/ChapterListFragment.kt
  83. 32
      app/src/main/java/io/legado/app/ui/config/BackupConfigFragment.kt
  84. 10
      app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt
  85. 7
      app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
  86. 4
      app/src/main/java/io/legado/app/ui/config/ThemeListDialog.kt
  87. 9
      app/src/main/java/io/legado/app/ui/filepicker/FilePicker.kt
  88. 6
      app/src/main/java/io/legado/app/ui/main/MainActivity.kt
  89. 13
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
  90. 2
      app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt
  91. 7
      app/src/main/java/io/legado/app/ui/replace/GroupManageDialog.kt
  92. 9
      app/src/main/java/io/legado/app/ui/replace/ReplaceRuleActivity.kt
  93. 21
      app/src/main/java/io/legado/app/ui/replace/edit/ReplaceEditActivity.kt
  94. 41
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
  95. 21
      app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
  96. 7
      app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt
  97. 9
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
  98. 28
      app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt
  99. 7
      app/src/main/java/io/legado/app/ui/widget/text/StrokeTextView.kt
  100. 7
      app/src/main/java/io/legado/app/utils/EncoderUtils.kt
  101. Some files were not shown because too many files have changed in this diff Show More

@ -111,7 +111,7 @@ kapt {
} }
dependencies { dependencies {
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.0'
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
testImplementation 'junit:junit:4.13.1' testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test:runner:1.3.0' androidTestImplementation 'androidx.test:runner:1.3.0'

@ -2,7 +2,7 @@
## **新人必读** ## **新人必读**
【温馨提醒】 *更新前一定要做好备份,以免数据丢失!* 【温馨提醒】 *本帮助可以在我的-右上角帮助按钮再次打开,更新前一定要做好备份,以免数据丢失!*
1. 为什么第一次安装好之后什么东西都没有? 1. 为什么第一次安装好之后什么东西都没有?
* 阅读只是一个转码工具,不提供内容,第一次安装app,需要自己手动导入书源,可以从公众号[开源阅读]()、QQ群、酷安评论里获取由书友制作分享的书源。 * 阅读只是一个转码工具,不提供内容,第一次安装app,需要自己手动导入书源,可以从公众号[开源阅读]()、QQ群、酷安评论里获取由书友制作分享的书源。

@ -0,0 +1,25 @@
# 书源管理界面帮助
* 书源右上角标志
* 绿点表示书源有发现,且启用了发现
* 红点表示书源有发现,但是未启用
* 没有标志表示此书源没有发现
* 右上角有分组菜单,可以按分组筛选书源
* 右上角更多菜单里包含
* 新建书源
* 本地导入
* 网络导入
* 二维码导入
* 分享选中书源
* 选择书源的更多操作在右下角的菜单里面,操作都是针对选择的书源
* 启用所选
* 禁用所选
* 添加分组
* 移除分组
* 启用发现
* 禁用发现
* 置顶所选
* 置底所选
* 导出所选
* 校验所选
* 校验失败的书源分组会加上"失效",选择"失效"分组即可批量操作

@ -0,0 +1,42 @@
# 阅读界面帮助文档
1.阅读界面主菜单
* 顶部操作
* 章节名称:点击可编辑书源
* 章节url:点击可打开浏览器浏览
* 菜单:不同类型的书籍显示的菜单不同,详情查看菜单文字,长按菜单图标可显示文字
* 中间左侧-亮度调节
* 亮度调节的顶端有跟随系统亮度的开关,打开后亮度跟随系统,关闭后才可以调节亮度条
* 底部操作
* 4个圆形按钮依次为 全文搜索✧自动翻页✧替换净化✧切换夜间模式
* 上一章✧下一章中间的进度条为页数进度,要快速跳转章节点击目录按钮进入目录快速跳转
* 目录->目录和书签界面
* 朗读->单击开始朗读,长按进入朗读设置界面
* 界面->所有排版设置都在里面
* 设置->其它一些设置,找不到的设置去这里看看,可滚动
2.朗读设置界面
* 后台->进入后台朗读,可以做一些其它事
* 设置->朗读引擎设置,可以切换本地TTS和在线朗读,在线朗读可自定义
3.排版设置界面
* 白天模式和夜间模式背景不同布局相同
* 共用布局->启用共用布局时所有背景使用同一布局,关闭共用布局则每个背景单独布局
* 长按背景可进入文字颜色和背景设置界面
4.其它设置界面
* 屏幕方向
* 屏幕超时
* 隐藏状态栏
* 扩展到刘海
* 隐藏导航栏
* 文字两端对齐
* 文字底部对齐
* 音量键翻页
* 点击翻页
* 朗读时音量键翻页
* 自动换源->书源被删除时自动切换到其它书源
* 长按选择文本
* 显示亮度调节控件
* 点击区域设置
* 自定义翻页按键

@ -1,4 +1,4 @@
## 正则表达式学习 # 正则表达式学习
- [基本匹配](#1-基本匹配) - [基本匹配](#1-基本匹配)
- [元字符](#2-元字符) - [元字符](#2-元字符)

@ -0,0 +1,19 @@
# WebDav备份教程
### 阅读支持云备份,采用WebDav协议,所有支持WebDav的云盘都可以,建议采用坚果云,每月免费1G流量,用来备份阅读足够了,下面就采用坚果云来讲解配置步骤.
1. 打开坚果云网站 https://www.jianguoyun.com/d/home#/
2. 如果没有注册过坚果云先注册一下
3. 登录坚果云
4. 右上角用户名点开点账户信息
5. 点击安全选项
6. 在第三方管理里添加应用
7. 将应用示例里的服务器地址,用户名,和密码填到阅读的WebDav设置里
8. 阅读的WebDav配置在我的-备份与恢复,创建子文件夹选项保持默认即可
9. 设置完成后手动执行一下备份,看看是否成功
10. 恢复时选择想要恢复的备份文件
### 自动备份说明
* 设置好备份之后每次退出App会自动进行备份
* WebDav同一天的备份会覆盖,不同日期的备份不会覆盖

@ -3,6 +3,20 @@
* 关注合作公众号 **[小说拾遗]()** 获取好看的小说。 * 关注合作公众号 **[小说拾遗]()** 获取好看的小说。
* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
**2020/11/13**
* 正文规则添加字体规则,返回ByteArray
* js添加方法:
```
base64DecodeToByteArray(str: String?): ByteArray?
base64DecodeToByteArray(str: String?, flags: Int): ByteArray?
```
**2020/11/08**
* 优化书源,订阅源导入,添加保持原名选项
* 优化使用体验,初次使用是自动弹出帮助文档
* 添加阅读界面的点击区域设置
* 滚动翻页时自动翻页也采用滚动模式
**2020/11/07** **2020/11/07**
* 详情页菜单添加拷贝URL * 详情页菜单添加拷贝URL
* 解决一些书名太长缓存报错的bug * 解决一些书名太长缓存报错的bug

@ -38,7 +38,7 @@ object AppConst {
val keyboardToolChars: List<String> by lazy { val keyboardToolChars: List<String> by lazy {
arrayListOf( arrayListOf(
"", "@", "&", "|", "%", "/", ":", "[", "]", "{", "}", "<", ">", "\\", "", "@", "&", "|", "%", "/", ":", "[", "]", "{", "}", "<", ">", "\\",
"$", "#", "!", ".", "href", "src", "textNodes", "xpath", "json", "css", "$", "#", "!", ".", "href", "src", "textNodes", "xpath", "json", "css",
"id", "class", "tag" "id", "class", "tag"
) )

@ -5,8 +5,15 @@ object PreferKey {
const val language = "language" const val language = "language"
const val themeMode = "themeMode" const val themeMode = "themeMode"
const val hideStatusBar = "hideStatusBar" const val hideStatusBar = "hideStatusBar"
const val clickTurnPage = "clickTurnPage" const val clickActionTopLeft = "clickActionTopLeft"
const val clickAllNext = "clickAllNext" const val clickActionTopCenter = "clickActionTopCenter"
const val clickActionTopRight = "clickActionTopRight"
const val clickActionMiddleLeft = "clickActionMiddleLeft"
const val clickActionMiddleCenter = "clickActionMiddleCenter"
const val clickActionMiddleRight = "clickActionMiddleRight"
const val clickActionBottomLeft = "clickActionBottomLeft"
const val clickActionBottomCenter = "clickActionBottomCenter"
const val clickActionBottomRight = "clickActionBottomRight"
const val hideNavigationBar = "hideNavigationBar" const val hideNavigationBar = "hideNavigationBar"
const val precisionSearch = "precisionSearch" const val precisionSearch = "precisionSearch"
const val speakEngine = "speakEngine" const val speakEngine = "speakEngine"
@ -53,6 +60,7 @@ object PreferKey {
const val showBrightnessView = "showBrightnessView" const val showBrightnessView = "showBrightnessView"
const val autoClearExpired = "autoClearExpired" const val autoClearExpired = "autoClearExpired"
const val autoChangeSource = "autoChangeSource" const val autoChangeSource = "autoChangeSource"
const val importKeepName = "importKeepName"
const val cPrimary = "colorPrimary" const val cPrimary = "colorPrimary"
const val cAccent = "colorAccent" const val cAccent = "colorAccent"

@ -6,6 +6,7 @@ import androidx.room.ForeignKey
import androidx.room.Ignore import androidx.room.Ignore
import androidx.room.Index import androidx.room.Index
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.MD5Utils
import io.legado.app.utils.NetworkUtils import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.fromJsonObject import io.legado.app.utils.fromJsonObject
import kotlinx.android.parcel.IgnoredOnParcel import kotlinx.android.parcel.IgnoredOnParcel
@ -62,5 +63,9 @@ data class BookChapter(
fun getAbsoluteURL(): String { fun getAbsoluteURL(): String {
return NetworkUtils.getAbsoluteURL(baseUrl, url)!! return NetworkUtils.getAbsoluteURL(baseUrl, url)!!
} }
fun getFileName(): String = String.format("%05d-%s.nb", index, MD5Utils.md5Encode16(title))
fun getFontName(): String = String.format("%05d-%s.ttf", index, MD5Utils.md5Encode16(title))
} }

@ -10,5 +10,6 @@ data class ContentRule(
var webJs: String? = null, var webJs: String? = null,
var sourceRegex: String? = null, var sourceRegex: String? = null,
var replaceRegex: String? = null, var replaceRegex: String? = null,
var imageStyle: String? = null //默认大小居中,FULL最大宽度 var imageStyle: String? = null, //默认大小居中,FULL最大宽度
var font: String? = null //必须返回ByteArray
) : Parcelable ) : Parcelable

@ -134,5 +134,19 @@ object AppConfig {
val changeSourceLoadInfo get() = App.INSTANCE.getPrefBoolean(PreferKey.changeSourceLoadToc) val changeSourceLoadInfo get() = App.INSTANCE.getPrefBoolean(PreferKey.changeSourceLoadToc)
val changeSourceLoadToc get() = App.INSTANCE.getPrefBoolean(PreferKey.changeSourceLoadToc) val changeSourceLoadToc get() = App.INSTANCE.getPrefBoolean(PreferKey.changeSourceLoadToc)
val importKeepName get() = App.INSTANCE.getPrefBoolean(PreferKey.importKeepName)
val clickActionTopLeft get() = App.INSTANCE.getPrefInt(PreferKey.clickActionTopLeft, 2)
val clickActionTopCenter get() = App.INSTANCE.getPrefInt(PreferKey.clickActionTopCenter, 2)
val clickActionTopRight get() = App.INSTANCE.getPrefInt(PreferKey.clickActionTopRight, 1)
val clickActionMiddleLeft get() = App.INSTANCE.getPrefInt(PreferKey.clickActionMiddleLeft, 2)
val clickActionMiddleCenter
get() = App.INSTANCE.getPrefInt(PreferKey.clickActionMiddleCenter, 0)
val clickActionMiddleRight get() = App.INSTANCE.getPrefInt(PreferKey.clickActionMiddleRight, 1)
val clickActionBottomLeft get() = App.INSTANCE.getPrefInt(PreferKey.clickActionBottomLeft, 2)
val clickActionBottomCenter
get() = App.INSTANCE.getPrefInt(PreferKey.clickActionBottomCenter, 1)
val clickActionBottomRight get() = App.INSTANCE.getPrefInt(PreferKey.clickActionBottomRight, 1)
} }

@ -31,14 +31,6 @@ object BookHelp {
private val downloadDir: File = App.INSTANCE.externalFilesDir private val downloadDir: File = App.INSTANCE.externalFilesDir
private val downloadImages = CopyOnWriteArraySet<String>() private val downloadImages = CopyOnWriteArraySet<String>()
fun formatChapterName(bookChapter: BookChapter): String {
return String.format(
"%05d-%s.nb",
bookChapter.index,
MD5Utils.md5Encode16(bookChapter.title)
)
}
fun clearCache() { fun clearCache() {
FileUtils.deleteFile( FileUtils.deleteFile(
FileUtils.getPath(downloadDir, cacheFolderName) FileUtils.getPath(downloadDir, cacheFolderName)
@ -75,7 +67,7 @@ object BookHelp {
downloadDir, downloadDir,
cacheFolderName, cacheFolderName,
book.getFolderName(), book.getFolderName(),
formatChapterName(bookChapter), bookChapter.getFileName(),
).writeText(content) ).writeText(content)
//保存图片 //保存图片
content.split("\n").forEach { content.split("\n").forEach {
@ -91,6 +83,30 @@ object BookHelp {
postEvent(EventBus.SAVE_CONTENT, bookChapter) postEvent(EventBus.SAVE_CONTENT, bookChapter)
} }
fun saveFont(book: Book, bookChapter: BookChapter, font: ByteArray) {
FileUtils.createFileIfNotExist(
downloadDir,
cacheFolderName,
book.getFolderName(),
"font",
bookChapter.getFontName()
).writeBytes(font)
}
fun getFontPath(book: Book, bookChapter: BookChapter): String? {
val fontFile = FileUtils.getFile(
downloadDir,
cacheFolderName,
book.getFolderName(),
"font",
bookChapter.getFontName()
)
if (fontFile.exists()) {
return fontFile.absolutePath
}
return null
}
suspend fun saveImage(book: Book, src: String) { suspend fun saveImage(book: Book, src: String) {
while (downloadImages.contains(src)) { while (downloadImages.contains(src)) {
delay(100) delay(100)
@ -158,7 +174,7 @@ object BookHelp {
downloadDir, downloadDir,
cacheFolderName, cacheFolderName,
book.getFolderName(), book.getFolderName(),
formatChapterName(bookChapter) bookChapter.getFileName()
) )
} }
} }
@ -171,7 +187,7 @@ object BookHelp {
downloadDir, downloadDir,
cacheFolderName, cacheFolderName,
book.getFolderName(), book.getFolderName(),
formatChapterName(bookChapter) bookChapter.getFileName()
) )
if (file.exists()) { if (file.exists()) {
return file.readText() return file.readText()
@ -188,7 +204,7 @@ object BookHelp {
downloadDir, downloadDir,
cacheFolderName, cacheFolderName,
book.getFolderName(), book.getFolderName(),
formatChapterName(bookChapter) bookChapter.getFileName()
).delete() ).delete()
} }
} }

@ -121,10 +121,10 @@ interface JsExtensions {
.execute() .execute()
} }
/** /**
*js实现读取cookie *js实现读取cookie
*/ */
fun getCookie(tag: String, key: String? = null): String { fun getCookie(tag: String, key: String? = null): String {
val cookie = CookieStore.getCookie(tag) val cookie = CookieStore.getCookie(tag)
val cookieMap = CookieStore.cookieToMap(cookie) val cookieMap = CookieStore.cookieToMap(cookie)
return if (key != null) { return if (key != null) {
@ -132,7 +132,7 @@ interface JsExtensions {
} else { } else {
cookie cookie
} }
} }
/** /**
* js实现解码,不能删 * js实现解码,不能删
@ -191,4 +191,19 @@ interface JsExtensions {
fun htmlFormat(str: String): String { fun htmlFormat(str: String): String {
return str.htmlFormat() return str.htmlFormat()
} }
fun base64DecodeToByteArray(str: String?): ByteArray? {
if (str.isNullOrBlank()) {
return null
}
return Base64.decode(str, Base64.DEFAULT)
}
fun base64DecodeToByteArray(str: String?, flags: Int): ByteArray? {
if (str.isNullOrBlank()) {
return null
}
return Base64.decode(str, flags)
}
} }

@ -0,0 +1,56 @@
package io.legado.app.help
import android.content.Context
import androidx.core.content.edit
import io.legado.app.App
object LocalConfig {
private val localConfig =
App.INSTANCE.getSharedPreferences("local", Context.MODE_PRIVATE)
val isFirstOpenApp: Boolean
get() {
val value = localConfig.getBoolean("firstOpen", true)
if (value) {
localConfig.edit { putBoolean("firstOpen", false) }
}
return value
}
val isFirstRead: Boolean
get() {
val value = localConfig.getBoolean("firstRead", true)
if (value) {
localConfig.edit { putBoolean("firstRead", false) }
}
return value
}
val isFirstOpenBackup: Boolean
get() {
val value = localConfig.getBoolean("firstBackup", true)
if (value) {
localConfig.edit { putBoolean("firstBackup", false) }
}
return value
}
val isFirstReadMenuShow: Boolean
get() {
val value = localConfig.getBoolean("firstReadMenu", true)
if (value) {
localConfig.edit { putBoolean("firstReadMenu", false) }
}
return value
}
val isFirstOpenBookSources: Boolean
get() {
val value = localConfig.getBoolean("firstOpenBookSources", true)
if (value) {
localConfig.edit { putBoolean("firstOpenBookSources", false) }
}
return value
}
}

@ -153,8 +153,6 @@ object ReadBookConfig {
App.INSTANCE.putPrefBoolean(PreferKey.shareLayout, value) App.INSTANCE.putPrefBoolean(PreferKey.shareLayout, value)
} }
} }
val clickTurnPage get() = App.INSTANCE.getPrefBoolean(PreferKey.clickTurnPage, true)
val clickAllNext get() = App.INSTANCE.getPrefBoolean(PreferKey.clickAllNext, false)
val textFullJustify get() = App.INSTANCE.getPrefBoolean(PreferKey.textFullJustify, true) val textFullJustify get() = App.INSTANCE.getPrefBoolean(PreferKey.textFullJustify, true)
val textBottomJustify get() = App.INSTANCE.getPrefBoolean(PreferKey.textBottomJustify, true) val textBottomJustify get() = App.INSTANCE.getPrefBoolean(PreferKey.textBottomJustify, true)
var hideStatusBar = App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar) var hideStatusBar = App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar)

@ -1,19 +1,3 @@
/*
* Copyright 2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("NOTHING_TO_INLINE", "unused") @file:Suppress("NOTHING_TO_INLINE", "unused")
package io.legado.app.lib.dialogs package io.legado.app.lib.dialogs
@ -63,19 +47,34 @@ interface AlertBuilder<out D : DialogInterface> {
@Deprecated(NO_GETTER, level = ERROR) get @Deprecated(NO_GETTER, level = ERROR) get
fun positiveButton(buttonText: String, onClicked: ((dialog: DialogInterface) -> Unit)? = null) fun positiveButton(buttonText: String, onClicked: ((dialog: DialogInterface) -> Unit)? = null)
fun positiveButton(@StringRes buttonTextResource: Int, onClicked: ((dialog: DialogInterface) -> Unit)? = null) fun positiveButton(
@StringRes buttonTextResource: Int,
onClicked: ((dialog: DialogInterface) -> Unit)? = null
)
fun negativeButton(buttonText: String, onClicked: ((dialog: DialogInterface) -> Unit)? = null) fun negativeButton(buttonText: String, onClicked: ((dialog: DialogInterface) -> Unit)? = null)
fun negativeButton(@StringRes buttonTextResource: Int, onClicked: ((dialog: DialogInterface) -> Unit)? = null) fun negativeButton(
@StringRes buttonTextResource: Int,
onClicked: ((dialog: DialogInterface) -> Unit)? = null
)
fun neutralButton(buttonText: String, onClicked: ((dialog: DialogInterface) -> Unit)? = null) fun neutralButton(buttonText: String, onClicked: ((dialog: DialogInterface) -> Unit)? = null)
fun neutralButton(@StringRes buttonTextResource: Int, onClicked: ((dialog: DialogInterface) -> Unit)? = null) fun neutralButton(
@StringRes buttonTextResource: Int,
onClicked: ((dialog: DialogInterface) -> Unit)? = null
)
fun onCancelled(handler: (dialog: DialogInterface) -> Unit) fun onCancelled(handler: (dialog: DialogInterface) -> Unit)
fun onKeyPressed(handler: (dialog: DialogInterface, keyCode: Int, e: KeyEvent) -> Boolean) fun onKeyPressed(handler: (dialog: DialogInterface, keyCode: Int, e: KeyEvent) -> Boolean)
fun items(items: List<CharSequence>, onItemSelected: (dialog: DialogInterface, index: Int) -> Unit) fun onDismiss(handler: (dialog: DialogInterface) -> Unit)
fun items(
items: List<CharSequence>,
onItemSelected: (dialog: DialogInterface, index: Int) -> Unit
)
fun <T> items( fun <T> items(
items: List<T>, items: List<T>,
onItemSelected: (dialog: DialogInterface, item: T, index: Int) -> Unit onItemSelected: (dialog: DialogInterface, item: T, index: Int) -> Unit
@ -95,24 +94,25 @@ interface AlertBuilder<out D : DialogInterface> {
fun build(): D fun build(): D
fun show(): D fun show(): D
}
fun AlertBuilder<*>.customTitle(view: () -> View) {
customTitle = view()
}
fun AlertBuilder<*>.customView(view: () -> View) { fun customTitle(view: () -> View) {
customView = view() customTitle = view()
} }
fun customView(view: () -> View) {
customView = view()
}
inline fun AlertBuilder<*>.okButton(noinline handler: ((dialog: DialogInterface) -> Unit)? = null) = fun okButton(handler: ((dialog: DialogInterface) -> Unit)? = null) =
positiveButton(android.R.string.ok, handler) positiveButton(android.R.string.ok, handler)
inline fun AlertBuilder<*>.cancelButton(noinline handler: ((dialog: DialogInterface) -> Unit)? = null) = fun cancelButton(handler: ((dialog: DialogInterface) -> Unit)? = null) =
negativeButton(android.R.string.cancel, handler) negativeButton(android.R.string.cancel, handler)
inline fun AlertBuilder<*>.yesButton(noinline handler: ((dialog: DialogInterface) -> Unit)? = null) = fun yesButton(handler: ((dialog: DialogInterface) -> Unit)? = null) =
positiveButton(R.string.yes, handler) positiveButton(R.string.yes, handler)
inline fun AlertBuilder<*>.noButton(noinline handler: ((dialog: DialogInterface) -> Unit)? = null) = fun noButton(handler: ((dialog: DialogInterface) -> Unit)? = null) =
negativeButton(R.string.no, handler) negativeButton(R.string.no, handler)
}

@ -1,19 +1,3 @@
/*
* Copyright 2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.legado.app.lib.dialogs package io.legado.app.lib.dialogs
import android.content.Context import android.content.Context
@ -22,12 +6,11 @@ import android.graphics.drawable.Drawable
import android.view.KeyEvent import android.view.KeyEvent
import android.view.View import android.view.View
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import io.legado.app.utils.applyTint
import org.jetbrains.anko.internals.AnkoInternals import org.jetbrains.anko.internals.AnkoInternals
import org.jetbrains.anko.internals.AnkoInternals.NO_GETTER import org.jetbrains.anko.internals.AnkoInternals.NO_GETTER
import kotlin.DeprecationLevel.ERROR import kotlin.DeprecationLevel.ERROR
val Android: AlertBuilderFactory<AlertDialog> = ::AndroidAlertBuilder
internal class AndroidAlertBuilder(override val ctx: Context) : AlertBuilder<AlertDialog> { internal class AndroidAlertBuilder(override val ctx: Context) : AlertBuilder<AlertDialog> {
private val builder = AlertDialog.Builder(ctx) private val builder = AlertDialog.Builder(ctx)
@ -91,15 +74,28 @@ internal class AndroidAlertBuilder(override val ctx: Context) : AlertBuilder<Ale
builder.setNegativeButton(buttonTextResource) { dialog, _ -> onClicked?.invoke(dialog) } builder.setNegativeButton(buttonTextResource) { dialog, _ -> onClicked?.invoke(dialog) }
} }
override fun neutralButton(buttonText: String, onClicked: ((dialog: DialogInterface) -> Unit)?) { override fun neutralButton(
buttonText: String,
onClicked: ((dialog: DialogInterface) -> Unit)?
) {
builder.setNeutralButton(buttonText) { dialog, _ -> onClicked?.invoke(dialog) } builder.setNeutralButton(buttonText) { dialog, _ -> onClicked?.invoke(dialog) }
} }
override fun neutralButton(buttonTextResource: Int, onClicked: ((dialog: DialogInterface) -> Unit)?) { override fun neutralButton(
buttonTextResource: Int,
onClicked: ((dialog: DialogInterface) -> Unit)?
) {
builder.setNeutralButton(buttonTextResource) { dialog, _ -> onClicked?.invoke(dialog) } builder.setNeutralButton(buttonTextResource) { dialog, _ -> onClicked?.invoke(dialog) }
} }
override fun items(items: List<CharSequence>, onItemSelected: (dialog: DialogInterface, index: Int) -> Unit) { override fun onDismiss(handler: (dialog: DialogInterface) -> Unit) {
builder.setOnDismissListener(handler)
}
override fun items(
items: List<CharSequence>,
onItemSelected: (dialog: DialogInterface, index: Int) -> Unit
) {
builder.setItems(Array(items.size) { i -> items[i].toString() }) { dialog, which -> builder.setItems(Array(items.size) { i -> items[i].toString() }) { dialog, which ->
onItemSelected(dialog, which) onItemSelected(dialog, which)
} }
@ -136,5 +132,5 @@ internal class AndroidAlertBuilder(override val ctx: Context) : AlertBuilder<Ale
override fun build(): AlertDialog = builder.create() override fun build(): AlertDialog = builder.create()
override fun show(): AlertDialog = builder.show() override fun show(): AlertDialog = builder.show().applyTint()
} }

@ -1,19 +1,3 @@
/*
* Copyright 2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("NOTHING_TO_INLINE", "unused", "DEPRECATION") @file:Suppress("NOTHING_TO_INLINE", "unused", "DEPRECATION")
package io.legado.app.lib.dialogs package io.legado.app.lib.dialogs

@ -21,7 +21,6 @@ package io.legado.app.lib.dialogs
import android.content.Context import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import io.legado.app.utils.applyTint
inline fun Fragment.selector( inline fun Fragment.selector(
title: CharSequence? = null, title: CharSequence? = null,
@ -39,7 +38,7 @@ fun Context.selector(
this.title = title this.title = title
} }
items(items, onClick) items(items, onClick)
show().applyTint() show()
} }
} }
@ -53,6 +52,6 @@ fun Context.selector(
this.title = getString(titleSource) this.title = getString(titleSource)
} }
items(items, onClick) items(items, onClick)
show().applyTint() show()
} }
} }

@ -1,19 +1,3 @@
/*
* Copyright 2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("NOTHING_TO_INLINE", "unused") @file:Suppress("NOTHING_TO_INLINE", "unused")
package io.legado.app.lib.dialogs package io.legado.app.lib.dialogs

@ -1,19 +1,3 @@
/*
* Copyright 2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
@file:Suppress("NOTHING_TO_INLINE", "unused") @file:Suppress("NOTHING_TO_INLINE", "unused")
package io.legado.app.lib.dialogs package io.legado.app.lib.dialogs

@ -10,17 +10,22 @@ import java.util.regex.Pattern
@Suppress("RegExpRedundantEscape") @Suppress("RegExpRedundantEscape")
@Keep @Keep
class AnalyzeByJSonPath { class AnalyzeByJSonPath(json: Any) {
private lateinit var ctx: ReadContext
fun parse(json: Any): AnalyzeByJSonPath { companion object {
ctx = when (json) { private val jsonRulePattern = Pattern.compile("(?<=\\{)\\$\\..+?(?=\\})")
is String -> JsonPath.parse(json)
else -> JsonPath.parse(json) fun parse(json: Any): ReadContext {
return when (json) {
is ReadContext -> json
is String -> JsonPath.parse(json)
else -> JsonPath.parse(json)
}
} }
return this
} }
private var ctx: ReadContext = parse(json)
fun getString(rule: String): String? { fun getString(rule: String): String? {
if (TextUtils.isEmpty(rule)) return null if (TextUtils.isEmpty(rule)) return null
var result = "" var result = ""
@ -210,7 +215,4 @@ class AnalyzeByJSonPath {
return result return result
} }
companion object {
private val jsonRulePattern = Pattern.compile("(?<=\\{)\\$\\..+?(?=\\})")
}
} }

@ -17,22 +17,22 @@ import java.util.*
* 书源规则解析 * 书源规则解析
*/ */
@Keep @Keep
class AnalyzeByJSoup { class AnalyzeByJSoup(doc: Any) {
companion object { companion object {
val validKeys = arrayOf("class", "id", "tag", "text", "children") val validKeys = arrayOf("class", "id", "tag", "text", "children")
}
private lateinit var element: Element
fun parse(doc: Any): AnalyzeByJSoup { fun parse(doc: Any): Element {
element = when (doc) { return when (doc) {
is Element -> doc is Element -> doc
is JXNode -> if (doc.isElement) doc.asElement() else Jsoup.parse(doc.toString()) is JXNode -> if (doc.isElement) doc.asElement() else Jsoup.parse(doc.toString())
else -> Jsoup.parse(doc.toString()) else -> Jsoup.parse(doc.toString())
}
} }
return this
} }
private var element: Element = parse(doc)
/** /**
* 获取列表 * 获取列表
*/ */

@ -11,18 +11,17 @@ import org.seimicrawler.xpath.JXNode
import java.util.* import java.util.*
@Keep @Keep
class AnalyzeByXPath { class AnalyzeByXPath(doc: Any) {
private lateinit var jxNode: Any private var jxNode: Any = parse(doc)
fun parse(doc: Any): AnalyzeByXPath { private fun parse(doc: Any): Any {
jxNode = when (doc) { return when (doc) {
is JXNode -> if (doc.isElement) doc else strToJXDocument(doc.toString()) is JXNode -> if (doc.isElement) doc else strToJXDocument(doc.toString())
is Document -> JXDocument.create(doc) is Document -> JXDocument.create(doc)
is Element -> JXDocument.create(Elements(doc)) is Element -> JXDocument.create(Elements(doc))
is Elements -> JXDocument.create(doc) is Elements -> JXDocument.create(doc)
else -> strToJXDocument(doc.toString()) else -> strToJXDocument(doc.toString())
} }
return this
} }
private fun strToJXDocument(html: String): JXDocument { private fun strToJXDocument(html: String): JXDocument {

@ -65,17 +65,14 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
*/ */
private fun getAnalyzeByXPath(o: Any): AnalyzeByXPath { private fun getAnalyzeByXPath(o: Any): AnalyzeByXPath {
return if (o != content) { return if (o != content) {
AnalyzeByXPath().parse(o) AnalyzeByXPath(o)
} else getAnalyzeByXPath() } else {
} if (analyzeByXPath == null || objectChangedXP) {
analyzeByXPath = AnalyzeByXPath(content!!)
private fun getAnalyzeByXPath(): AnalyzeByXPath { objectChangedXP = false
if (analyzeByXPath == null || objectChangedXP) { }
analyzeByXPath = AnalyzeByXPath() analyzeByXPath!!
analyzeByXPath?.parse(content!!)
objectChangedXP = false
} }
return analyzeByXPath as AnalyzeByXPath
} }
/** /**
@ -83,17 +80,14 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
*/ */
private fun getAnalyzeByJSoup(o: Any): AnalyzeByJSoup { private fun getAnalyzeByJSoup(o: Any): AnalyzeByJSoup {
return if (o != content) { return if (o != content) {
AnalyzeByJSoup().parse(o) AnalyzeByJSoup(o)
} else getAnalyzeByJSoup() } else {
} if (analyzeByJSoup == null || objectChangedJS) {
analyzeByJSoup = AnalyzeByJSoup(content!!)
private fun getAnalyzeByJSoup(): AnalyzeByJSoup { objectChangedJS = false
if (analyzeByJSoup == null || objectChangedJS) { }
analyzeByJSoup = AnalyzeByJSoup() analyzeByJSoup!!
analyzeByJSoup?.parse(content!!)
objectChangedJS = false
} }
return analyzeByJSoup as AnalyzeByJSoup
} }
/** /**
@ -101,17 +95,14 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
*/ */
private fun getAnalyzeByJSonPath(o: Any): AnalyzeByJSonPath { private fun getAnalyzeByJSonPath(o: Any): AnalyzeByJSonPath {
return if (o != content) { return if (o != content) {
AnalyzeByJSonPath().parse(o) AnalyzeByJSonPath(o)
} else getAnalyzeByJSonPath() } else {
} if (analyzeByJSonPath == null || objectChangedJP) {
analyzeByJSonPath = AnalyzeByJSonPath(content!!)
private fun getAnalyzeByJSonPath(): AnalyzeByJSonPath { objectChangedJP = false
if (analyzeByJSonPath == null || objectChangedJP) { }
analyzeByJSonPath = AnalyzeByJSonPath() analyzeByJSonPath!!
analyzeByJSonPath?.parse(content!!)
objectChangedJP = false
} }
return analyzeByJSonPath as AnalyzeByJSonPath
} }
/** /**
@ -124,6 +115,7 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
return getStringList(ruleList, isUrl) return getStringList(ruleList, isUrl)
} }
@JvmOverloads
fun getStringList(ruleList: List<SourceRule>, isUrl: Boolean = false): List<String>? { fun getStringList(ruleList: List<SourceRule>, isUrl: Boolean = false): List<String>? {
var result: Any? = null var result: Any? = null
val content = this.content val content = this.content
@ -181,6 +173,7 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
/** /**
* 获取文本 * 获取文本
*/ */
@JvmOverloads
fun getString(ruleStr: String?, isUrl: Boolean = false): String { fun getString(ruleStr: String?, isUrl: Boolean = false): String {
if (TextUtils.isEmpty(ruleStr)) return "" if (TextUtils.isEmpty(ruleStr)) return ""
val ruleList = splitSourceRule(ruleStr) val ruleList = splitSourceRule(ruleStr)
@ -302,6 +295,33 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
return ArrayList() return ArrayList()
} }
fun getByteArray(ruleStr: String): ByteArray? {
if (ruleStr.isEmpty()) return null
val ruleList = splitSourceRule(ruleStr)
var result: Any? = null
content?.let { o ->
if (ruleList.isNotEmpty()) result = o
for (sourceRule in ruleList) {
putRule(sourceRule.putMap)
result?.let {
result = when (sourceRule.mode) {
Mode.Regex -> AnalyzeByRegex.getElements(
result.toString(),
sourceRule.rule.splitNotBlank("&&")
)
Mode.Js -> evalJS(sourceRule.rule, result)
Mode.Json -> getAnalyzeByJSonPath(it).getList(sourceRule.rule)
Mode.XPath -> getAnalyzeByXPath(it).getElements(sourceRule.rule)
else -> getAnalyzeByJSoup(it).getElements(sourceRule.rule)
}
if (sourceRule.replaceRegex.isNotEmpty()) {
result = replaceRegex(result.toString(), sourceRule)
}
}
}
}
return result as? ByteArray
}
/** /**
* 保存变量 * 保存变量

@ -45,6 +45,7 @@ class AnalyzeUrl(
headerMapF: Map<String, String>? = null headerMapF: Map<String, String>? = null
) : JsExtensions { ) : JsExtensions {
companion object { companion object {
private val splitUrlRegex = Regex(",\\s*(?=\\{)")
private val pagePattern = Pattern.compile("<(.*?)>") private val pagePattern = Pattern.compile("<(.*?)>")
private val jsonType = MediaType.parse("application/json; charset=utf-8") private val jsonType = MediaType.parse("application/json; charset=utf-8")
} }
@ -59,7 +60,6 @@ class AnalyzeUrl(
private var charset: String? = null private var charset: String? = null
private var requestBody: RequestBody? = null private var requestBody: RequestBody? = null
private var method = RequestMethod.GET private var method = RequestMethod.GET
private val splitUrlRegex = Regex(",\\s*(?=\\{)")
private var proxy: String? = null private var proxy: String? = null
init { init {

@ -6,6 +6,7 @@ import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.rule.ContentRule import io.legado.app.data.entities.rule.ContentRule
import io.legado.app.help.BookHelp
import io.legado.app.model.Debug import io.legado.app.model.Debug
import io.legado.app.model.analyzeRule.AnalyzeRule import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.analyzeRule.AnalyzeUrl
@ -33,6 +34,14 @@ object BookContent {
val content = StringBuilder() val content = StringBuilder()
val nextUrlList = arrayListOf(baseUrl) val nextUrlList = arrayListOf(baseUrl)
val contentRule = bookSource.getContentRule() val contentRule = bookSource.getContentRule()
contentRule.font?.let {
//todo 获取字体
val analyzeRule = AnalyzeRule(book)
analyzeRule.setContent(body).setBaseUrl(baseUrl)
analyzeRule.getByteArray(it)?.let { font ->
BookHelp.saveFont(book, bookChapter, font)
}
}
var contentData = analyzeContent( var contentData = analyzeContent(
book, baseUrl, body, contentRule, bookChapter, bookSource book, baseUrl, body, contentRule, bookChapter, bookSource
) )
@ -54,12 +63,10 @@ object BookContent {
ruleUrl = nextUrl, ruleUrl = nextUrl,
book = book, book = book,
headerMapF = bookSource.getHeaderMap() headerMapF = bookSource.getHeaderMap()
).getResponseAwait(bookSource.bookSourceUrl) ).getResponseAwait(bookSource.bookSourceUrl).body?.let { nextBody ->
.body?.let { nextBody -> contentData = analyzeContent(
contentData = book, nextUrl, nextBody, contentRule, bookChapter, bookSource, false
analyzeContent( )
book, nextUrl, nextBody, contentRule, bookChapter, bookSource, false
)
nextUrl = nextUrl =
if (contentData.nextUrl.isNotEmpty()) contentData.nextUrl[0] else "" if (contentData.nextUrl.isNotEmpty()) contentData.nextUrl[0] else ""
content.append(contentData.content).append("\n") content.append(contentData.content).append("\n")
@ -78,14 +85,12 @@ object BookContent {
ruleUrl = item.nextUrl, ruleUrl = item.nextUrl,
book = book, book = book,
headerMapF = bookSource.getHeaderMap() headerMapF = bookSource.getHeaderMap()
).getResponseAwait(bookSource.bookSourceUrl) ).getResponseAwait(bookSource.bookSourceUrl).body?.let {
.body?.let { contentData = analyzeContent(
contentData = book, item.nextUrl, it, contentRule, bookChapter, bookSource, false
analyzeContent( )
book, item.nextUrl, it, contentRule, bookChapter, bookSource, false item.content = contentData.content
) }
item.content = contentData.content
}
} }
} }
for (item in contentDataList) { for (item in contentDataList) {
@ -105,6 +110,9 @@ object BookContent {
Debug.log(bookSource.bookSourceUrl, "${bookChapter.title}") Debug.log(bookSource.bookSourceUrl, "${bookChapter.title}")
Debug.log(bookSource.bookSourceUrl, "┌获取正文内容") Debug.log(bookSource.bookSourceUrl, "┌获取正文内容")
Debug.log(bookSource.bookSourceUrl, "\n$contentStr") Debug.log(bookSource.bookSourceUrl, "\n$contentStr")
if (contentStr.isNotBlank()) {
BookHelp.saveContent(book, bookChapter, contentStr)
}
return contentStr return contentStr
} }

@ -24,7 +24,6 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.constant.Status import io.legado.app.constant.Status
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
import io.legado.app.help.IntentHelp import io.legado.app.help.IntentHelp
import io.legado.app.help.MediaHelp import io.legado.app.help.MediaHelp
import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.analyzeRule.AnalyzeUrl
@ -284,23 +283,25 @@ class AudioPlayService : BaseService(),
} }
private fun loadContent(chapter: BookChapter) { private fun loadContent(chapter: BookChapter) {
AudioPlay.book?.let { book -> val book = AudioPlay.book
AudioPlay.webBook?.getContent(book, chapter, scope = this) val webBook = AudioPlay.webBook
?.onSuccess(IO) { content -> if (book != null && webBook != null) {
webBook.getContent(book, chapter, scope = this@AudioPlayService)
.onSuccess { content ->
removeLoading(chapter.index)
if (content.isEmpty()) { if (content.isEmpty()) {
withContext(Main) { withContext(Main) {
toast("未获取到资源链接") toast("未获取到资源链接")
} }
removeLoading(chapter.index)
} else { } else {
BookHelp.saveContent(book, chapter, content)
contentLoadFinish(chapter, content) contentLoadFinish(chapter, content)
removeLoading(chapter.index)
} }
}?.onError { }.onError {
contentLoadFinish(chapter, it.localizedMessage ?: toString()) contentLoadFinish(chapter, it.localizedMessage ?: toString())
removeLoading(chapter.index) removeLoading(chapter.index)
} }
} else {
toast("book or source is null")
} }
} }

@ -20,7 +20,6 @@ import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.help.CacheBook import io.legado.app.service.help.CacheBook
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
@ -197,8 +196,7 @@ class CacheBookService : BaseService() {
CacheBook.addLog("getContentError${it.localizedMessage}") CacheBook.addLog("getContentError${it.localizedMessage}")
updateNotification("getContentError${it.localizedMessage}") updateNotification("getContentError${it.localizedMessage}")
} }
.onSuccess(IO) { content -> .onSuccess {
BookHelp.saveContent(book, bookChapter, content)
synchronized(this@CacheBookService) { synchronized(this@CacheBookService) {
downloadCount[book.bookUrl]?.increaseSuccess() downloadCount[book.bookUrl]?.increaseSuccess()
downloadCount[book.bookUrl]?.increaseFinished() downloadCount[book.bookUrl]?.increaseFinished()
@ -221,7 +219,7 @@ class CacheBookService : BaseService() {
downloadCount.remove(book.bookUrl) downloadCount.remove(book.bookUrl)
} }
} }
}.onFinally(IO) { }.onFinally {
postDownloading(true) postDownloading(true)
} }
} else { } else {

@ -7,11 +7,9 @@ import io.legado.app.R
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.CacheBookService import io.legado.app.service.CacheBookService
import io.legado.app.utils.msg import io.legado.app.utils.msg
import kotlinx.coroutines.Dispatchers.IO
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.CopyOnWriteArraySet
@ -76,10 +74,7 @@ object CacheBook {
} }
downloadMap[book.bookUrl]?.add(chapter.index) downloadMap[book.bookUrl]?.add(chapter.index)
webBook.getContent(book, chapter) webBook.getContent(book, chapter)
.onSuccess(IO) { content -> .onSuccess { content ->
if (content.isNotBlank()) {
BookHelp.saveContent(book, chapter, content)
}
if (ReadBook.book?.bookUrl == book.bookUrl) { if (ReadBook.book?.bookUrl == book.bookUrl) {
ReadBook.contentLoadFinish( ReadBook.contentLoadFinish(
book, book,

@ -11,7 +11,6 @@ import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.ui.widget.dialog.TextDialog import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.utils.applyTint
import io.legado.app.utils.openUrl import io.legado.app.utils.openUrl
import io.legado.app.utils.sendMail import io.legado.app.utils.sendMail
import io.legado.app.utils.sendToClip import io.legado.app.utils.sendToClip
@ -85,7 +84,7 @@ class AboutFragment : PreferenceFragmentCompat() {
} }
} }
} }
}.show().applyTint() }.show()
} }
private fun joinQQGroup(key: String): Boolean { private fun joinQQGroup(key: String): Boolean {

@ -9,9 +9,6 @@ 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.data.entities.ReadRecordShow import io.legado.app.data.entities.ReadRecordShow
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.okButton
import io.legado.app.utils.applyTint
import kotlinx.android.synthetic.main.activity_read_record.* import kotlinx.android.synthetic.main.activity_read_record.*
import kotlinx.android.synthetic.main.item_read_record.* import kotlinx.android.synthetic.main.item_read_record.*
import kotlinx.android.synthetic.main.item_read_record.view.* import kotlinx.android.synthetic.main.item_read_record.view.*
@ -41,7 +38,7 @@ class ReadRecordActivity : BaseActivity(R.layout.activity_read_record) {
initData() initData()
} }
noButton() noButton()
}.show().applyTint() }.show()
} }
} }
@ -83,7 +80,7 @@ class ReadRecordActivity : BaseActivity(R.layout.activity_read_record) {
} }
} }
noButton() noButton()
}.show().applyTint() }.show()
} }
} }
} }

@ -1,38 +1,13 @@
package io.legado.app.ui.association package io.legado.app.ui.association
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.Theme import io.legado.app.constant.Theme
import io.legado.app.data.entities.BookSource
import io.legado.app.help.IntentDataHelp import io.legado.app.help.IntentDataHelp
import io.legado.app.help.SourceHelp
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.okButton
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.applyTint
import io.legado.app.utils.getSize
import io.legado.app.utils.getViewModel import io.legado.app.utils.getViewModel
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.activity_translucence.* import kotlinx.android.synthetic.main.activity_translucence.*
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_source_import.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
@ -97,167 +72,14 @@ class ImportBookSourceActivity : VMBaseActivity<ImportBookSourceViewModel>(
private fun errorDialog(msg: String) { private fun errorDialog(msg: String) {
alert(getString(R.string.error), msg) { alert(getString(R.string.error), msg) {
okButton { } okButton { }
}.show().applyTint().setOnDismissListener { onDismiss {
finish() finish()
}
}
private fun successDialog() {
val bundle = Bundle()
val allSourceKey = IntentDataHelp.putData(viewModel.allSources, "source")
bundle.putString("allSourceKey", allSourceKey)
val checkStatusKey = IntentDataHelp.putData(viewModel.sourceCheckState, "check")
bundle.putString("checkStatusKey", checkStatusKey)
val selectStatusKey = IntentDataHelp.putData(viewModel.selectStatus, "select")
bundle.putString("selectStatusKey", selectStatusKey)
SourcesDialog().apply {
arguments = bundle
}.show(supportFragmentManager, "SourceDialog")
}
class SourcesDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
lateinit var adapter: SourcesAdapter
private var _groupName: String? = null
override fun onStart() {
super.onStart()
val dm = requireActivity().getSize()
dialog?.window?.setLayout(
(dm.widthPixels * 0.9).toInt(),
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.dialog_recycler_view, container)
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setTitle(R.string.import_book_source)
initMenu()
arguments?.let {
adapter = SourcesAdapter(requireContext())
val allSources =
IntentDataHelp.getData<ArrayList<BookSource>>(it.getString("allSourceKey"))
adapter.sourceCheckState =
IntentDataHelp.getData<ArrayList<Boolean>>(it.getString("checkStatusKey"))!!
adapter.selectStatus =
IntentDataHelp.getData<ArrayList<Boolean>>(it.getString("selectStatusKey"))!!
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.adapter = adapter
adapter.setItems(allSources)
tv_cancel.visible()
tv_cancel.onClick {
dismiss()
}
tv_ok.visible()
tv_ok.onClick {
importSelect()
dismiss()
}
} }
} }.show()
private fun initMenu() {
tool_bar.setOnMenuItemClickListener(this)
tool_bar.inflateMenu(R.menu.import_source)
}
@SuppressLint("InflateParams")
override fun onMenuItemClick(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_new_group -> {
alert(R.string.diy_edit_source_group) {
var editText: AutoCompleteTextView? = null
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view
}
}
okButton {
editText?.text?.toString()?.let { group ->
_groupName = group
item.title = getString(R.string.diy_edit_source_group_title, _groupName)
}
}
noButton { }
}.show().applyTint()
}
R.id.menu_select_all -> {
adapter.selectStatus.forEachIndexed { index, b ->
if (!b) {
adapter.selectStatus[index] = true
}
}
adapter.notifyDataSetChanged()
}
R.id.menu_un_select_all -> {
adapter.selectStatus.forEachIndexed { index, b ->
if (b) {
adapter.selectStatus[index] = false
}
}
adapter.notifyDataSetChanged()
}
}
return false
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
activity?.finish()
}
private fun importSelect() {
val selectSource = arrayListOf<BookSource>()
adapter.selectStatus.forEachIndexed { index, b ->
if (_groupName != null) {
adapter.getItem(index)!!.bookSourceGroup = _groupName
}
if (b) {
selectSource.add(adapter.getItem(index)!!)
}
}
SourceHelp.insertBookSource(*selectSource.toTypedArray())
}
} }
class SourcesAdapter(context: Context) : private fun successDialog() {
SimpleRecyclerAdapter<BookSource>(context, R.layout.item_source_import) { ImportBookSourceDialog().show(supportFragmentManager, "SourceDialog")
lateinit var sourceCheckState: ArrayList<Boolean>
lateinit var selectStatus: ArrayList<Boolean>
override fun convert(holder: ItemViewHolder, item: BookSource, payloads: MutableList<Any>) {
holder.itemView.apply {
cb_source_name.isChecked = selectStatus[holder.layoutPosition]
cb_source_name.text = item.bookSourceName
tv_source_state.text = if (sourceCheckState[holder.layoutPosition]) {
"已存在"
} else {
"新书源"
}
}
}
override fun registerListener(holder: ItemViewHolder) {
holder.itemView.apply {
cb_source_name.setOnCheckedChangeListener { buttonView, isChecked ->
if (buttonView.isPressed) {
selectStatus[holder.layoutPosition] = isChecked
}
}
}
}
} }
} }

@ -0,0 +1,156 @@
package io.legado.app.ui.association
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.BookSource
import io.legado.app.help.AppConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.putPrefBoolean
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_source_import.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
/**
* 导入书源弹出窗口
*/
class ImportBookSourceDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
val viewModel: ImportBookSourceViewModel
get() = getViewModelOfActivity(ImportBookSourceViewModel::class.java)
lateinit var adapter: SourcesAdapter
override fun onStart() {
super.onStart()
dialog?.window?.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.dialog_recycler_view, container)
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setTitle(R.string.import_book_source)
initMenu()
adapter = SourcesAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.adapter = adapter
adapter.setItems(viewModel.allSources)
tv_cancel.visible()
tv_cancel.onClick {
dismiss()
}
tv_ok.visible()
tv_ok.onClick {
viewModel.importSelect {
dismiss()
}
}
}
private fun initMenu() {
tool_bar.setOnMenuItemClickListener(this)
tool_bar.inflateMenu(R.menu.import_source)
tool_bar.menu.findItem(R.id.menu_Keep_original_name)?.isChecked = AppConfig.importKeepName
}
@SuppressLint("InflateParams")
override fun onMenuItemClick(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_new_group -> {
alert(R.string.diy_edit_source_group) {
var editText: AutoCompleteTextView? = null
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view
}
}
okButton {
editText?.text?.toString()?.let { group ->
viewModel.groupName = group
item.title = getString(R.string.diy_edit_source_group_title, group)
}
}
noButton()
}.show()
}
R.id.menu_select_all -> {
viewModel.selectStatus.forEachIndexed { index, b ->
if (!b) {
viewModel.selectStatus[index] = true
}
}
adapter.notifyDataSetChanged()
}
R.id.menu_un_select_all -> {
viewModel.selectStatus.forEachIndexed { index, b ->
if (b) {
viewModel.selectStatus[index] = false
}
}
adapter.notifyDataSetChanged()
}
R.id.menu_Keep_original_name -> {
item.isChecked = !item.isChecked
putPrefBoolean(PreferKey.importKeepName, item.isChecked)
}
}
return false
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
activity?.finish()
}
inner class SourcesAdapter(context: Context) :
SimpleRecyclerAdapter<BookSource>(context, R.layout.item_source_import) {
override fun convert(holder: ItemViewHolder, item: BookSource, payloads: MutableList<Any>) {
holder.itemView.apply {
cb_source_name.isChecked = viewModel.selectStatus[holder.layoutPosition]
cb_source_name.text = item.bookSourceName
tv_source_state.text = if (viewModel.checkSources[holder.layoutPosition] != null) {
"已存在"
} else {
"新书源"
}
}
}
override fun registerListener(holder: ItemViewHolder) {
holder.itemView.apply {
cb_source_name.setOnCheckedChangeListener { buttonView, isChecked ->
if (buttonView.isPressed) {
viewModel.selectStatus[holder.layoutPosition] = isChecked
}
}
}
}
}
}

@ -9,6 +9,8 @@ import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.help.AppConfig
import io.legado.app.help.SourceHelp
import io.legado.app.help.http.HttpHelper import io.legado.app.help.http.HttpHelper
import io.legado.app.help.storage.OldRule import io.legado.app.help.storage.OldRule
import io.legado.app.help.storage.Restore import io.legado.app.help.storage.Restore
@ -16,14 +18,41 @@ import io.legado.app.utils.*
import java.io.File import java.io.File
class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) { class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) {
var groupName: String? = null
val errorLiveData = MutableLiveData<String>() val errorLiveData = MutableLiveData<String>()
val successLiveData = MutableLiveData<Int>() val successLiveData = MutableLiveData<Int>()
val allSources = arrayListOf<BookSource>() val allSources = arrayListOf<BookSource>()
val sourceCheckState = arrayListOf<Boolean>() val checkSources = arrayListOf<BookSource?>()
val selectStatus = arrayListOf<Boolean>() val selectStatus = arrayListOf<Boolean>()
fun importSelect(finally: () -> Unit) {
execute {
val keepName = AppConfig.importKeepName
val selectSource = arrayListOf<BookSource>()
selectStatus.forEachIndexed { index, b ->
if (b) {
val source = allSources[index]
if (groupName != null) {
source.bookSourceGroup = groupName
}
if (keepName) {
checkSources[index]?.let {
source.bookSourceName = it.bookSourceName
source.bookSourceGroup = it.bookSourceGroup
}
}
selectSource.add(source)
}
}
SourceHelp.insertBookSource(*selectSource.toTypedArray())
}.onFinally {
finally.invoke()
}
}
fun importSourceFromFilePath(path: String) { fun importSourceFromFilePath(path: String) {
execute { execute {
val content = if (path.isContentScheme()) { val content = if (path.isContentScheme()) {
@ -106,9 +135,9 @@ class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) {
private fun comparisonSource() { private fun comparisonSource() {
execute { execute {
allSources.forEach { allSources.forEach {
val has = App.db.bookSourceDao().getBookSource(it.bookSourceUrl) != null val has = App.db.bookSourceDao().getBookSource(it.bookSourceUrl)
sourceCheckState.add(has) checkSources.add(has)
selectStatus.add(!has) selectStatus.add(has == null)
} }
successLiveData.postValue(allSources.size) successLiveData.postValue(allSources.size)
} }

@ -8,9 +8,6 @@ import io.legado.app.constant.Theme
import io.legado.app.data.entities.ReplaceRule import io.legado.app.data.entities.ReplaceRule
import io.legado.app.help.IntentDataHelp import io.legado.app.help.IntentDataHelp
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.okButton
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_translucence.* import kotlinx.android.synthetic.main.activity_translucence.*
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
@ -76,9 +73,10 @@ class ImportReplaceRuleActivity : VMBaseActivity<ImportReplaceRuleViewModel>(
private fun errorDialog(msg: String) { private fun errorDialog(msg: String) {
alert("导入出错", msg) { alert("导入出错", msg) {
okButton { } okButton { }
}.show().applyTint().setOnDismissListener { onDismiss {
finish() finish()
} }
}.show()
} }
private fun successDialog(allSource: ArrayList<ReplaceRule>) { private fun successDialog(allSource: ArrayList<ReplaceRule>) {
@ -86,11 +84,10 @@ class ImportReplaceRuleActivity : VMBaseActivity<ImportReplaceRuleViewModel>(
okButton { okButton {
App.db.replaceRuleDao().insert(*allSource.toTypedArray()) App.db.replaceRuleDao().insert(*allSource.toTypedArray())
} }
noButton { noButton()
onDismiss {
finish()
} }
}.show().applyTint().setOnDismissListener { }.show()
finish()
}
} }
} }

@ -1,33 +1,13 @@
package io.legado.app.ui.association package io.legado.app.ui.association
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.Theme import io.legado.app.constant.Theme
import io.legado.app.data.entities.RssSource
import io.legado.app.help.IntentDataHelp import io.legado.app.help.IntentDataHelp
import io.legado.app.help.SourceHelp
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.okButton
import io.legado.app.utils.applyTint
import io.legado.app.utils.getSize
import io.legado.app.utils.getViewModel import io.legado.app.utils.getViewModel
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.activity_translucence.* import kotlinx.android.synthetic.main.activity_translucence.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_source_import.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
class ImportRssSourceActivity : VMBaseActivity<ImportRssSourceViewModel>( class ImportRssSourceActivity : VMBaseActivity<ImportRssSourceViewModel>(
@ -91,145 +71,14 @@ class ImportRssSourceActivity : VMBaseActivity<ImportRssSourceViewModel>(
private fun errorDialog(msg: String) { private fun errorDialog(msg: String) {
alert(getString(R.string.error), msg) { alert(getString(R.string.error), msg) {
okButton { } okButton { }
}.show().applyTint().setOnDismissListener { onDismiss {
finish() finish()
}
}
private fun successDialog() {
val bundle = Bundle()
val allSourceKey = IntentDataHelp.putData(viewModel.allSources, "source")
bundle.putString("allSourceKey", allSourceKey)
val checkStatusKey = IntentDataHelp.putData(viewModel.sourceCheckState, "check")
bundle.putString("checkStatusKey", checkStatusKey)
val selectStatusKey = IntentDataHelp.putData(viewModel.selectStatus, "select")
bundle.putString("selectStatusKey", selectStatusKey)
SourcesDialog().apply {
arguments = bundle
}.show(supportFragmentManager, "SourceDialog")
}
class SourcesDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
lateinit var adapter: SourcesAdapter
override fun onStart() {
super.onStart()
val dm = requireActivity().getSize()
dialog?.window?.setLayout(
(dm.widthPixels * 0.9).toInt(),
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.dialog_recycler_view, container)
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.title = getString(R.string.import_rss_source)
initMenu()
arguments?.let {
adapter = SourcesAdapter(requireContext())
val allSources =
IntentDataHelp.getData<ArrayList<RssSource>>(it.getString("allSourceKey"))
adapter.sourceCheckState =
IntentDataHelp.getData<ArrayList<Boolean>>(it.getString("checkStatusKey"))!!
adapter.selectStatus =
IntentDataHelp.getData<ArrayList<Boolean>>(it.getString("selectStatusKey"))!!
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.adapter = adapter
adapter.setItems(allSources)
tv_cancel.visible()
tv_cancel.onClick {
dismiss()
}
tv_ok.visible()
tv_ok.onClick {
importSelect()
dismiss()
}
} }
} }.show()
private fun initMenu() {
tool_bar.setOnMenuItemClickListener(this)
tool_bar.inflateMenu(R.menu.import_source)
}
override fun onMenuItemClick(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_select_all -> {
adapter.selectStatus.forEachIndexed { index, b ->
if (!b) {
adapter.selectStatus[index] = true
}
}
adapter.notifyDataSetChanged()
}
R.id.menu_un_select_all -> {
adapter.selectStatus.forEachIndexed { index, b ->
if (b) {
adapter.selectStatus[index] = false
}
}
adapter.notifyDataSetChanged()
}
}
return false
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
activity?.finish()
}
private fun importSelect() {
val selectSource = arrayListOf<RssSource>()
adapter.selectStatus.forEachIndexed { index, b ->
if (b) {
selectSource.add(adapter.getItem(index)!!)
}
}
SourceHelp.insertRssSource(*selectSource.toTypedArray())
}
} }
class SourcesAdapter(context: Context) : private fun successDialog() {
SimpleRecyclerAdapter<RssSource>(context, R.layout.item_source_import) { ImportRssSourceDialog().show(supportFragmentManager, "SourceDialog")
lateinit var sourceCheckState: ArrayList<Boolean>
lateinit var selectStatus: ArrayList<Boolean>
override fun convert(holder: ItemViewHolder, item: RssSource, payloads: MutableList<Any>) {
holder.itemView.apply {
cb_source_name.isChecked = selectStatus[holder.layoutPosition]
cb_source_name.text = item.sourceName
tv_source_state.text = if (sourceCheckState[holder.layoutPosition]) {
"已存在"
} else {
"新订阅源"
}
}
}
override fun registerListener(holder: ItemViewHolder) {
holder.itemView.apply {
cb_source_name.setOnCheckedChangeListener { buttonView, isChecked ->
if (buttonView.isPressed) {
selectStatus[holder.layoutPosition] = isChecked
}
}
}
}
} }
} }

@ -0,0 +1,155 @@
package io.legado.app.ui.association
import android.annotation.SuppressLint
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.RssSource
import io.legado.app.help.AppConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.putPrefBoolean
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_source_import.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
/**
* 导入rss源弹出窗口
*/
class ImportRssSourceDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
val viewModel: ImportRssSourceViewModel
get() = getViewModelOfActivity(ImportRssSourceViewModel::class.java)
lateinit var adapter: SourcesAdapter
override fun onStart() {
super.onStart()
dialog?.window?.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.dialog_recycler_view, container)
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setTitle(R.string.import_rss_source)
initMenu()
adapter = SourcesAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
recycler_view.adapter = adapter
adapter.setItems(viewModel.allSources)
tv_cancel.visible()
tv_cancel.onClick {
dismiss()
}
tv_ok.visible()
tv_ok.onClick {
viewModel.importSelect {
dismiss()
}
}
}
private fun initMenu() {
tool_bar.setOnMenuItemClickListener(this)
tool_bar.inflateMenu(R.menu.import_source)
tool_bar.menu.findItem(R.id.menu_Keep_original_name)?.isChecked = AppConfig.importKeepName
}
@SuppressLint("InflateParams")
override fun onMenuItemClick(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_new_group -> {
alert(R.string.diy_edit_source_group) {
var editText: AutoCompleteTextView? = null
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view
}
}
okButton {
editText?.text?.toString()?.let { group ->
viewModel.groupName = group
item.title = getString(R.string.diy_edit_source_group_title, group)
}
}
noButton()
}.show()
}
R.id.menu_select_all -> {
viewModel.selectStatus.forEachIndexed { index, b ->
if (!b) {
viewModel.selectStatus[index] = true
}
}
adapter.notifyDataSetChanged()
}
R.id.menu_un_select_all -> {
viewModel.selectStatus.forEachIndexed { index, b ->
if (b) {
viewModel.selectStatus[index] = false
}
}
adapter.notifyDataSetChanged()
}
R.id.menu_Keep_original_name -> {
item.isChecked = !item.isChecked
putPrefBoolean(PreferKey.importKeepName, item.isChecked)
}
}
return false
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
activity?.finish()
}
inner class SourcesAdapter(context: Context) :
SimpleRecyclerAdapter<RssSource>(context, R.layout.item_source_import) {
override fun convert(holder: ItemViewHolder, item: RssSource, payloads: MutableList<Any>) {
holder.itemView.apply {
cb_source_name.isChecked = viewModel.selectStatus[holder.layoutPosition]
cb_source_name.text = item.sourceName
tv_source_state.text = if (viewModel.checkSources[holder.layoutPosition] != null) {
"已存在"
} else {
"新订阅源"
}
}
}
override fun registerListener(holder: ItemViewHolder) {
holder.itemView.apply {
cb_source_name.setOnCheckedChangeListener { buttonView, isChecked ->
if (buttonView.isPressed) {
viewModel.selectStatus[holder.layoutPosition] = isChecked
}
}
}
}
}
}

@ -9,21 +9,48 @@ import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.help.AppConfig
import io.legado.app.help.SourceHelp
import io.legado.app.help.http.HttpHelper import io.legado.app.help.http.HttpHelper
import io.legado.app.help.storage.Restore import io.legado.app.help.storage.Restore
import io.legado.app.utils.* import io.legado.app.utils.*
import java.io.File import java.io.File
class ImportRssSourceViewModel(app: Application) : BaseViewModel(app) { class ImportRssSourceViewModel(app: Application) : BaseViewModel(app) {
var groupName: String? = null
val errorLiveData = MutableLiveData<String>() val errorLiveData = MutableLiveData<String>()
val successLiveData = MutableLiveData<Int>() val successLiveData = MutableLiveData<Int>()
val allSources = arrayListOf<RssSource>() val allSources = arrayListOf<RssSource>()
val sourceCheckState = arrayListOf<Boolean>() val checkSources = arrayListOf<RssSource?>()
val selectStatus = arrayListOf<Boolean>() val selectStatus = arrayListOf<Boolean>()
fun importSelect(finally: () -> Unit) {
execute {
val keepName = AppConfig.importKeepName
val selectSource = arrayListOf<RssSource>()
selectStatus.forEachIndexed { index, b ->
if (b) {
val source = allSources[index]
if (groupName != null) {
source.sourceGroup = groupName
}
if (keepName) {
checkSources[index]?.let {
source.sourceName = it.sourceName
source.sourceGroup = it.sourceGroup
}
}
selectSource.add(source)
}
}
SourceHelp.insertRssSource(*selectSource.toTypedArray())
}.onFinally {
finally.invoke()
}
}
fun importSourceFromFilePath(path: String) { fun importSourceFromFilePath(path: String) {
execute { execute {
val content = if (path.isContentScheme()) { val content = if (path.isContentScheme()) {
@ -101,9 +128,9 @@ class ImportRssSourceViewModel(app: Application) : BaseViewModel(app) {
private fun comparisonSource() { private fun comparisonSource() {
execute { execute {
allSources.forEach { allSources.forEach {
val has = App.db.rssSourceDao().getByKey(it.sourceUrl) != null val has = App.db.rssSourceDao().getByKey(it.sourceUrl)
sourceCheckState.add(has) checkSources.add(has)
selectStatus.add(!has) selectStatus.add(has == null)
} }
successLiveData.postValue(allSources.size) successLiveData.postValue(allSources.size)
} }

@ -20,8 +20,6 @@ import io.legado.app.data.entities.Book
import io.legado.app.help.BlurTransformation import io.legado.app.help.BlurTransformation
import io.legado.app.help.ImageLoader import io.legado.app.help.ImageLoader
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.okButton
import io.legado.app.service.AudioPlayService import io.legado.app.service.AudioPlayService
import io.legado.app.service.help.AudioPlay import io.legado.app.service.help.AudioPlay
import io.legado.app.ui.book.changesource.ChangeSourceDialog import io.legado.app.ui.book.changesource.ChangeSourceDialog
@ -150,14 +148,14 @@ class AudioPlayActivity :
override fun finish() { override fun finish() {
AudioPlay.book?.let { AudioPlay.book?.let {
if (!AudioPlay.inBookshelf) { if (!AudioPlay.inBookshelf) {
this.alert(title = getString(R.string.add_to_shelf)) { alert(title = getString(R.string.add_to_shelf)) {
message = getString(R.string.check_add_bookshelf, it.name) message = getString(R.string.check_add_bookshelf, it.name)
okButton { okButton {
AudioPlay.inBookshelf = true AudioPlay.inBookshelf = true
setResult(Activity.RESULT_OK) setResult(Activity.RESULT_OK)
} }
noButton { viewModel.removeFromBookshelf { super.finish() } } noButton { viewModel.removeFromBookshelf { super.finish() } }
}.show().applyTint() }.show()
} else { } else {
super.finish() super.finish()
} }

@ -15,8 +15,6 @@ import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
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.okButton
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.ui.book.group.GroupManageDialog import io.legado.app.ui.book.group.GroupManageDialog
import io.legado.app.ui.book.group.GroupSelectDialog import io.legado.app.ui.book.group.GroupSelectDialog
@ -24,7 +22,6 @@ import io.legado.app.ui.widget.SelectActionBar
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
import io.legado.app.ui.widget.recycler.ItemTouchCallback import io.legado.app.ui.widget.recycler.ItemTouchCallback
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.getPrefInt import io.legado.app.utils.getPrefInt
import io.legado.app.utils.getViewModel import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_arrange_book.* import kotlinx.android.synthetic.main.activity_arrange_book.*
@ -149,8 +146,8 @@ class ArrangeBookActivity : VMBaseActivity<ArrangeBookViewModel>(R.layout.activi
R.id.menu_del_selection -> R.id.menu_del_selection ->
alert(titleResource = R.string.draw, messageResource = R.string.sure_del) { alert(titleResource = R.string.draw, messageResource = R.string.sure_del) {
okButton { viewModel.deleteBook(*adapter.selectedBooks()) } okButton { viewModel.deleteBook(*adapter.selectedBooks()) }
noButton { } noButton()
}.show().applyTint() }.show()
R.id.menu_update_enable -> R.id.menu_update_enable ->
viewModel.upCanUpdate(adapter.selectedBooks(), true) viewModel.upCanUpdate(adapter.selectedBooks(), true)
R.id.menu_update_disable -> R.id.menu_update_disable ->
@ -210,7 +207,7 @@ class ArrangeBookActivity : VMBaseActivity<ArrangeBookViewModel>(R.layout.activi
okButton { okButton {
viewModel.deleteBook(book) viewModel.deleteBook(book)
} }
}.show().applyTint() }.show()
} }
} }

@ -151,7 +151,7 @@ class CacheActivity : VMBaseActivity<CacheViewModel>(R.layout.activity_download)
val chapterCaches = hashSetOf<String>() val chapterCaches = hashSetOf<String>()
val cacheNames = BookHelp.getChapterFiles(book) val cacheNames = BookHelp.getChapterFiles(book)
App.db.bookChapterDao().getChapterList(book.bookUrl).forEach { chapter -> App.db.bookChapterDao().getChapterList(book.bookUrl).forEach { chapter ->
if (cacheNames.contains(BookHelp.formatChapterName(chapter))) { if (cacheNames.contains(chapter.getFileName())) {
chapterCaches.add(chapter.url) chapterCaches.add(chapter.url)
} }
} }

@ -157,7 +157,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
webBook.getBookInfo(book, this) webBook.getBookInfo(book, this)
.onSuccess { .onSuccess {
if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) { if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
loadChapter(webBook, book) loadBookToc(webBook, book)
} else { } else {
//从详情页里获取最新章节 //从详情页里获取最新章节
book.latestChapterTitle = it.latestChapterTitle book.latestChapterTitle = it.latestChapterTitle
@ -169,7 +169,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
} }
} }
private fun loadChapter(webBook: WebBook, book: Book) { private fun loadBookToc(webBook: WebBook, book: Book) {
webBook.getChapterList(book, this) webBook.getChapterList(book, this)
.onSuccess(IO) { chapters -> .onSuccess(IO) { chapters ->
if (chapters.isNotEmpty()) { if (chapters.isNotEmpty()) {

@ -114,7 +114,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
} }
} }
noButton() noButton()
}.show().applyTint().requestInputMethod() }.show().requestInputMethod()
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
@ -133,7 +133,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
viewModel.upGroup(bookGroup.copy(groupName = editText?.text?.toString() ?: "")) viewModel.upGroup(bookGroup.copy(groupName = editText?.text?.toString() ?: ""))
} }
noButton() noButton()
}.show().applyTint().requestInputMethod() }.show().requestInputMethod()
} }
private fun deleteGroup(bookGroup: BookGroup) { private fun deleteGroup(bookGroup: BookGroup) {
@ -142,7 +142,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
viewModel.delGroup(bookGroup) viewModel.delGroup(bookGroup)
} }
noButton() noButton()
}.show().applyTint() }.show()
} }
private class GroupDiffCallBack( private class GroupDiffCallBack(

@ -20,9 +20,6 @@ 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.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
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.lib.theme.backgroundColor import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
@ -139,7 +136,7 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
} }
} }
noButton() noButton()
}.show().applyTint().requestInputMethod() }.show().requestInputMethod()
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
@ -158,7 +155,7 @@ class GroupSelectDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
viewModel.upGroup(bookGroup.copy(groupName = editText?.text?.toString() ?: "")) viewModel.upGroup(bookGroup.copy(groupName = editText?.text?.toString() ?: ""))
} }
noButton() noButton()
}.show().applyTint().requestInputMethod() }.show().requestInputMethod()
} }
private inner class GroupAdapter(context: Context) : private inner class GroupAdapter(context: Context) :

@ -65,6 +65,7 @@ class BookInfoActivity :
scroll_view.setBackgroundColor(backgroundColor) scroll_view.setBackgroundColor(backgroundColor)
fl_action.setBackgroundColor(bottomBackground) fl_action.setBackgroundColor(bottomBackground)
tv_shelf.setTextColor(getPrimaryTextColor(ColorUtils.isColorLight(bottomBackground))) tv_shelf.setTextColor(getPrimaryTextColor(ColorUtils.isColorLight(bottomBackground)))
tv_toc.text = getString(R.string.toc_s, getString(R.string.loading))
viewModel.bookData.observe(this, { showBook(it) }) viewModel.bookData.observe(this, { showBook(it) })
viewModel.chapterListData.observe(this, { upLoading(false, it) }) viewModel.chapterListData.observe(this, { upLoading(false, it) })
viewModel.initData(intent) viewModel.initData(intent)
@ -136,7 +137,6 @@ class BookInfoActivity :
tv_author.text = getString(R.string.author_show, book.getRealAuthor()) tv_author.text = getString(R.string.author_show, book.getRealAuthor())
tv_origin.text = getString(R.string.origin_show, book.originName) tv_origin.text = getString(R.string.origin_show, book.originName)
tv_lasted.text = getString(R.string.lasted_show, book.latestChapterTitle) tv_lasted.text = getString(R.string.lasted_show, book.latestChapterTitle)
tv_toc.text = getString(R.string.toc_s, getString(R.string.loading))
tv_intro.text = book.getDisplayIntro() tv_intro.text = book.getDisplayIntro()
upTvBookshelf() upTvBookshelf()
val kinds = book.getKindList() val kinds = book.getKindList()

@ -161,7 +161,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
) )
book.durChapterTitle = chapters[book.durChapterIndex].title book.durChapterTitle = chapters[book.durChapterIndex].title
if (inBookshelf) { if (inBookshelf) {
App.db.bookDao().insert(book) App.db.bookDao().update(book)
App.db.bookChapterDao().insert(*chapters.toTypedArray()) App.db.bookChapterDao().insert(*chapters.toTypedArray())
} }
bookData.postValue(book) bookData.postValue(book)

@ -113,7 +113,7 @@ class ImportBookAdapter(context: Context, val callBack: CallBack) :
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
if (it.isDir) { if (it.isDir) {
callBack.nextDoc(it.uri) callBack.nextDoc(it.uri)
} else if (!bookFileNames.contains(it.uri.toString())) { } else if (!bookFileNames.contains(it.name)) {
if (!selectedUris.contains(it.uri.toString())) { if (!selectedUris.contains(it.uri.toString())) {
selectedUris.add(it.uri.toString()) selectedUris.add(it.uri.toString())
} else { } else {

@ -4,9 +4,6 @@ import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
@ -18,21 +15,15 @@ import androidx.core.view.size
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
import io.legado.app.BuildConfig import io.legado.app.BuildConfig
import io.legado.app.R import io.legado.app.R
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.constant.Status import io.legado.app.constant.Status
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
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.storage.Backup 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.okButton
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.receiver.TimeBatteryReceiver import io.legado.app.receiver.TimeBatteryReceiver
import io.legado.app.service.BaseReadAloudService import io.legado.app.service.BaseReadAloudService
@ -48,7 +39,6 @@ import io.legado.app.ui.book.read.page.PageView
import io.legado.app.ui.book.read.page.TextPageFactory import io.legado.app.ui.book.read.page.TextPageFactory
import io.legado.app.ui.book.read.page.delegate.PageDelegate import io.legado.app.ui.book.read.page.delegate.PageDelegate
import io.legado.app.ui.book.searchContent.SearchContentActivity import io.legado.app.ui.book.searchContent.SearchContentActivity
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.book.toc.ChapterListActivity import io.legado.app.ui.book.toc.ChapterListActivity
import io.legado.app.ui.login.SourceLogin import io.legado.app.ui.login.SourceLogin
import io.legado.app.ui.replace.ReplaceRuleActivity import io.legado.app.ui.replace.ReplaceRuleActivity
@ -63,12 +53,11 @@ import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivity
import org.jetbrains.anko.startActivityForResult import org.jetbrains.anko.startActivityForResult
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_book_read), class ReadBookActivity : ReadBookBaseActivity(),
View.OnTouchListener, View.OnTouchListener,
PageView.CallBack, PageView.CallBack,
TextActionMenu.CallBack, TextActionMenu.CallBack,
@ -81,21 +70,17 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
TocRegexDialog.CallBack, TocRegexDialog.CallBack,
ColorPickerDialogListener { ColorPickerDialogListener {
private val requestCodeChapterList = 568 private val requestCodeChapterList = 568
private val requestCodeEditSource = 111
private val requestCodeReplace = 312 private val requestCodeReplace = 312
private val requestCodeSearchResult = 123 private val requestCodeSearchResult = 123
private var menu: Menu? = null private var menu: Menu? = null
private var textActionMenu: TextActionMenu? = null private var textActionMenu: TextActionMenu? = null
override val viewModel: ReadBookViewModel
get() = getViewModel(ReadBookViewModel::class.java)
override val scope: CoroutineScope get() = this override val scope: CoroutineScope get() = this
override val isInitFinish: Boolean get() = viewModel.isInitFinish override val isInitFinish: Boolean get() = viewModel.isInitFinish
override val isScroll: Boolean get() = page_view.isScroll override val isScroll: Boolean get() = page_view.isScroll
private val mHandler = Handler(Looper.getMainLooper()) private val mHandler = Handler(Looper.getMainLooper())
private val keepScreenRunnable: Runnable = private val keepScreenRunnable: Runnable =
Runnable { ReadBookActivityHelp.keepScreenOn(window, false) } Runnable { keepScreenOn(window, false) }
private val autoPageRunnable: Runnable = Runnable { autoPagePlus() } private val autoPageRunnable: Runnable = Runnable { autoPagePlus() }
override var autoPageProgress = 0 override var autoPageProgress = 0
override var isAutoPage = false override var isAutoPage = false
@ -105,15 +90,12 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
override val pageFactory: TextPageFactory get() = page_view.pageFactory override val pageFactory: TextPageFactory get() = page_view.pageFactory
override val headerHeight: Int get() = page_view.curPage.headerHeight override val headerHeight: Int get() = page_view.curPage.headerHeight
override fun onCreate(savedInstanceState: Bundle?) {
ReadBook.msg = null
ReadBookActivityHelp.setOrientation(this)
super.onCreate(savedInstanceState)
}
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
ReadBookActivityHelp.upLayoutInDisplayCutoutMode(window) super.onActivityCreated(savedInstanceState)
initView() cursor_left.setColorFilter(accentColor)
cursor_right.setColorFilter(accentColor)
cursor_left.setOnTouchListener(this)
cursor_right.setOnTouchListener(this)
upScreenTimeOut() upScreenTimeOut()
ReadBook.callBack = this ReadBook.callBack = this
ReadBook.titleDate.observe(this) { ReadBook.titleDate.observe(this) {
@ -157,55 +139,6 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
} }
} }
override fun upNavigationBarColor() {
when {
read_menu == null -> return
read_menu.isVisible -> {
ATH.setNavigationBarColorAuto(this)
}
ReadBookConfig.bg is ColorDrawable -> {
ATH.setNavigationBarColorAuto(this, ReadBookConfig.bgMeanColor)
}
else -> {
ATH.setNavigationBarColorAuto(this, Color.BLACK)
}
}
}
/**
* 初始化View
*/
private fun initView() {
cursor_left.setColorFilter(accentColor)
cursor_right.setColorFilter(accentColor)
cursor_left.setOnTouchListener(this)
cursor_right.setOnTouchListener(this)
tv_chapter_name.onClick {
ReadBook.webBook?.let {
startActivityForResult<BookSourceEditActivity>(
requestCodeEditSource,
Pair("data", it.bookSource.bookSourceUrl)
)
}
}
tv_chapter_url.onClick {
runCatching {
val url = tv_chapter_url.text.toString()
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(url)
startActivity(intent)
}
}
}
fun showPaddingConfig() {
PaddingConfigDialog().show(supportFragmentManager, "paddingConfig")
}
fun showBgTextConfig() {
BgTextConfigDialog().show(supportFragmentManager, "bgTextConfig")
}
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean { override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.read_book, menu) menuInflater.inflate(R.menu.read_book, menu)
return super.onCompatCreateOptionsMenu(menu) return super.onCompatCreateOptionsMenu(menu)
@ -257,8 +190,8 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
viewModel.refreshContent(it) viewModel.refreshContent(it)
} }
} }
R.id.menu_download -> ReadBookActivityHelp.showDownloadDialog(this) R.id.menu_download -> showDownloadDialog()
R.id.menu_add_bookmark -> ReadBookActivityHelp.showBookMark(this) R.id.menu_add_bookmark -> showBookMark()
R.id.menu_copy_text -> R.id.menu_copy_text ->
TextDialog.show(supportFragmentManager, ReadBook.curTextChapter?.getContent()) TextDialog.show(supportFragmentManager, ReadBook.curTextChapter?.getContent())
R.id.menu_update_toc -> ReadBook.book?.let { R.id.menu_update_toc -> ReadBook.book?.let {
@ -267,14 +200,14 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
R.id.menu_enable_replace -> ReadBook.book?.let { R.id.menu_enable_replace -> ReadBook.book?.let {
it.setUseReplaceRule(!it.getUseReplaceRule()) it.setUseReplaceRule(!it.getUseReplaceRule())
menu?.findItem(R.id.menu_enable_replace)?.isChecked = it.getUseReplaceRule() menu?.findItem(R.id.menu_enable_replace)?.isChecked = it.getUseReplaceRule()
onReplaceRuleSave() viewModel.replaceRuleChanged()
} }
R.id.menu_re_segment -> ReadBook.book?.let { R.id.menu_re_segment -> ReadBook.book?.let {
it.setReSegment(!it.getReSegment()) it.setReSegment(!it.getReSegment())
menu?.findItem(R.id.menu_re_segment)?.isChecked = it.getReSegment() menu?.findItem(R.id.menu_re_segment)?.isChecked = it.getReSegment()
ReadBook.loadContent(false) ReadBook.loadContent(false)
} }
R.id.menu_page_anim -> ReadBookActivityHelp.showPageAnimConfig(this) { R.id.menu_page_anim -> showPageAnimConfig {
page_view.upPageAnim() page_view.upPageAnim()
} }
R.id.menu_book_info -> ReadBook.book?.let { R.id.menu_book_info -> ReadBook.book?.let {
@ -293,7 +226,8 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
Pair("loginUrl", it.loginUrl) Pair("loginUrl", it.loginUrl)
) )
} }
R.id.menu_set_charset -> ReadBookActivityHelp.showCharsetConfig(this) R.id.menu_set_charset -> showCharsetConfig()
R.id.menu_help -> showReadMenuHelp()
} }
return super.onCompatOptionsItemSelected(item) return super.onCompatOptionsItemSelected(item)
} }
@ -324,13 +258,13 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
*/ */
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
when { when {
ReadBookActivityHelp.isPrevKey(this, keyCode) -> { isPrevKey(keyCode) -> {
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
page_view.pageDelegate?.keyTurnPage(PageDelegate.Direction.PREV) page_view.pageDelegate?.keyTurnPage(PageDelegate.Direction.PREV)
return true return true
} }
} }
ReadBookActivityHelp.isNextKey(this, keyCode) -> { isNextKey(keyCode) -> {
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
page_view.pageDelegate?.keyTurnPage(PageDelegate.Direction.NEXT) page_view.pageDelegate?.keyTurnPage(PageDelegate.Direction.NEXT)
return true return true
@ -622,7 +556,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
viewModel.changeTo(book) viewModel.changeTo(book)
} }
override fun clickCenter() { override fun showActionMenu() {
when { when {
BaseReadAloudService.isRun -> { BaseReadAloudService.isRun -> {
showReadAloudDialog() showReadAloudDialog()
@ -636,6 +570,11 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
} }
} }
override fun showReadMenuHelp() {
val text = String(assets.open("help/readMenuHelp.md").readBytes())
TextDialog.show(supportFragmentManager, text, TextDialog.MD)
}
/** /**
* 显示朗读菜单 * 显示朗读菜单
*/ */
@ -647,6 +586,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
* 自动翻页 * 自动翻页
*/ */
override fun autoPage() { override fun autoPage() {
ReadAloud.stop(this)
if (isAutoPage) { if (isAutoPage) {
autoPageStop() autoPageStop()
} else { } else {
@ -666,14 +606,18 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
private fun autoPagePlus() { private fun autoPagePlus() {
mHandler.removeCallbacks(autoPageRunnable) mHandler.removeCallbacks(autoPageRunnable)
autoPageProgress++ if (page_view.isScroll) {
if (autoPageProgress >= ReadBookConfig.autoReadSpeed * 10) { page_view.curPage.scroll(-page_view.height / ReadBookConfig.autoReadSpeed / 50)
autoPageProgress = 0
page_view.fillPage(PageDelegate.Direction.NEXT)
} else { } else {
page_view.invalidate() autoPageProgress++
if (autoPageProgress >= ReadBookConfig.autoReadSpeed * 50) {
autoPageProgress = 0
page_view.fillPage(PageDelegate.Direction.NEXT)
} else {
page_view.invalidate()
}
} }
mHandler.postDelayed(autoPageRunnable, 100) mHandler.postDelayed(autoPageRunnable, 20)
} }
/** /**
@ -708,16 +652,6 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
} }
} }
/**
* 替换规则变化
*/
private fun onReplaceRuleSave() {
Coroutine.async {
BookHelp.upReplaceRules()
ReadBook.loadContent(resetPageOffset = false)
}
}
/** /**
* 显示阅读样式配置 * 显示阅读样式配置
*/ */
@ -736,7 +670,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
* 更新状态栏,导航栏 * 更新状态栏,导航栏
*/ */
override fun upSystemUiVisibility() { override fun upSystemUiVisibility() {
ReadBookActivityHelp.upSystemUiVisibility(this, isInMultiWindow, !read_menu.isVisible) upSystemUiVisibility(isInMultiWindow, !read_menu.isVisible)
upNavigationBarColor() upNavigationBarColor()
} }
@ -744,6 +678,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
* 朗读按钮 * 朗读按钮
*/ */
override fun onClickReadAloud() { override fun onClickReadAloud() {
autoPageStop()
when { when {
!BaseReadAloudService.isRun -> ReadBook.readAloud() !BaseReadAloudService.isRun -> ReadBook.readAloud()
BaseReadAloudService.pause -> ReadAloud.resume(this) BaseReadAloudService.pause -> ReadAloud.resume(this)
@ -784,7 +719,6 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) { if (resultCode == Activity.RESULT_OK) {
when (requestCode) { when (requestCode) {
requestCodeEditSource -> viewModel.upBookSource()
requestCodeChapterList -> requestCodeChapterList ->
data?.getIntExtra("index", ReadBook.durChapterIndex)?.let { index -> data?.getIntExtra("index", ReadBook.durChapterIndex)?.let { index ->
if (index != ReadBook.durChapterIndex) { if (index != ReadBook.durChapterIndex) {
@ -840,23 +774,22 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
} }
} }
} }
requestCodeReplace -> onReplaceRuleSave() requestCodeReplace -> viewModel.replaceRuleChanged()
} }
} }
} }
override fun finish() { override fun finish() {
ReadBook.book?.let { ReadBook.book?.let {
if (!ReadBook.inBookshelf) { if (!ReadBook.inBookshelf) {
this.alert(title = getString(R.string.add_to_shelf)) { alert(title = getString(R.string.add_to_shelf)) {
message = getString(R.string.check_add_bookshelf, it.name) message = getString(R.string.check_add_bookshelf, it.name)
okButton { okButton {
ReadBook.inBookshelf = true ReadBook.inBookshelf = true
setResult(Activity.RESULT_OK) setResult(Activity.RESULT_OK)
} }
noButton { viewModel.removeFromBookshelf { super.finish() } } noButton { viewModel.removeFromBookshelf { super.finish() } }
}.show().applyTint() }.show()
} else { } else {
super.finish() super.finish()
} }
@ -934,7 +867,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
read_menu.upBrightnessState() read_menu.upBrightnessState()
} }
observeEvent<String>(EventBus.REPLACE_RULE_SAVE) { observeEvent<String>(EventBus.REPLACE_RULE_SAVE) {
onReplaceRuleSave() viewModel.replaceRuleChanged()
} }
} }
@ -950,16 +883,16 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
*/ */
override fun screenOffTimerStart() { override fun screenOffTimerStart() {
if (screenTimeOut < 0) { if (screenTimeOut < 0) {
ReadBookActivityHelp.keepScreenOn(window, true) keepScreenOn(window, true)
return return
} }
val t = screenTimeOut - sysScreenOffTime val t = screenTimeOut - sysScreenOffTime
if (t > 0) { if (t > 0) {
mHandler.removeCallbacks(keepScreenRunnable) mHandler.removeCallbacks(keepScreenRunnable)
ReadBookActivityHelp.keepScreenOn(window, true) keepScreenOn(window, true)
mHandler.postDelayed(keepScreenRunnable, screenTimeOut) mHandler.postDelayed(keepScreenRunnable, screenTimeOut)
} else { } else {
ReadBookActivityHelp.keepScreenOn(window, false) keepScreenOn(window, false)
} }
} }
} }

@ -2,45 +2,134 @@ package io.legado.app.ui.book.read
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle
import android.view.* import android.view.*
import android.widget.EditText import android.widget.EditText
import androidx.core.view.isVisible
import io.legado.app.App import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Bookmark import io.legado.app.data.entities.Bookmark
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.LocalConfig
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.lib.dialogs.* import io.legado.app.lib.dialogs.*
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.ThemeStore import io.legado.app.lib.theme.ThemeStore
import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.bottomBackground
import io.legado.app.service.help.CacheBook import io.legado.app.service.help.CacheBook
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.config.BgTextConfigDialog
import io.legado.app.ui.book.read.config.ClickActionConfigDialog
import io.legado.app.ui.book.read.config.PaddingConfigDialog
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.widget.text.AutoCompleteTextView import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.applyTint
import io.legado.app.utils.getPrefString import io.legado.app.utils.getPrefString
import io.legado.app.utils.getViewModel
import io.legado.app.utils.requestInputMethod import io.legado.app.utils.requestInputMethod
import kotlinx.android.synthetic.main.activity_book_read.*
import kotlinx.android.synthetic.main.dialog_download_choice.view.* import kotlinx.android.synthetic.main.dialog_download_choice.view.*
import kotlinx.android.synthetic.main.dialog_edit_text.view.* import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import org.jetbrains.anko.layoutInflater import kotlinx.android.synthetic.main.view_read_menu.*
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.startActivityForResult
abstract class ReadBookBaseActivity :
VMBaseActivity<ReadBookViewModel>(R.layout.activity_book_read) {
object ReadBookActivityHelp { override val viewModel: ReadBookViewModel
get() = getViewModel(ReadBookViewModel::class.java)
private val requestCodeEditSource = 111
var bottomDialog = 0
override fun onCreate(savedInstanceState: Bundle?) {
ReadBook.msg = null
setOrientation()
upLayoutInDisplayCutoutMode()
super.onCreate(savedInstanceState)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
initView()
if (LocalConfig.isFirstRead) {
showClickRegionalConfig()
}
}
/**
* 初始化View
*/
private fun initView() {
tv_chapter_name.onClick {
ReadBook.webBook?.let {
startActivityForResult<BookSourceEditActivity>(
requestCodeEditSource,
Pair("data", it.bookSource.bookSourceUrl)
)
}
}
tv_chapter_url.onClick {
runCatching {
val url = tv_chapter_url.text.toString()
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(url)
startActivity(intent)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
requestCodeEditSource -> viewModel.upBookSource()
}
}
}
fun showPaddingConfig() {
PaddingConfigDialog().show(supportFragmentManager, "paddingConfig")
}
fun showBgTextConfig() {
BgTextConfigDialog().show(supportFragmentManager, "bgTextConfig")
}
fun showClickRegionalConfig() {
ClickActionConfigDialog().show(supportFragmentManager, "clickActionConfig")
}
/**
* 屏幕方向
*/
@SuppressLint("SourceLockedOrientationActivity")
fun setOrientation() {
when (AppConfig.requestedDirection) {
"0" -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
"1" -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
"2" -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
"3" -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
}
}
/** /**
* 更新状态栏,导航栏 * 更新状态栏,导航栏
*/ */
fun upSystemUiVisibility( fun upSystemUiVisibility(
activity: Activity,
isInMultiWindow: Boolean, isInMultiWindow: Boolean,
toolBarHide: Boolean = true toolBarHide: Boolean = true
) { ) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
activity.window.insetsController?.let { window.insetsController?.let {
if (toolBarHide) { if (toolBarHide) {
if (ReadBookConfig.hideStatusBar) { if (ReadBookConfig.hideStatusBar) {
it.hide(WindowInsets.Type.statusBars()) it.hide(WindowInsets.Type.statusBars())
@ -54,20 +143,19 @@ object ReadBookActivityHelp {
} }
} }
} }
upSystemUiVisibilityO(activity, isInMultiWindow, toolBarHide) upSystemUiVisibilityO(isInMultiWindow, toolBarHide)
if (toolBarHide) { if (toolBarHide) {
ATH.setLightStatusBar(activity, ReadBookConfig.durConfig.curStatusIconDark()) ATH.setLightStatusBar(this, ReadBookConfig.durConfig.curStatusIconDark())
} else { } else {
ATH.setLightStatusBarAuto( ATH.setLightStatusBarAuto(
activity, this,
ThemeStore.statusBarColor(activity, AppConfig.isTransparentStatusBar) ThemeStore.statusBarColor(this, AppConfig.isTransparentStatusBar)
) )
} }
} }
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
private fun upSystemUiVisibilityO( private fun upSystemUiVisibilityO(
activity: Activity,
isInMultiWindow: Boolean, isInMultiWindow: Boolean,
toolBarHide: Boolean = true toolBarHide: Boolean = true
) { ) {
@ -88,23 +176,23 @@ object ReadBookActivityHelp {
flag = flag or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION flag = flag or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
} }
} }
activity.window.decorView.systemUiVisibility = flag window.decorView.systemUiVisibility = flag
} }
/** override fun upNavigationBarColor() {
* 屏幕方向 when {
*/ read_menu == null -> return
@SuppressLint("SourceLockedOrientationActivity") read_menu.isVisible -> ATH.setNavigationBarColorAuto(this)
fun setOrientation(activity: Activity) = activity.apply { bottomDialog > 0 -> ATH.setNavigationBarColorAuto(this, bottomBackground)
when (AppConfig.requestedDirection) { ReadBookConfig.bg is ColorDrawable -> {
"0" -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED ATH.setNavigationBarColorAuto(this, ReadBookConfig.bgMeanColor)
"1" -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT }
"2" -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE else -> {
"3" -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR ATH.setNavigationBarColorAuto(this, Color.BLACK)
}
} }
} }
/** /**
* 保持亮屏 * 保持亮屏
*/ */
@ -119,7 +207,7 @@ object ReadBookActivityHelp {
/** /**
* 适配刘海 * 适配刘海
*/ */
fun upLayoutInDisplayCutoutMode(window: Window) { private fun upLayoutInDisplayCutoutMode() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && AppConfig.readBodyToLh) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && AppConfig.readBodyToLh) {
window.attributes = window.attributes.apply { window.attributes = window.attributes.apply {
layoutInDisplayCutoutMode = layoutInDisplayCutoutMode =
@ -129,12 +217,13 @@ object ReadBookActivityHelp {
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
fun showDownloadDialog(context: Context) { fun showDownloadDialog() {
ReadBook.book?.let { book -> ReadBook.book?.let { book ->
context.alert(titleResource = R.string.offline_cache) { alert(titleResource = R.string.offline_cache) {
var view: View? = null var view: View? = null
customView { customView {
LayoutInflater.from(context).inflate(R.layout.dialog_download_choice, null) LayoutInflater.from(this@ReadBookBaseActivity)
.inflate(R.layout.dialog_download_choice, null)
.apply { .apply {
view = this view = this
setBackgroundColor(context.backgroundColor) setBackgroundColor(context.backgroundColor)
@ -150,15 +239,15 @@ object ReadBookActivityHelp {
} }
} }
noButton() noButton()
}.show().applyTint() }.show()
} }
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
fun showBookMark(context: Context) = with(context) { fun showBookMark() {
val book = ReadBook.book ?: return val book = ReadBook.book ?: return
val textChapter = ReadBook.curTextChapter ?: return val textChapter = ReadBook.curTextChapter ?: return
context.alert(title = getString(R.string.bookmark_add)) { alert(title = getString(R.string.bookmark_add)) {
var editText: EditText? = null var editText: EditText? = null
message = book.name + "" + textChapter.title message = book.name + "" + textChapter.title
customView { customView {
@ -184,11 +273,11 @@ object ReadBookActivityHelp {
} }
} }
noButton() noButton()
}.show().applyTint().requestInputMethod() }.show().requestInputMethod()
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
fun showCharsetConfig(context: Context) = with(context) { fun showCharsetConfig() {
val charsets = val charsets =
arrayListOf("UTF-8", "GB2312", "GBK", "Unicode", "UTF-16", "UTF-16LE", "ASCII") arrayListOf("UTF-8", "GB2312", "GBK", "Unicode", "UTF-16", "UTF-16LE", "ASCII")
alert(R.string.set_charset) { alert(R.string.set_charset) {
@ -207,10 +296,10 @@ object ReadBookActivityHelp {
} }
} }
cancelButton() cancelButton()
}.show().applyTint() }.show()
} }
fun showPageAnimConfig(context: Context, success: () -> Unit) = with(context) { fun showPageAnimConfig(success: () -> Unit) {
val items = arrayListOf<String>() val items = arrayListOf<String>()
items.add(getString(R.string.btn_default_s)) items.add(getString(R.string.btn_default_s))
items.add(getString(R.string.page_anim_cover)) items.add(getString(R.string.page_anim_cover))
@ -224,13 +313,13 @@ object ReadBookActivityHelp {
} }
} }
fun isPrevKey(context: Context, keyCode: Int): Boolean { fun isPrevKey(keyCode: Int): Boolean {
val prevKeysStr = context.getPrefString(PreferKey.prevKeys) val prevKeysStr = getPrefString(PreferKey.prevKeys)
return prevKeysStr?.split(",")?.contains(keyCode.toString()) ?: false return prevKeysStr?.split(",")?.contains(keyCode.toString()) ?: false
} }
fun isNextKey(context: Context, keyCode: Int): Boolean { fun isNextKey(keyCode: Int): Boolean {
val nextKeysStr = context.getPrefString(PreferKey.nextKeys) val nextKeysStr = getPrefString(PreferKey.nextKeys)
return nextKeysStr?.split(",")?.contains(keyCode.toString()) ?: false return nextKeysStr?.split(",")?.contains(keyCode.toString()) ?: false
} }
} }

@ -271,6 +271,16 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
} }
} }
/**
* 替换规则变化
*/
fun replaceRuleChanged() {
execute {
BookHelp.upReplaceRules()
ReadBook.loadContent(resetPageOffset = false)
}
}
override fun onCleared() { override fun onCleared() {
super.onCleared() super.onCleared()
if (BaseReadAloudService.pause) { if (BaseReadAloudService.pause) {

@ -13,6 +13,7 @@ import io.legado.app.App
import io.legado.app.R import io.legado.app.R
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.LocalConfig
import io.legado.app.help.ReadBookConfig import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.* import io.legado.app.lib.theme.*
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
@ -29,7 +30,7 @@ class ReadMenu @JvmOverloads constructor(
attrs: AttributeSet? = null attrs: AttributeSet? = null
) : FrameLayout(context, attrs) { ) : FrameLayout(context, attrs) {
var cnaShowMenu: Boolean = false var cnaShowMenu: Boolean = false
private val callBack: CallBack? get() = activity as? CallBack private val callBack: CallBack get() = activity as CallBack
private lateinit var menuTopIn: Animation private lateinit var menuTopIn: Animation
private lateinit var menuTopOut: Animation private lateinit var menuTopOut: Animation
private lateinit var menuBottomIn: Animation private lateinit var menuBottomIn: Animation
@ -166,19 +167,19 @@ class ReadMenu @JvmOverloads constructor(
//搜索 //搜索
fabSearch.onClick { fabSearch.onClick {
runMenuOut { runMenuOut {
callBack?.openSearchActivity(null) callBack.openSearchActivity(null)
} }
} }
//自动翻页 //自动翻页
fabAutoPage.onClick { fabAutoPage.onClick {
runMenuOut { runMenuOut {
callBack?.autoPage() callBack.autoPage()
} }
} }
//替换 //替换
fabReplaceRule.onClick { callBack?.openReplaceRule() } fabReplaceRule.onClick { callBack.openReplaceRule() }
//夜间模式 //夜间模式
fabNightTheme.onClick { fabNightTheme.onClick {
@ -195,31 +196,31 @@ class ReadMenu @JvmOverloads constructor(
//目录 //目录
ll_catalog.onClick { ll_catalog.onClick {
runMenuOut { runMenuOut {
callBack?.openChapterList() callBack.openChapterList()
} }
} }
//朗读 //朗读
ll_read_aloud.onClick { ll_read_aloud.onClick {
runMenuOut { runMenuOut {
callBack?.onClickReadAloud() callBack.onClickReadAloud()
} }
} }
ll_read_aloud.onLongClick { ll_read_aloud.onLongClick {
runMenuOut { callBack?.showReadAloudDialog() } runMenuOut { callBack.showReadAloudDialog() }
true true
} }
//界面 //界面
ll_font.onClick { ll_font.onClick {
runMenuOut { runMenuOut {
callBack?.showReadStyle() callBack.showReadStyle()
} }
} }
//设置 //设置
ll_setting.onClick { ll_setting.onClick {
runMenuOut { runMenuOut {
callBack?.showMoreSetting() callBack.showMoreSetting()
} }
} }
} }
@ -230,19 +231,19 @@ class ReadMenu @JvmOverloads constructor(
menuBottomIn = AnimationUtilsSupport.loadAnimation(context, R.anim.anim_readbook_bottom_in) menuBottomIn = AnimationUtilsSupport.loadAnimation(context, R.anim.anim_readbook_bottom_in)
menuTopIn.setAnimationListener(object : Animation.AnimationListener { menuTopIn.setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationStart(animation: Animation) { override fun onAnimationStart(animation: Animation) {
callBack?.upSystemUiVisibility() callBack.upSystemUiVisibility()
ll_brightness.visible(showBrightnessView) ll_brightness.visible(showBrightnessView)
} }
override fun onAnimationEnd(animation: Animation) { override fun onAnimationEnd(animation: Animation) {
vw_menu_bg.onClick { runMenuOut() } vw_menu_bg.onClick { runMenuOut() }
val hasNavigationBar = ReadBookConfig.hideNavigationBar
&& SystemUtils.isNavigationBarExist(activity)
vwNavigationBar.layoutParams = vwNavigationBar.layoutParams.apply { vwNavigationBar.layoutParams = vwNavigationBar.layoutParams.apply {
height = height = if (hasNavigationBar) context.navigationBarHeight else 0
if (ReadBookConfig.hideNavigationBar }
&& SystemUtils.isNavigationBarExist(activity) if (LocalConfig.isFirstReadMenuShow) {
) callBack.showReadMenuHelp()
context.navigationBarHeight
else 0
} }
} }
@ -264,7 +265,7 @@ class ReadMenu @JvmOverloads constructor(
bottom_menu.invisible() bottom_menu.invisible()
cnaShowMenu = false cnaShowMenu = false
onMenuOutEnd?.invoke() onMenuOutEnd?.invoke()
callBack?.upSystemUiVisibility() callBack.upSystemUiVisibility()
} }
override fun onAnimationRepeat(animation: Animation) = Unit override fun onAnimationRepeat(animation: Animation) = Unit
@ -292,6 +293,7 @@ class ReadMenu @JvmOverloads constructor(
fun showReadAloudDialog() fun showReadAloudDialog()
fun upSystemUiVisibility() fun upSystemUiVisibility()
fun onClickReadAloud() fun onClickReadAloud()
fun showReadMenuHelp()
} }
} }

@ -1,5 +1,6 @@
package io.legado.app.ui.book.read.config package io.legado.app.ui.book.read.config
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.*
import android.widget.SeekBar import android.widget.SeekBar
@ -10,6 +11,7 @@ import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.getPrimaryTextColor import io.legado.app.lib.theme.getPrimaryTextColor
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
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.utils.ColorUtils import io.legado.app.utils.ColorUtils
import kotlinx.android.synthetic.main.dialog_auto_read.* import kotlinx.android.synthetic.main.dialog_auto_read.*
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
@ -19,6 +21,7 @@ class AutoReadDialog : BaseDialogFragment() {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
(activity as ReadBookActivity).bottomDialog++
dialog?.window?.let { dialog?.window?.let {
it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
it.setBackgroundDrawableResource(R.color.background) it.setBackgroundDrawableResource(R.color.background)
@ -31,6 +34,11 @@ class AutoReadDialog : BaseDialogFragment() {
} }
} }
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
(activity as ReadBookActivity).bottomDialog--
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,

@ -17,7 +17,7 @@ import io.legado.app.help.ReadBookConfig
import io.legado.app.help.http.HttpHelper import io.legado.app.help.http.HttpHelper
import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat import io.legado.app.help.permission.PermissionsCompat
import io.legado.app.lib.dialogs.* import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.bottomBackground
import io.legado.app.lib.theme.getPrimaryTextColor import io.legado.app.lib.theme.getPrimaryTextColor
import io.legado.app.lib.theme.getSecondaryTextColor import io.legado.app.lib.theme.getSecondaryTextColor
@ -50,6 +50,7 @@ class BgTextConfigDialog : BaseDialogFragment(), FilePickerDialog.CallBack {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
(activity as ReadBookActivity).bottomDialog++
dialog?.window?.let { dialog?.window?.let {
it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
it.setBackgroundDrawableResource(R.color.background) it.setBackgroundDrawableResource(R.color.background)
@ -79,6 +80,7 @@ class BgTextConfigDialog : BaseDialogFragment(), FilePickerDialog.CallBack {
override fun onDismiss(dialog: DialogInterface) { override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog) super.onDismiss(dialog)
ReadBookConfig.save() ReadBookConfig.save()
(activity as ReadBookActivity).bottomDialog--
} }
private fun initView() { private fun initView() {
@ -131,7 +133,7 @@ class BgTextConfigDialog : BaseDialogFragment(), FilePickerDialog.CallBack {
} }
} }
cancelButton() cancelButton()
}.show().applyTint() }.show()
} }
sw_dark_status_icon.onCheckedChange { buttonView, isChecked -> sw_dark_status_icon.onCheckedChange { buttonView, isChecked ->
if (buttonView?.isPressed == true) { if (buttonView?.isPressed == true) {
@ -283,8 +285,8 @@ class BgTextConfigDialog : BaseDialogFragment(), FilePickerDialog.CallBack {
importNetConfig(url) importNetConfig(url)
} }
} }
noButton { } noButton()
}.show().applyTint() }.show()
} }
private fun importNetConfig(url: String) { private fun importNetConfig(url: String) {

@ -10,7 +10,6 @@ import io.legado.app.help.AppConfig
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.widget.text.StrokeTextView import io.legado.app.ui.widget.text.StrokeTextView
import io.legado.app.utils.applyTint
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
class ChineseConverter(context: Context, attrs: AttributeSet?) : StrokeTextView(context, attrs) { class ChineseConverter(context: Context, attrs: AttributeSet?) : StrokeTextView(context, attrs) {
@ -45,7 +44,7 @@ class ChineseConverter(context: Context, attrs: AttributeSet?) : StrokeTextView(
upUi(i) upUi(i)
onChanged?.invoke() onChanged?.invoke()
} }
}.show().applyTint() }.show()
} }
fun onChanged(unit: () -> Unit) { fun onChanged(unit: () -> Unit) {

@ -0,0 +1,132 @@
package io.legado.app.ui.book.read.config
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig
import io.legado.app.lib.dialogs.selector
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.utils.getCompatColor
import io.legado.app.utils.putPrefInt
import kotlinx.android.synthetic.main.dialog_click_action_config.*
import org.jetbrains.anko.sdk27.listeners.onClick
class ClickActionConfigDialog : BaseDialogFragment() {
private val actions = linkedMapOf<Int, String>()
override fun onStart() {
super.onStart()
(activity as ReadBookActivity).bottomDialog++
dialog?.window?.let {
it.setBackgroundDrawableResource(R.color.transparent)
it.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.dialog_click_action_config, container)
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
(activity as ReadBookActivity).bottomDialog--
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
view.setBackgroundColor(getCompatColor(R.color.translucent))
actions[-1] = getString(R.string.non_action)
actions[0] = getString(R.string.menu)
actions[1] = getString(R.string.next_page)
actions[2] = getString(R.string.prev_page)
actions[3] = getString(R.string.next_chapter)
actions[4] = getString(R.string.previous_chapter)
initData()
initViewEvent()
}
private fun initData() = with(AppConfig) {
tv_top_left.text = actions[clickActionTopLeft]
tv_top_center.text = actions[clickActionTopCenter]
tv_top_right.text = actions[clickActionTopRight]
tv_middle_left.text = actions[clickActionMiddleLeft]
tv_middle_right.text = actions[clickActionMiddleRight]
tv_bottom_left.text = actions[clickActionBottomLeft]
tv_bottom_center.text = actions[clickActionBottomCenter]
tv_bottom_right.text = actions[clickActionBottomRight]
}
private fun initViewEvent() {
iv_close.onClick {
dismiss()
}
tv_top_left.onClick {
selectAction { action ->
putPrefInt(PreferKey.clickActionTopLeft, action)
(it as? TextView)?.text = actions[action]
}
}
tv_top_center.onClick {
selectAction { action ->
putPrefInt(PreferKey.clickActionTopCenter, action)
(it as? TextView)?.text = actions[action]
}
}
tv_top_right.onClick {
selectAction { action ->
putPrefInt(PreferKey.clickActionTopRight, action)
(it as? TextView)?.text = actions[action]
}
}
tv_middle_left.onClick {
selectAction { action ->
putPrefInt(PreferKey.clickActionMiddleLeft, action)
(it as? TextView)?.text = actions[action]
}
}
tv_middle_right.onClick {
selectAction { action ->
putPrefInt(PreferKey.clickActionMiddleRight, action)
(it as? TextView)?.text = actions[action]
}
}
tv_bottom_left.onClick {
selectAction { action ->
putPrefInt(PreferKey.clickActionBottomLeft, action)
(it as? TextView)?.text = actions[action]
}
}
tv_bottom_center.onClick {
selectAction { action ->
putPrefInt(PreferKey.clickActionBottomCenter, action)
(it as? TextView)?.text = actions[action]
}
}
tv_bottom_right.onClick {
selectAction { action ->
putPrefInt(PreferKey.clickActionBottomRight, action)
(it as? TextView)?.text = actions[action]
}
}
}
private fun selectAction(success: (action: Int) -> Unit) {
selector(
getString(R.string.select_action),
actions.values.toList()
) { _, index ->
success.invoke(actions.keys.toList()[index])
}
}
}

@ -1,6 +1,7 @@
package io.legado.app.ui.book.read.config package io.legado.app.ui.book.read.config
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.DialogInterface
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.*
@ -14,7 +15,7 @@ import io.legado.app.constant.PreferKey
import io.legado.app.help.ReadBookConfig import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.bottomBackground import io.legado.app.lib.theme.bottomBackground
import io.legado.app.ui.book.read.ReadBookActivityHelp import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.utils.dp import io.legado.app.utils.dp
import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
@ -24,6 +25,7 @@ class MoreConfigDialog : DialogFragment() {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
(activity as ReadBookActivity).bottomDialog++
dialog?.window?.let { dialog?.window?.let {
it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
it.setBackgroundDrawableResource(R.color.background) it.setBackgroundDrawableResource(R.color.background)
@ -57,6 +59,11 @@ class MoreConfigDialog : DialogFragment() {
.commit() .commit()
} }
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
(activity as ReadBookActivity).bottomDialog--
}
class ReadPreferenceFragment : BasePreferenceFragment(), class ReadPreferenceFragment : BasePreferenceFragment(),
SharedPreferences.OnSharedPreferenceChangeListener { SharedPreferences.OnSharedPreferenceChangeListener {
@ -101,9 +108,7 @@ class MoreConfigDialog : DialogFragment() {
PreferKey.keepLight -> postEvent(key, true) PreferKey.keepLight -> postEvent(key, true)
PreferKey.textSelectAble -> postEvent(key, getPrefBoolean(key)) PreferKey.textSelectAble -> postEvent(key, getPrefBoolean(key))
getString(R.string.pk_requested_direction) -> { getString(R.string.pk_requested_direction) -> {
activity?.let { (activity as? ReadBookActivity)?.setOrientation()
ReadBookActivityHelp.setOrientation(it)
}
} }
PreferKey.textFullJustify, PreferKey.textFullJustify,
PreferKey.textBottomJustify -> { PreferKey.textBottomJustify -> {
@ -118,6 +123,9 @@ class MoreConfigDialog : DialogFragment() {
override fun onPreferenceTreeClick(preference: Preference?): Boolean { override fun onPreferenceTreeClick(preference: Preference?): Boolean {
when (preference?.key) { when (preference?.key) {
"customPageKey" -> PageKeyDialog(requireContext()).show() "customPageKey" -> PageKeyDialog(requireContext()).show()
"clickRegionalConfig" -> {
(activity as? ReadBookActivity)?.showClickRegionalConfig()
}
} }
return super.onPreferenceTreeClick(preference) return super.onPreferenceTreeClick(preference)
} }

@ -117,7 +117,6 @@ class ReadAloudConfigDialog : DialogFragment() {
when (preference) { when (preference) {
is ListPreference -> { is ListPreference -> {
val index = preference.findIndexOfValue(value) val index = preference.findIndexOfValue(value)
// Set the summary to reflect the new value.
preference.summary = if (index >= 0) preference.entries[index] else null preference.summary = if (index >= 0) preference.entries[index] else null
} }
else -> { else -> {

@ -1,5 +1,6 @@
package io.legado.app.ui.book.read.config package io.legado.app.ui.book.read.config
import android.content.DialogInterface
import android.os.Bundle import android.os.Bundle
import android.view.* import android.view.*
import android.widget.SeekBar import android.widget.SeekBar
@ -12,6 +13,7 @@ import io.legado.app.lib.theme.getPrimaryTextColor
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
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.utils.ColorUtils import io.legado.app.utils.ColorUtils
import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.observeEvent import io.legado.app.utils.observeEvent
@ -24,6 +26,7 @@ class ReadAloudDialog : BaseDialogFragment() {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
(activity as ReadBookActivity).bottomDialog++
dialog?.window?.let { dialog?.window?.let {
it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
it.setBackgroundDrawableResource(R.color.background) it.setBackgroundDrawableResource(R.color.background)
@ -36,6 +39,11 @@ class ReadAloudDialog : BaseDialogFragment() {
} }
} }
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
(activity as ReadBookActivity).bottomDialog--
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,

@ -35,6 +35,7 @@ class ReadStyleDialog : BaseDialogFragment(), FontSelectDialog.CallBack {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
(activity as ReadBookActivity).bottomDialog++
dialog?.window?.let { dialog?.window?.let {
it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND) it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
it.setBackgroundDrawableResource(R.color.background) it.setBackgroundDrawableResource(R.color.background)
@ -64,6 +65,7 @@ class ReadStyleDialog : BaseDialogFragment(), FontSelectDialog.CallBack {
override fun onDismiss(dialog: DialogInterface) { override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog) super.onDismiss(dialog)
ReadBookConfig.save() ReadBookConfig.save()
(activity as ReadBookActivity).bottomDialog--
} }
private fun initView() { private fun initView() {
@ -190,7 +192,7 @@ class ReadStyleDialog : BaseDialogFragment(), FontSelectDialog.CallBack {
} }
} }
customView = rootView customView = rootView
}.show().applyTint() }.show()
} }
private fun changeBg(index: Int) { private fun changeBg(index: Int) {

@ -18,9 +18,6 @@ import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.HttpTTS import io.legado.app.data.entities.HttpTTS
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.cancelButton
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.okButton
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.service.help.ReadAloud import io.legado.app.service.help.ReadAloud
import io.legado.app.ui.widget.dialog.TextDialog import io.legado.app.ui.widget.dialog.TextDialog
@ -29,7 +26,6 @@ import kotlinx.android.synthetic.main.dialog_http_tts_edit.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.* import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_http_tts.view.* import kotlinx.android.synthetic.main.item_http_tts.view.*
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
import java.io.File
class SpeakEngineDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener { class SpeakEngineDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
@ -128,11 +124,11 @@ class SpeakEngineDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
} }
neutralButton(R.string.help) { neutralButton(R.string.help) {
val helpStr = String( val helpStr = String(
requireContext().assets.open("help${File.separator}httpTts.md").readBytes() requireContext().assets.open("help/httpTTSHelp.md").readBytes()
) )
TextDialog.show(childFragmentManager, helpStr, TextDialog.MD) TextDialog.show(childFragmentManager, helpStr, TextDialog.MD)
} }
}.show().applyTint() }.show()
} }
inner class Adapter(context: Context) : inner class Adapter(context: Context) :

@ -10,7 +10,6 @@ import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.widget.text.StrokeTextView import io.legado.app.ui.widget.text.StrokeTextView
import io.legado.app.utils.applyTint
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
class TextFontWeightConverter(context: Context, attrs: AttributeSet?) : StrokeTextView(context, attrs) { class TextFontWeightConverter(context: Context, attrs: AttributeSet?) : StrokeTextView(context, attrs) {
@ -46,7 +45,7 @@ class TextFontWeightConverter(context: Context, attrs: AttributeSet?) : StrokeTe
upUi(i) upUi(i)
onChanged?.invoke() onChanged?.invoke()
} }
}.show().applyTint() }.show()
} }
fun onChanged(unit: () -> Unit) { fun onChanged(unit: () -> Unit) {

@ -21,9 +21,6 @@ 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.data.entities.TxtTocRule import io.legado.app.data.entities.TxtTocRule
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.cancelButton
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.okButton
import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.ItemTouchCallback import io.legado.app.ui.widget.recycler.ItemTouchCallback
@ -167,7 +164,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
} }
} }
cancelButton() cancelButton()
}.show().applyTint() }.show()
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
@ -191,7 +188,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
} }
} }
cancelButton() cancelButton()
}.show().applyTint() }.show()
} }
inner class TocRegexAdapter(context: Context) : inner class TocRegexAdapter(context: Context) :

@ -12,9 +12,7 @@ import io.legado.app.constant.PreferKey
import io.legado.app.help.ReadBookConfig import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.entities.TextChar import io.legado.app.ui.book.read.page.entities.*
import io.legado.app.ui.book.read.page.entities.TextLine
import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.ui.book.read.page.provider.ImageProvider import io.legado.app.ui.book.read.page.provider.ImageProvider
import io.legado.app.ui.widget.dialog.PhotoDialog import io.legado.app.ui.widget.dialog.PhotoDialog
@ -40,19 +38,21 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
private val visibleRect = RectF() private val visibleRect = RectF()
private val selectStart = arrayOf(0, 0, 0) private val selectStart = arrayOf(0, 0, 0)
private val selectEnd = arrayOf(0, 0, 0) private val selectEnd = arrayOf(0, 0, 0)
private var textChapter: TextChapter? = null
private var textPage: TextPage = TextPage() private var textPage: TextPage = TextPage()
//滚动参数 //滚动参数
private val pageFactory: TextPageFactory get() = callBack.pageFactory private val pageFactory: TextPageFactory get() = callBack.pageFactory
private var pageOffset = 0f private var pageOffset = 0
init { init {
callBack = activity as CallBack callBack = activity as CallBack
contentDescription = textPage.text contentDescription = textPage.text
} }
fun setContent(textPage: TextPage) { fun setContent(pageData: PageData) {
this.textPage = textPage this.textChapter = pageData.textChapter
this.textPage = pageData.textPage
contentDescription = textPage.text contentDescription = textPage.text
invalidate() invalidate()
} }
@ -139,7 +139,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
isTitle: Boolean, isTitle: Boolean,
isReadAloud: Boolean, isReadAloud: Boolean,
) { ) {
val textPaint = if (isTitle) ChapterProvider.titlePaint else ChapterProvider.contentPaint val textPaint = if (isTitle) {
textChapter?.titlePaint ?: ChapterProvider.titlePaint
} else {
textChapter?.contentPaint ?: ChapterProvider.contentPaint
}
textPaint.color = textPaint.color =
if (isReadAloud) context.accentColor else ReadBookConfig.textColor if (isReadAloud) context.accentColor else ReadBookConfig.textColor
textChars.forEach { textChars.forEach {
@ -173,33 +177,33 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
/** /**
* 滚动事件 * 滚动事件
*/ */
fun onScroll(mOffset: Float) { fun scroll(mOffset: Int) {
if (mOffset == 0f) return if (mOffset == 0) return
pageOffset += mOffset pageOffset += mOffset
if (!pageFactory.hasPrev() && pageOffset > 0) { if (!pageFactory.hasPrev() && pageOffset > 0) {
pageOffset = 0f pageOffset = 0
} else if (!pageFactory.hasNext() } else if (!pageFactory.hasNext()
&& pageOffset < 0 && pageOffset < 0
&& pageOffset + textPage.height < ChapterProvider.visibleHeight && pageOffset + textPage.height < ChapterProvider.visibleHeight
) { ) {
val offset = ChapterProvider.visibleHeight - textPage.height val offset = (ChapterProvider.visibleHeight - textPage.height).toInt()
pageOffset = min(0f, offset) pageOffset = min(0, offset)
} else if (pageOffset > 0) { } else if (pageOffset > 0) {
pageFactory.moveToPrev(false) pageFactory.moveToPrev(false)
textPage = pageFactory.currentPage textPage = pageFactory.curData.textPage
pageOffset -= textPage.height pageOffset -= textPage.height.toInt()
upView?.invoke(textPage) upView?.invoke(textPage)
} else if (pageOffset < -textPage.height) { } else if (pageOffset < -textPage.height) {
pageOffset += textPage.height pageOffset += textPage.height.toInt()
pageFactory.moveToNext(false) pageFactory.moveToNext(false)
textPage = pageFactory.currentPage textPage = pageFactory.curData.textPage
upView?.invoke(textPage) upView?.invoke(textPage)
} }
invalidate() invalidate()
} }
fun resetPageOffset() { fun resetPageOffset() {
pageOffset = 0f pageOffset = 0
} }
/** /**
@ -480,17 +484,17 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
private fun relativeOffset(relativePos: Int): Float { private fun relativeOffset(relativePos: Int): Float {
return when (relativePos) { return when (relativePos) {
0 -> pageOffset 0 -> pageOffset.toFloat()
1 -> pageOffset + textPage.height 1 -> pageOffset + textPage.height
else -> pageOffset + textPage.height + pageFactory.nextPage.height else -> pageOffset + textPage.height + pageFactory.nextData.textPage.height
} }
} }
private fun relativePage(relativePos: Int): TextPage { private fun relativePage(relativePos: Int): TextPage {
return when (relativePos) { return when (relativePos) {
0 -> textPage 0 -> textPage
1 -> pageFactory.nextPage 1 -> pageFactory.nextData.textPage
else -> pageFactory.nextPagePlus else -> pageFactory.nextPlusData.textPage
} }
} }

@ -12,6 +12,7 @@ import io.legado.app.constant.AppConst.timeFormat
import io.legado.app.help.ReadBookConfig import io.legado.app.help.ReadBookConfig
import io.legado.app.help.ReadTipConfig import io.legado.app.help.ReadTipConfig
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.entities.PageData
import io.legado.app.ui.book.read.page.entities.TextPage import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.ui.widget.BatteryView import io.legado.app.ui.widget.BatteryView
@ -217,11 +218,12 @@ class ContentView(context: Context) : FrameLayout(context) {
tvBattery?.setBattery(battery) tvBattery?.setBattery(battery)
} }
fun setContent(textPage: TextPage, resetPageOffset: Boolean = true) { fun setContent(pageData: PageData, resetPageOffset: Boolean = true) {
setProgress(textPage) setProgress(pageData.textPage)
if (resetPageOffset) if (resetPageOffset) {
resetPageOffset() resetPageOffset()
content_text_view.setContent(textPage) }
content_text_view.setContent(pageData)
} }
fun resetPageOffset() { fun resetPageOffset() {
@ -237,8 +239,8 @@ class ContentView(context: Context) : FrameLayout(context) {
tvPageAndTotal?.text = "${index.plus(1)}/$pageSize $readProgress" tvPageAndTotal?.text = "${index.plus(1)}/$pageSize $readProgress"
} }
fun onScroll(offset: Float) { fun scroll(offset: Int) {
content_text_view.onScroll(offset) content_text_view.scroll(offset)
} }
fun upSelectAble(selectAble: Boolean) { fun upSelectAble(selectAble: Boolean) {

@ -10,13 +10,13 @@ abstract class PageFactory<DATA>(protected val dataSource: DataSource) {
abstract fun moveToPrev(upContent: Boolean): Boolean abstract fun moveToPrev(upContent: Boolean): Boolean
abstract val nextPage: DATA abstract val nextData: DATA
abstract val prevPage: DATA abstract val prevData: DATA
abstract val currentPage: DATA abstract val curData: DATA
abstract val nextPagePlus: DATA abstract val nextPlusData: DATA
abstract fun hasNext(): Boolean abstract fun hasNext(): Boolean

@ -10,6 +10,7 @@ import android.util.AttributeSet
import android.view.MotionEvent import android.view.MotionEvent
import android.view.ViewConfiguration import android.view.ViewConfiguration
import android.widget.FrameLayout import android.widget.FrameLayout
import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
@ -71,7 +72,18 @@ class PageView(context: Context, attrs: AttributeSet) :
private var firstCharIndex: Int = 0 private var firstCharIndex: Int = 0
val slopSquare by lazy { ViewConfiguration.get(context).scaledTouchSlop } val slopSquare by lazy { ViewConfiguration.get(context).scaledTouchSlop }
private val topLeftRectF = RectF(0F, 0F, width * 0.33f, height * 0.33f)
private val topCenterRectF = RectF(width * 0.33f, 0F, width * 0.66f, height * 0.33f)
private val topRightRectF = RectF(width * 0.36f, 0F, width.toFloat(), height * 0.33f)
private val middleLeftRectF = RectF(0F, height * 0.33f, width * 0.33f, height * 0.66f)
private val centerRectF = RectF(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f) private val centerRectF = RectF(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f)
private val middleRightRectF =
RectF(width * 0.66f, height * 0.33f, width.toFloat(), height * 0.66f)
private val bottomLeftRectF = RectF(0F, height * 0.66f, width * 0.33f, height.toFloat())
private val bottomCenterRectF =
RectF(width * 0.33f, height * 0.66f, width * 0.66f, height.toFloat())
private val bottomRightRectF =
RectF(width * 0.66f, height * 0.66f, width.toFloat(), height.toFloat())
private val autoPageRect by lazy { Rect() } private val autoPageRect by lazy { Rect() }
private val autoPagePint by lazy { private val autoPagePint by lazy {
Paint().apply { Paint().apply {
@ -90,7 +102,15 @@ class PageView(context: Context, attrs: AttributeSet) :
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh) super.onSizeChanged(w, h, oldw, oldh)
topLeftRectF.set(0F, 0F, width * 0.33f, height * 0.33f)
topCenterRectF.set(width * 0.33f, 0F, width * 0.66f, height * 0.33f)
topRightRectF.set(width * 0.36f, 0F, width.toFloat(), height * 0.33f)
middleLeftRectF.set(0F, height * 0.33f, width * 0.33f, height * 0.66f)
centerRectF.set(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f) centerRectF.set(width * 0.33f, height * 0.33f, width * 0.66f, height * 0.66f)
middleRightRectF.set(width * 0.66f, height * 0.33f, width.toFloat(), height * 0.66f)
bottomLeftRectF.set(0F, height * 0.66f, width * 0.33f, height.toFloat())
bottomCenterRectF.set(width * 0.33f, height * 0.66f, width * 0.66f, height.toFloat())
bottomRightRectF.set(width * 0.66f, height * 0.66f, width.toFloat(), height.toFloat())
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) {
@ -101,10 +121,10 @@ class PageView(context: Context, attrs: AttributeSet) :
override fun dispatchDraw(canvas: Canvas) { override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas) super.dispatchDraw(canvas)
pageDelegate?.onDraw(canvas) pageDelegate?.onDraw(canvas)
if (callBack.isAutoPage) { if (callBack.isAutoPage && !isScroll) {
nextPage.screenshot()?.let { nextPage.screenshot()?.let {
val bottom = val bottom =
page_view.height * callBack.autoPageProgress / (ReadBookConfig.autoReadSpeed * 10) page_view.height * callBack.autoPageProgress / (ReadBookConfig.autoReadSpeed * 50)
autoPageRect.set(0, 0, page_view.width, bottom) autoPageRect.set(0, 0, page_view.width, bottom)
canvas.drawBitmap(it, autoPageRect, autoPageRect, null) canvas.drawBitmap(it, autoPageRect, autoPageRect, null)
canvas.drawRect( canvas.drawRect(
@ -239,24 +259,49 @@ class PageView(context: Context, attrs: AttributeSet) :
* 单击 * 单击
*/ */
private fun onSingleTapUp(): Boolean { private fun onSingleTapUp(): Boolean {
if (isTextSelected) { when {
isTextSelected = false isTextSelected -> isTextSelected = false
return true centerRectF.contains(startX, startY) -> if (!isAbortAnim) {
} click(AppConfig.clickActionMiddleCenter)
if (centerRectF.contains(startX, startY)) { }
if (!isAbortAnim) { bottomCenterRectF.contains(startX, startY) -> {
callBack.clickCenter() click(AppConfig.clickActionBottomCenter)
}
bottomLeftRectF.contains(startX, startY) -> {
click(AppConfig.clickActionBottomLeft)
}
bottomRightRectF.contains(startX, startY) -> {
click(AppConfig.clickActionBottomRight)
}
middleLeftRectF.contains(startX, startY) -> {
click(AppConfig.clickActionMiddleLeft)
}
middleRightRectF.contains(startX, startY) -> {
click(AppConfig.clickActionMiddleRight)
} }
} else if (ReadBookConfig.clickTurnPage) { topLeftRectF.contains(startX, startY) -> {
if (startX > width / 2 || ReadBookConfig.clickAllNext) { click(AppConfig.clickActionTopLeft)
pageDelegate?.nextPageByAnim(defaultAnimationSpeed) }
} else { topCenterRectF.contains(startX, startY) -> {
pageDelegate?.prevPageByAnim(defaultAnimationSpeed) click(AppConfig.clickActionTopCenter)
}
topRightRectF.contains(startX, startY) -> {
click(AppConfig.clickActionTopRight)
} }
} }
return true return true
} }
private fun click(action: Int) {
when (action) {
0 -> callBack.showActionMenu()
1 -> pageDelegate?.nextPageByAnim(defaultAnimationSpeed)
2 -> pageDelegate?.prevPageByAnim(defaultAnimationSpeed)
3 -> ReadBook.moveToNextChapter(true)
4 -> ReadBook.moveToPrevChapter(upContent = true, toLast = false)
}
}
/** /**
* 选择文本 * 选择文本
*/ */
@ -331,16 +376,16 @@ class PageView(context: Context, attrs: AttributeSet) :
override fun upContent(relativePosition: Int, resetPageOffset: Boolean) { override fun upContent(relativePosition: Int, resetPageOffset: Boolean) {
if (isScroll && !callBack.isAutoPage) { if (isScroll && !callBack.isAutoPage) {
curPage.setContent(pageFactory.currentPage, resetPageOffset) curPage.setContent(pageFactory.curData, resetPageOffset)
} else { } else {
curPage.resetPageOffset() curPage.resetPageOffset()
when (relativePosition) { when (relativePosition) {
-1 -> prevPage.setContent(pageFactory.prevPage) -1 -> prevPage.setContent(pageFactory.prevData)
1 -> nextPage.setContent(pageFactory.nextPage) 1 -> nextPage.setContent(pageFactory.nextData)
else -> { else -> {
curPage.setContent(pageFactory.currentPage) curPage.setContent(pageFactory.curData)
nextPage.setContent(pageFactory.nextPage) nextPage.setContent(pageFactory.nextData)
prevPage.setContent(pageFactory.prevPage) prevPage.setContent(pageFactory.prevData)
} }
} }
} }
@ -408,7 +453,7 @@ class PageView(context: Context, attrs: AttributeSet) :
val isInitFinish: Boolean val isInitFinish: Boolean
val isAutoPage: Boolean val isAutoPage: Boolean
val autoPageProgress: Int val autoPageProgress: Int
fun clickCenter() fun showActionMenu()
fun screenOffTimerStart() fun screenOffTimerStart()
fun showTextActionMenu() fun showTextActionMenu()
} }

@ -1,9 +1,10 @@
package io.legado.app.ui.book.read.page package io.legado.app.ui.book.read.page
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.entities.PageData
import io.legado.app.ui.book.read.page.entities.TextPage import io.legado.app.ui.book.read.page.entities.TextPage
class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(dataSource) { class TextPageFactory(dataSource: DataSource) : PageFactory<PageData>(dataSource) {
override fun hasPrev(): Boolean = with(dataSource) { override fun hasPrev(): Boolean = with(dataSource) {
return hasPrevChapter() || pageIndex > 0 return hasPrevChapter() || pageIndex > 0
@ -57,74 +58,81 @@ class TextPageFactory(dataSource: DataSource) : PageFactory<TextPage>(dataSource
false false
} }
override val currentPage: TextPage override val curData: PageData
get() = with(dataSource) { get() = with(dataSource) {
ReadBook.msg?.let { ReadBook.msg?.let {
return@with TextPage(text = it).format() return@with PageData(TextPage(text = it).format())
} }
currentChapter?.let { currentChapter?.let {
return@with it.page(pageIndex) val page = it.page(pageIndex) ?: TextPage(title = it.title).format()
?: TextPage(title = it.title).format() return@with PageData(page, it)
} }
return TextPage().format() return PageData(TextPage().format())
} }
override val nextPage: TextPage override val nextData: PageData
get() = with(dataSource) { get() = with(dataSource) {
ReadBook.msg?.let { ReadBook.msg?.let {
return@with TextPage(text = it).format() return@with PageData(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() val page = it.page(pageIndex + 1)?.removePageAloudSpan()
?: TextPage(title = it.title).format() ?: TextPage(title = it.title).format()
return@with PageData(page, it)
} }
} }
if (!hasNextChapter()) { if (!hasNextChapter()) {
return@with TextPage(text = "") return@with PageData(TextPage(text = ""))
} }
nextChapter?.let { nextChapter?.let {
return@with it.page(0)?.removePageAloudSpan() val page = it.page(0)?.removePageAloudSpan()
?: TextPage(title = it.title).format() ?: TextPage(title = it.title).format()
return@with PageData(page, it)
} }
return TextPage().format() return PageData(TextPage().format())
} }
override val prevPage: TextPage override val prevData: PageData
get() = with(dataSource) { get() = with(dataSource) {
ReadBook.msg?.let { ReadBook.msg?.let {
return@with TextPage(text = it).format() return@with PageData(TextPage(text = it).format())
} }
if (pageIndex > 0) { if (pageIndex > 0) {
currentChapter?.let { currentChapter?.let {
return@with it.page(pageIndex - 1)?.removePageAloudSpan() val page = it.page(pageIndex - 1)?.removePageAloudSpan()
?: TextPage(title = it.title).format() ?: TextPage(title = it.title).format()
return@with PageData(page, it)
} }
} }
prevChapter?.let { prevChapter?.let {
return@with it.lastPage?.removePageAloudSpan() val page = it.lastPage?.removePageAloudSpan()
?: TextPage(title = it.title).format() ?: TextPage(title = it.title).format()
return@with PageData(page, it)
} }
return TextPage().format() return PageData(TextPage().format())
} }
override val nextPagePlus: TextPage override val nextPlusData: PageData
get() = with(dataSource) { get() = with(dataSource) {
currentChapter?.let { currentChapter?.let {
if (pageIndex < it.pageSize - 2) { if (pageIndex < it.pageSize - 2) {
return@with it.page(pageIndex + 2)?.removePageAloudSpan() val page = it.page(pageIndex + 2)?.removePageAloudSpan()
?: TextPage(title = it.title).format() ?: TextPage(title = it.title).format()
return@with PageData(page, it)
} }
nextChapter?.let { nc -> nextChapter?.let { nc ->
if (pageIndex < it.pageSize - 1) { if (pageIndex < it.pageSize - 1) {
return@with nc.page(0)?.removePageAloudSpan() val page = nc.page(0)?.removePageAloudSpan()
?: TextPage(title = nc.title).format() ?: TextPage(title = nc.title).format()
return@with PageData(page, nc)
} }
return@with nc.page(1)?.removePageAloudSpan() val page = nc.page(1)?.removePageAloudSpan()
?: TextPage(title = nc.title).format() ?: TextPage(title = nc.title).format()
return@with PageData(page, nc)
} }
} }
return TextPage().format() return PageData(TextPage().format())
} }
} }

@ -41,7 +41,7 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) {
} }
override fun onScroll() { override fun onScroll() {
curPage.onScroll(touchY - lastY) curPage.scroll((touchY - lastY).toInt())
} }
override fun onDraw(canvas: Canvas) { override fun onDraw(canvas: Canvas) {

@ -0,0 +1,6 @@
package io.legado.app.ui.book.read.page.entities
data class PageData(
val textPage: TextPage,
val textChapter: TextChapter? = null
)

@ -1,5 +1,6 @@
package io.legado.app.ui.book.read.page.entities package io.legado.app.ui.book.read.page.entities
import android.text.TextPaint
import kotlin.math.min import kotlin.math.min
data class TextChapter( data class TextChapter(
@ -9,7 +10,9 @@ data class TextChapter(
val pages: List<TextPage>, val pages: List<TextPage>,
val pageLines: List<Int>, val pageLines: List<Int>,
val pageLengths: List<Int>, val pageLengths: List<Int>,
val chaptersSize: Int val chaptersSize: Int,
var titlePaint: TextPaint? = null,
var contentPaint: TextPaint? = null
) { ) {
fun page(index: Int): TextPage? { fun page(index: Int): TextPage? {
return pages.getOrNull(index) return pages.getOrNull(index)

@ -11,6 +11,7 @@ import io.legado.app.constant.AppPattern
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp
import io.legado.app.help.ReadBookConfig import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.entities.TextChapter import io.legado.app.ui.book.read.page.entities.TextChapter
import io.legado.app.ui.book.read.page.entities.TextChar import io.legado.app.ui.book.read.page.entities.TextChar
@ -57,6 +58,11 @@ object ChapterProvider {
val pageLengths = arrayListOf<Int>() val pageLengths = arrayListOf<Int>()
val stringBuilder = StringBuilder() val stringBuilder = StringBuilder()
var durY = 0f var durY = 0f
var paint = Pair(titlePaint, contentPaint)
BookHelp.getFontPath(book, bookChapter)?.let {
val typeface = getTypeface(it)
paint = getPaint(typeface)
}
textPages.add(TextPage()) textPages.add(TextPage())
contents.forEachIndexed { index, text -> contents.forEachIndexed { index, text ->
val matcher = AppPattern.imgPattern.matcher(text) val matcher = AppPattern.imgPattern.matcher(text)
@ -66,19 +72,18 @@ object ChapterProvider {
src = NetworkUtils.getAbsoluteURL(bookChapter.url, src) src = NetworkUtils.getAbsoluteURL(bookChapter.url, src)
} }
src?.let { src?.let {
durY = durY = setTypeImage(
setTypeImage( book, bookChapter, src, durY, textPages, imageStyle
book, bookChapter, src, durY, textPages, imageStyle )
)
} }
} else { } else {
val isTitle = index == 0 val isTitle = index == 0
val textPaint = if (isTitle) paint.first else paint.second
if (!(isTitle && ReadBookConfig.titleMode == 2)) { if (!(isTitle && ReadBookConfig.titleMode == 2)) {
durY = durY = setTypeText(
setTypeText( text, durY, textPages, pageLines,
text, durY, textPages, pageLines, pageLengths, stringBuilder, isTitle, textPaint
pageLengths, stringBuilder, isTitle )
)
} }
} }
} }
@ -106,7 +111,9 @@ object ChapterProvider {
textPages, textPages,
pageLines, pageLines,
pageLengths, pageLengths,
chapterSize chapterSize,
paint.first,
paint.second
) )
} }
@ -185,9 +192,9 @@ object ChapterProvider {
pageLengths: ArrayList<Int>, pageLengths: ArrayList<Int>,
stringBuilder: StringBuilder, stringBuilder: StringBuilder,
isTitle: Boolean, isTitle: Boolean,
textPaint: TextPaint
): Float { ): Float {
var durY = if (isTitle) y + titleTopSpacing else y var durY = if (isTitle) y + titleTopSpacing else y
val textPaint = if (isTitle) titlePaint else contentPaint
val layout = StaticLayout( val layout = StaticLayout(
text, textPaint, visibleWidth, Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true text, textPaint, visibleWidth, Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true
) )
@ -200,12 +207,7 @@ object ChapterProvider {
if (lineIndex == 0 && layout.lineCount > 1 && !isTitle) { if (lineIndex == 0 && layout.lineCount > 1 && !isTitle) {
//第一行 //第一行
textLine.text = words textLine.text = words
addCharsToLineFirst( addCharsToLineFirst(textLine, words.toStringArray(), textPaint, desiredWidth)
textLine,
words.toStringArray(),
textPaint,
desiredWidth
)
} else if (lineIndex == layout.lineCount - 1) { } else if (lineIndex == layout.lineCount - 1) {
//最后一行 //最后一行
textLine.text = "$words\n" textLine.text = "$words\n"
@ -213,22 +215,11 @@ object ChapterProvider {
val x = if (isTitle && ReadBookConfig.titleMode == 1) val x = if (isTitle && ReadBookConfig.titleMode == 1)
(visibleWidth - layout.getLineWidth(lineIndex)) / 2 (visibleWidth - layout.getLineWidth(lineIndex)) / 2
else 0f else 0f
addCharsToLineLast( addCharsToLineLast(textLine, words.toStringArray(), textPaint, x)
textLine,
words.toStringArray(),
textPaint,
x
)
} else { } else {
//中间行 //中间行
textLine.text = words textLine.text = words
addCharsToLineMiddle( addCharsToLineMiddle(textLine, words.toStringArray(), textPaint, desiredWidth, 0f)
textLine,
words.toStringArray(),
textPaint,
desiredWidth,
0f
)
} }
if (durY + textPaint.textHeight > visibleHeight) { if (durY + textPaint.textHeight > visibleHeight) {
//当前页面结束,设置各种值 //当前页面结束,设置各种值
@ -264,33 +255,18 @@ object ChapterProvider {
) { ) {
var x = 0f var x = 0f
if (!ReadBookConfig.textFullJustify) { if (!ReadBookConfig.textFullJustify) {
addCharsToLineLast( addCharsToLineLast(textLine, words, textPaint, x)
textLine,
words,
textPaint,
x
)
return return
} }
val bodyIndent = ReadBookConfig.paragraphIndent val bodyIndent = ReadBookConfig.paragraphIndent
val icw = StaticLayout.getDesiredWidth(bodyIndent, textPaint) / bodyIndent.length val icw = StaticLayout.getDesiredWidth(bodyIndent, textPaint) / bodyIndent.length
bodyIndent.toStringArray().forEach { bodyIndent.toStringArray().forEach {
val x1 = x + icw val x1 = x + icw
textLine.addTextChar( textLine.addTextChar(charData = it, start = paddingLeft + x, end = paddingLeft + x1)
charData = it,
start = paddingLeft + x,
end = paddingLeft + x1
)
x = x1 x = x1
} }
val words1 = words.copyOfRange(bodyIndent.length, words.size) val words1 = words.copyOfRange(bodyIndent.length, words.size)
addCharsToLineMiddle( addCharsToLineMiddle(textLine, words1, textPaint, desiredWidth, x)
textLine,
words1,
textPaint,
desiredWidth,
x
)
} }
/** /**
@ -304,12 +280,7 @@ object ChapterProvider {
startX: Float, startX: Float,
) { ) {
if (!ReadBookConfig.textFullJustify) { if (!ReadBookConfig.textFullJustify) {
addCharsToLineLast( addCharsToLineLast(textLine, words, textPaint, startX)
textLine,
words,
textPaint,
startX
)
return return
} }
val gapCount: Int = words.lastIndex val gapCount: Int = words.lastIndex
@ -318,17 +289,10 @@ object ChapterProvider {
words.forEachIndexed { index, s -> words.forEachIndexed { index, s ->
val cw = StaticLayout.getDesiredWidth(s, textPaint) val cw = StaticLayout.getDesiredWidth(s, textPaint)
val x1 = if (index != words.lastIndex) (x + cw + d) else (x + cw) val x1 = if (index != words.lastIndex) (x + cw + d) else (x + cw)
textLine.addTextChar( textLine.addTextChar(charData = s, start = paddingLeft + x, end = paddingLeft + x1)
charData = s,
start = paddingLeft + x,
end = paddingLeft + x1
)
x = x1 x = x1
} }
exceed( exceed(textLine, words)
textLine,
words
)
} }
/** /**
@ -344,17 +308,10 @@ object ChapterProvider {
words.forEach { words.forEach {
val cw = StaticLayout.getDesiredWidth(it, textPaint) val cw = StaticLayout.getDesiredWidth(it, textPaint)
val x1 = x + cw val x1 = x + cw
textLine.addTextChar( textLine.addTextChar(charData = it, start = paddingLeft + x, end = paddingLeft + x1)
charData = it,
start = paddingLeft + x,
end = paddingLeft + x1
)
x = x1 x = x1
} }
exceed( exceed(textLine, words)
textLine,
words
)
} }
/** /**
@ -378,8 +335,21 @@ object ChapterProvider {
* 更新样式 * 更新样式
*/ */
fun upStyle() { fun upStyle() {
typeface = try { typeface = getTypeface(ReadBookConfig.textFont)
val fontPath = ReadBookConfig.textFont getPaint(typeface).let {
titlePaint = it.first
contentPaint = it.second
}
//间距
lineSpacingExtra = ReadBookConfig.lineSpacingExtra
paragraphSpacing = ReadBookConfig.paragraphSpacing
titleTopSpacing = ReadBookConfig.titleTopSpacing.dp
titleBottomSpacing = ReadBookConfig.titleBottomSpacing.dp
upVisibleSize()
}
private fun getTypeface(fontPath: String): Typeface {
return try {
when { when {
fontPath.isContentScheme() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> { fontPath.isContentScheme() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O -> {
val fd = App.INSTANCE.contentResolver val fd = App.INSTANCE.contentResolver
@ -402,6 +372,9 @@ object ChapterProvider {
ReadBookConfig.save() ReadBookConfig.save()
Typeface.SANS_SERIF Typeface.SANS_SERIF
} }
}
private fun getPaint(typeface: Typeface): Pair<TextPaint, TextPaint> {
// 字体统一处理 // 字体统一处理
val bold = Typeface.create(typeface, Typeface.BOLD) val bold = Typeface.create(typeface, Typeface.BOLD)
val normal = Typeface.create(typeface, Typeface.NORMAL) val normal = Typeface.create(typeface, Typeface.NORMAL)
@ -422,25 +395,20 @@ object ChapterProvider {
} }
//标题 //标题
titlePaint = TextPaint() val tPaint = TextPaint()
titlePaint.color = ReadBookConfig.textColor tPaint.color = ReadBookConfig.textColor
titlePaint.letterSpacing = ReadBookConfig.letterSpacing tPaint.letterSpacing = ReadBookConfig.letterSpacing
titlePaint.typeface = titleFont tPaint.typeface = titleFont
titlePaint.textSize = with(ReadBookConfig) { textSize + titleSize }.sp.toFloat() tPaint.textSize = with(ReadBookConfig) { textSize + titleSize }.sp.toFloat()
titlePaint.isAntiAlias = true tPaint.isAntiAlias = true
//正文 //正文
contentPaint = TextPaint() val cPaint = TextPaint()
contentPaint.color = ReadBookConfig.textColor cPaint.color = ReadBookConfig.textColor
contentPaint.letterSpacing = ReadBookConfig.letterSpacing cPaint.letterSpacing = ReadBookConfig.letterSpacing
contentPaint.typeface = textFont cPaint.typeface = textFont
contentPaint.textSize = ReadBookConfig.textSize.sp.toFloat() cPaint.textSize = ReadBookConfig.textSize.sp.toFloat()
contentPaint.isAntiAlias = true cPaint.isAntiAlias = true
//间距 return Pair(tPaint, cPaint)
lineSpacingExtra = ReadBookConfig.lineSpacingExtra
paragraphSpacing = ReadBookConfig.paragraphSpacing
titleTopSpacing = ReadBookConfig.titleTopSpacing.dp
titleBottomSpacing = ReadBookConfig.titleBottomSpacing.dp
upVisibleSize()
} }
/** /**

@ -122,7 +122,7 @@ class SearchContentActivity :
observeEvent<BookChapter>(EventBus.SAVE_CONTENT) { chapter -> observeEvent<BookChapter>(EventBus.SAVE_CONTENT) { chapter ->
viewModel.book?.bookUrl?.let { bookUrl -> viewModel.book?.bookUrl?.let { bookUrl ->
if (chapter.bookUrl == bookUrl) { if (chapter.bookUrl == bookUrl) {
adapter.cacheFileNames.add(BookHelp.formatChapterName(chapter)) adapter.cacheFileNames.add(chapter.getFileName())
adapter.notifyItemChanged(chapter.index, true) adapter.notifyItemChanged(chapter.index, true)
} }
} }
@ -143,7 +143,7 @@ class SearchContentActivity :
App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map { chapter -> App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map { chapter ->
val job = async(Dispatchers.IO) { val job = async(Dispatchers.IO) {
if (isLocalBook if (isLocalBook
|| adapter.cacheFileNames.contains(BookHelp.formatChapterName(chapter)) || adapter.cacheFileNames.contains(chapter.getFileName())
) { ) {
searchResults = searchChapter(newText, chapter) searchResults = searchChapter(newText, chapter)
} }

@ -25,6 +25,7 @@ import io.legado.app.ui.book.source.debug.BookSourceDebugActivity
import io.legado.app.ui.login.SourceLogin import io.legado.app.ui.login.SourceLogin
import io.legado.app.ui.qrcode.QrCodeActivity import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.ui.widget.KeyboardToolPop import io.legado.app.ui.widget.KeyboardToolPop
import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_book_source_edit.* import kotlinx.android.synthetic.main.activity_book_source_edit.*
import org.jetbrains.anko.* import org.jetbrains.anko.*
@ -63,7 +64,7 @@ class BookSourceEditActivity :
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean { override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
R.id.menu_save -> getSource().let { source -> R.id.menu_save -> getSource().let { source ->
if (!source.equal(viewModel.bookSource ?: BookSource())){ if (!source.equal(viewModel.bookSource ?: BookSource())) {
source.lastUpdateTime = System.currentTimeMillis() source.lastUpdateTime = System.currentTimeMillis()
} }
if (checkSource(source)) { if (checkSource(source)) {
@ -81,7 +82,10 @@ class BookSourceEditActivity :
R.id.menu_paste_source -> viewModel.pasteSource { upRecyclerView(it) } R.id.menu_paste_source -> viewModel.pasteSource { upRecyclerView(it) }
R.id.menu_qr_code_camera -> startActivityForResult<QrCodeActivity>(qrRequestCode) R.id.menu_qr_code_camera -> startActivityForResult<QrCodeActivity>(qrRequestCode)
R.id.menu_share_str -> share(GSON.toJson(getSource())) R.id.menu_share_str -> share(GSON.toJson(getSource()))
R.id.menu_share_qr -> shareWithQr(getString(R.string.share_book_source), GSON.toJson(getSource())) R.id.menu_share_qr -> shareWithQr(
getString(R.string.share_book_source),
GSON.toJson(getSource())
)
R.id.menu_rule_summary -> { R.id.menu_rule_summary -> {
try { try {
val intent = Intent(Intent.ACTION_VIEW) val intent = Intent(Intent.ACTION_VIEW)
@ -138,7 +142,7 @@ class BookSourceEditActivity :
negativeButton(R.string.no) { negativeButton(R.string.no) {
super.finish() super.finish()
} }
}.show().applyTint() }.show()
} else { } else {
super.finish() super.finish()
} }
@ -229,6 +233,7 @@ class BookSourceEditActivity :
add(EditEntity("sourceRegex", cr?.sourceRegex, R.string.rule_source_regex)) add(EditEntity("sourceRegex", cr?.sourceRegex, R.string.rule_source_regex))
add(EditEntity("replaceRegex", cr?.replaceRegex, R.string.rule_replace_regex)) add(EditEntity("replaceRegex", cr?.replaceRegex, R.string.rule_replace_regex))
add(EditEntity("imageStyle", cr?.imageStyle, R.string.rule_image_style)) add(EditEntity("imageStyle", cr?.imageStyle, R.string.rule_image_style))
add(EditEntity("font", cr?.font, R.string.rule_font))
} }
//发现 //发现
val er = source?.getExploreRule() val er = source?.getExploreRule()
@ -332,6 +337,7 @@ class BookSourceEditActivity :
"sourceRegex" -> contentRule.sourceRegex = it.value "sourceRegex" -> contentRule.sourceRegex = it.value
"replaceRegex" -> contentRule.replaceRegex = it.value "replaceRegex" -> contentRule.replaceRegex = it.value
"imageStyle" -> contentRule.imageStyle = it.value "imageStyle" -> contentRule.imageStyle = it.value
"font" -> contentRule.font = it.value
} }
} }
source.ruleSearch = searchRule source.ruleSearch = searchRule
@ -344,7 +350,7 @@ class BookSourceEditActivity :
private fun checkSource(source: BookSource): Boolean { private fun checkSource(source: BookSource): Boolean {
if (source.bookSourceUrl.isBlank() || source.bookSourceName.isBlank()) { if (source.bookSourceUrl.isBlank() || source.bookSourceName.isBlank()) {
toast("书源名称和URL不能为空") toast(R.string.non_null_name_url)
return false return false
} }
return true return true
@ -367,12 +373,28 @@ class BookSourceEditActivity :
override fun sendText(text: String) { override fun sendText(text: String) {
if (text == AppConst.keyboardToolChars[0]) { if (text == AppConst.keyboardToolChars[0]) {
insertText(AppConst.urlOption) showHelpDialog()
} else { } else {
insertText(text) insertText(text)
} }
} }
private fun showHelpDialog() {
val items = arrayListOf("插入URL参数", "书源教程", "正则教程")
selector(getString(R.string.help), items) { _, index ->
when (index) {
0 -> insertText(AppConst.urlOption)
1 -> openUrl("https://alanskycn.gitee.io/teachme/Rule/source.html")
2 -> showRegexHelp()
}
}
}
private fun showRegexHelp() {
val mdText = String(assets.open("help/regexHelp.md").readBytes())
TextDialog.show(supportFragmentManager, mdText, TextDialog.MD)
}
private fun showKeyboardTopPopupWindow() { private fun showKeyboardTopPopupWindow() {
mSoftKeyboardTool?.let { mSoftKeyboardTool?.let {
if (it.isShowing) return if (it.isShowing) return

@ -18,11 +18,9 @@ class BookSourceEditAdapter : RecyclerView.Adapter<BookSourceEditAdapter.MyViewH
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder( val view = LayoutInflater.from(parent.context)
LayoutInflater.from( .inflate(R.layout.item_source_edit, parent, false)
parent.context return MyViewHolder(view)
).inflate(R.layout.item_source_edit, parent, false)
)
} }
override fun onBindViewHolder(holder: MyViewHolder, position: Int) { override fun onBindViewHolder(holder: MyViewHolder, position: Int) {

@ -25,7 +25,8 @@ import io.legado.app.constant.AppPattern
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.help.IntentDataHelp import io.legado.app.help.IntentDataHelp
import io.legado.app.lib.dialogs.* import io.legado.app.help.LocalConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.primaryTextColor import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.service.help.CheckSource import io.legado.app.service.help.CheckSource
@ -35,6 +36,7 @@ import io.legado.app.ui.filepicker.FilePicker
import io.legado.app.ui.filepicker.FilePickerDialog import io.legado.app.ui.filepicker.FilePickerDialog
import io.legado.app.ui.qrcode.QrCodeActivity import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.ui.widget.SelectActionBar import io.legado.app.ui.widget.SelectActionBar
import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
import io.legado.app.ui.widget.recycler.ItemTouchCallback import io.legado.app.ui.widget.recycler.ItemTouchCallback
import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.ui.widget.recycler.VerticalDivider
@ -75,6 +77,9 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
initLiveDataBookSource() initLiveDataBookSource()
initLiveDataGroup() initLiveDataGroup()
initSelectActionBar() initSelectActionBar()
if (LocalConfig.isFirstOpenBookSources) {
showHelp()
}
} }
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean { override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
@ -154,6 +159,7 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
R.id.menu_disabled_group -> { R.id.menu_disabled_group -> {
search_view.setQuery(getString(R.string.disabled), true) search_view.setQuery(getString(R.string.disabled), true)
} }
R.id.menu_help -> showHelp()
} }
if (item.groupId == R.id.source_group) { if (item.groupId == R.id.source_group) {
search_view.setQuery(item.title, true) search_view.setQuery(item.title, true)
@ -226,6 +232,11 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
}) })
} }
private fun showHelp() {
val text = String(assets.open("help/bookSourcesHelp.md").readBytes())
TextDialog.show(supportFragmentManager, text, TextDialog.MD)
}
private fun sortCheck(sortId: Int) { private fun sortCheck(sortId: Int) {
if (sort == sortId) { if (sort == sortId) {
sortAscending += 1 sortAscending += 1
@ -260,8 +271,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
override fun onClickMainAction() { override fun onClickMainAction() {
alert(titleResource = R.string.draw, messageResource = R.string.sure_del) { alert(titleResource = R.string.draw, messageResource = R.string.sure_del) {
okButton { viewModel.delSelection(adapter.getSelection()) } okButton { viewModel.delSelection(adapter.getSelection()) }
noButton { } noButton()
}.show().applyTint() }.show()
} }
private fun initSelectActionBar() { private fun initSelectActionBar() {
@ -305,8 +316,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
} }
CheckSource.start(this@BookSourceActivity, adapter.getSelection()) CheckSource.start(this@BookSourceActivity, adapter.getSelection())
} }
noButton { } noButton()
}.show().applyTint() }.show()
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
@ -326,8 +337,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
} }
} }
} }
noButton { } noButton()
}.show().applyTint() }.show()
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
@ -347,8 +358,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
} }
} }
} }
noButton { } noButton()
}.show().applyTint() }.show()
} }
private fun upGroupMenu() { private fun upGroupMenu() {
@ -389,7 +400,7 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
} }
} }
cancelButton() cancelButton()
}.show().applyTint() }.show()
} }
override fun observeLiveBus() { override fun observeLiveBus() {
@ -403,10 +414,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
} }
} }
observeEvent<Int>(EventBus.CHECK_SOURCE_DONE) { observeEvent<Int>(EventBus.CHECK_SOURCE_DONE) {
snackBar?.let { snackBar?.dismiss()
it.dismiss() snackBar = null
snackBar = null
}
groups.map { group -> groups.map { group ->
if (group.contains("失效")) { if (group.contains("失效")) {
search_view.setQuery("失效", true) search_view.setQuery("失效", true)

@ -17,9 +17,6 @@ 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.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.ui.widget.recycler.VerticalDivider
@ -99,7 +96,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
} }
} }
noButton() noButton()
}.show().applyTint().requestInputMethod() }.show().requestInputMethod()
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
@ -118,7 +115,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
viewModel.upGroup(group, editText?.text?.toString()) viewModel.upGroup(group, editText?.text?.toString())
} }
noButton() noButton()
}.show().applyTint().requestInputMethod() }.show().requestInputMethod()
} }
private inner class GroupAdapter(context: Context) : private inner class GroupAdapter(context: Context) :

@ -15,12 +15,8 @@ import io.legado.app.R
import io.legado.app.base.VMBaseFragment import io.legado.app.base.VMBaseFragment
import io.legado.app.data.entities.Bookmark import io.legado.app.data.entities.Bookmark
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
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.getViewModelOfActivity import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.requestInputMethod import io.legado.app.utils.requestInputMethod
import kotlinx.android.synthetic.main.dialog_edit_text.view.* import kotlinx.android.synthetic.main.dialog_edit_text.view.*
@ -109,7 +105,7 @@ class BookmarkFragment : VMBaseFragment<ChapterListViewModel>(R.layout.fragment_
neutralButton(R.string.delete) { neutralButton(R.string.delete) {
App.db.bookmarkDao().delete(bookmark) App.db.bookmarkDao().delete(bookmark)
} }
}.show().applyTint().requestInputMethod() }.show().requestInputMethod()
} }
} }
} }

@ -6,7 +6,6 @@ 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.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.utils.getCompatColor import io.legado.app.utils.getCompatColor
import io.legado.app.utils.visible import io.legado.app.utils.visible
@ -22,8 +21,7 @@ class ChapterListAdapter(context: Context, val callback: Callback) :
override fun convert(holder: ItemViewHolder, item: BookChapter, payloads: MutableList<Any>) { override fun convert(holder: ItemViewHolder, item: BookChapter, payloads: MutableList<Any>) {
with(holder.itemView) { with(holder.itemView) {
val isDur = callback.durChapterIndex() == item.index val isDur = callback.durChapterIndex() == item.index
val cached = callback.isLocalBook val cached = callback.isLocalBook || cacheFileNames.contains(item.getFileName())
|| cacheFileNames.contains(BookHelp.formatChapterName(item))
if (payloads.isEmpty()) { if (payloads.isEmpty()) {
if (isDur) { if (isDur) {
tv_chapter_name.setTextColor(context.accentColor) tv_chapter_name.setTextColor(context.accentColor)

@ -110,7 +110,7 @@ class ChapterListFragment : VMBaseFragment<ChapterListViewModel>(R.layout.fragme
observeEvent<BookChapter>(EventBus.SAVE_CONTENT) { chapter -> observeEvent<BookChapter>(EventBus.SAVE_CONTENT) { chapter ->
viewModel.book?.bookUrl?.let { bookUrl -> viewModel.book?.bookUrl?.let { bookUrl ->
if (chapter.bookUrl == bookUrl) { if (chapter.bookUrl == bookUrl) {
adapter.cacheFileNames.add(BookHelp.formatChapterName(chapter)) adapter.cacheFileNames.add(chapter.getFileName())
adapter.notifyItemChanged(chapter.index, true) adapter.notifyItemChanged(chapter.index, true)
} }
} }

@ -4,6 +4,9 @@ import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.text.InputType import android.text.InputType
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View import android.view.View
import androidx.preference.EditTextPreference import androidx.preference.EditTextPreference
import androidx.preference.ListPreference import androidx.preference.ListPreference
@ -11,11 +14,13 @@ import androidx.preference.Preference
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BasePreferenceFragment import io.legado.app.base.BasePreferenceFragment
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.help.LocalConfig
import io.legado.app.help.storage.Restore import io.legado.app.help.storage.Restore
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.filepicker.FilePickerDialog import io.legado.app.ui.filepicker.FilePickerDialog
import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.utils.applyTint import io.legado.app.utils.applyTint
import io.legado.app.utils.getPrefString import io.legado.app.utils.getPrefString
@ -55,6 +60,28 @@ class BackupConfigFragment : BasePreferenceFragment(),
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this) preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
ATH.applyEdgeEffectColor(listView) ATH.applyEdgeEffectColor(listView)
setHasOptionsMenu(true)
if (LocalConfig.isFirstOpenBackup) {
showHelp()
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.backup_restore, menu)
menu.applyTint(requireContext())
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_help -> showHelp()
}
return super.onOptionsItemSelected(item)
}
private fun showHelp() {
val text = String(requireContext().assets.open("help/webDavHelp.md").readBytes())
TextDialog.show(childFragmentManager, text, TextDialog.MD)
} }
override fun onDestroy() { override fun onDestroy() {
@ -126,11 +153,10 @@ class BackupConfigFragment : BasePreferenceFragment(),
multiChoiceItems(Restore.ignoreTitle, checkedItems) { _, which, isChecked -> multiChoiceItems(Restore.ignoreTitle, checkedItems) { _, which, isChecked ->
Restore.ignoreConfig[Restore.ignoreKeys[which]] = isChecked Restore.ignoreConfig[Restore.ignoreKeys[which]] = isChecked
} }
}.show() onDismiss {
.applyTint()
.setOnDismissListener {
Restore.saveIgnoreConfig() Restore.saveIgnoreConfig()
} }
}.show()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

@ -22,8 +22,6 @@ import io.legado.app.help.BookHelp
import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat import io.legado.app.help.permission.PermissionsCompat
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.okButton
import io.legado.app.lib.dialogs.selector import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.receiver.SharedReceiverActivity import io.legado.app.receiver.SharedReceiverActivity
@ -149,15 +147,17 @@ class OtherConfigFragment : BasePreferenceFragment(),
} }
private fun clearCache() { private fun clearCache() {
requireContext().alert(titleResource = R.string.clear_cache, requireContext().alert(
messageResource = R.string.sure_del) { titleResource = R.string.clear_cache,
messageResource = R.string.sure_del
) {
okButton { okButton {
BookHelp.clearCache() BookHelp.clearCache()
FileUtils.deleteFile(requireActivity().cacheDir.absolutePath) FileUtils.deleteFile(requireActivity().cacheDir.absolutePath)
toast(R.string.clear_cache_success) toast(R.string.clear_cache_success)
} }
noButton() noButton()
}.show().applyTint() }.show()
} }
private fun selectDefaultCover() { private fun selectDefaultCover() {

@ -18,9 +18,6 @@ import io.legado.app.help.AppConfig
import io.legado.app.help.LauncherIconHelp import io.legado.app.help.LauncherIconHelp
import io.legado.app.help.ThemeConfig import io.legado.app.help.ThemeConfig
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.okButton
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.ui.widget.number.NumberPickerDialog import io.legado.app.ui.widget.number.NumberPickerDialog
import io.legado.app.ui.widget.prefs.ColorPreference import io.legado.app.ui.widget.prefs.ColorPreference
@ -195,8 +192,8 @@ class ThemeConfigFragment : BasePreferenceFragment(),
} }
} }
} }
noButton { } noButton()
}.show().applyTint() }.show()
} }
private fun upTheme(isNightTheme: Boolean) { private fun upTheme(isNightTheme: Boolean) {

@ -13,8 +13,6 @@ 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.help.ThemeConfig import io.legado.app.help.ThemeConfig
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.okButton
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.* import io.legado.app.utils.*
@ -88,7 +86,7 @@ class ThemeListDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
initData() initData()
} }
noButton() noButton()
}.show().applyTint() }.show()
} }
fun share(index: Int) { fun share(index: Int) {

@ -9,7 +9,6 @@ import io.legado.app.base.BaseActivity
import io.legado.app.help.permission.Permissions import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat import io.legado.app.help.permission.PermissionsCompat
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.utils.applyTint
@Suppress("unused") @Suppress("unused")
object FilePicker { object FilePicker {
@ -62,7 +61,7 @@ object FilePicker {
} }
} }
} }
}.show().applyTint() }.show()
} }
fun selectFolder( fun selectFolder(
@ -113,7 +112,7 @@ object FilePicker {
} }
} }
} }
}.show().applyTint() }.show()
} }
fun selectFile( fun selectFile(
@ -171,7 +170,7 @@ object FilePicker {
} }
} }
} }
}.show().applyTint() }.show()
} }
fun selectFile( fun selectFile(
@ -229,7 +228,7 @@ object FilePicker {
} }
} }
} }
}.show().applyTint() }.show()
} }
private fun createSelectFileIntent(): Intent { private fun createSelectFileIntent(): Intent {

@ -17,6 +17,7 @@ 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.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.LocalConfig
import io.legado.app.help.storage.Backup 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.lib.theme.elevation import io.legado.app.lib.theme.elevation
@ -102,7 +103,10 @@ class MainActivity : VMBaseActivity<MainViewModel>(R.layout.activity_main),
private fun upVersion() { private fun upVersion() {
if (getPrefInt(PreferKey.versionCode) != App.versionCode) { if (getPrefInt(PreferKey.versionCode) != App.versionCode) {
putPrefInt(PreferKey.versionCode, App.versionCode) putPrefInt(PreferKey.versionCode, App.versionCode)
if (!BuildConfig.DEBUG) { if (LocalConfig.isFirstOpenApp) {
val text = String(assets.open("help/appHelp.md").readBytes())
TextDialog.show(supportFragmentManager, text, TextDialog.MD)
} else if (!BuildConfig.DEBUG) {
val log = String(assets.open("updateLog.md").readBytes()) val log = String(assets.open("updateLog.md").readBytes())
TextDialog.show(supportFragmentManager, log, TextDialog.MD, 5000, true) TextDialog.show(supportFragmentManager, log, TextDialog.MD, 5000, true)
} }

@ -16,9 +16,6 @@ import io.legado.app.constant.AppConst
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.okButton
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.book.arrange.ArrangeBookActivity import io.legado.app.ui.book.arrange.ArrangeBookActivity
@ -145,7 +142,7 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
private fun configBookshelf() { private fun configBookshelf() {
requireContext().alert(titleResource = R.string.bookshelf_layout) { alert(titleResource = R.string.bookshelf_layout) {
val bookshelfLayout = getPrefInt(PreferKey.bookshelfLayout) val bookshelfLayout = getPrefInt(PreferKey.bookshelfLayout)
val bookshelfSort = getPrefInt(PreferKey.bookshelfSort) val bookshelfSort = getPrefInt(PreferKey.bookshelfSort)
val root = LayoutInflater.from(requireContext()) val root = LayoutInflater.from(requireContext())
@ -171,7 +168,7 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
} }
} }
noButton() noButton()
}.show().applyTint() }.show()
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
@ -187,9 +184,9 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
editText?.text?.toString()?.let { editText?.text?.toString()?.let {
viewModel.addBookByUrl(it) viewModel.addBookByUrl(it)
} }
} }
noButton { } noButton()
}.show().applyTint() }.show()
} }
override fun onTabReselected(tab: TabLayout.Tab) { override fun onTabReselected(tab: TabLayout.Tab) {

@ -50,7 +50,7 @@ class MyFragment : BaseFragment(R.layout.fragment_my_config), FilePickerDialog.C
override fun onCompatOptionsItemSelected(item: MenuItem) { override fun onCompatOptionsItemSelected(item: MenuItem) {
when (item.itemId) { when (item.itemId) {
R.id.menu_help -> { R.id.menu_help -> {
val text = String(requireContext().assets.open("help/help.md").readBytes()) val text = String(requireContext().assets.open("help/appHelp.md").readBytes())
TextDialog.show(childFragmentManager, text, TextDialog.MD) TextDialog.show(childFragmentManager, text, TextDialog.MD)
} }
} }

@ -17,9 +17,6 @@ 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.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.backgroundColor
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.ui.widget.recycler.VerticalDivider
@ -99,7 +96,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
} }
} }
noButton() noButton()
}.show().applyTint().requestInputMethod() }.show().requestInputMethod()
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
@ -118,7 +115,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
viewModel.upGroup(group, editText?.text?.toString()) viewModel.upGroup(group, editText?.text?.toString())
} }
noButton() noButton()
}.show().applyTint().requestInputMethod() }.show().requestInputMethod()
} }
private inner class GroupAdapter(context: Context) : private inner class GroupAdapter(context: Context) :

@ -22,7 +22,7 @@ import io.legado.app.data.entities.ReplaceRule
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.IntentDataHelp import io.legado.app.help.IntentDataHelp
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.lib.dialogs.* import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.primaryTextColor import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.ui.association.ImportReplaceRuleActivity import io.legado.app.ui.association.ImportReplaceRuleActivity
@ -134,9 +134,8 @@ class ReplaceRuleActivity :
private fun delSourceDialog() { private fun delSourceDialog() {
alert(titleResource = R.string.draw, messageResource = R.string.sure_del) { alert(titleResource = R.string.draw, messageResource = R.string.sure_del) {
okButton { viewModel.delSelection(adapter.getSelection()) } okButton { viewModel.delSelection(adapter.getSelection()) }
noButton { } noButton()
} }.show()
.show().applyTint()
} }
private fun observeReplaceRuleData(key: String? = null) { private fun observeReplaceRuleData(key: String? = null) {
@ -233,7 +232,7 @@ class ReplaceRuleActivity :
} }
} }
cancelButton() cancelButton()
}.show().applyTint() }.show()
} }
override fun onQueryTextChange(newText: String?): Boolean { override fun onQueryTextChange(newText: String?): Boolean {

@ -22,6 +22,7 @@ import io.legado.app.utils.postEvent
import kotlinx.android.synthetic.main.activity_replace_edit.* import kotlinx.android.synthetic.main.activity_replace_edit.*
import org.jetbrains.anko.displayMetrics import org.jetbrains.anko.displayMetrics
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.selector
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
import kotlin.math.abs import kotlin.math.abs
@ -64,8 +65,7 @@ class ReplaceEditActivity :
upReplaceView(it) upReplaceView(it)
} }
iv_help.onClick { iv_help.onClick {
val mdText = String(assets.open("help/regex.md").readBytes()) showRegexHelp()
TextDialog.show(supportFragmentManager, mdText, TextDialog.MD)
} }
} }
@ -128,13 +128,26 @@ class ReplaceEditActivity :
override fun sendText(text: String) { override fun sendText(text: String) {
if (text == AppConst.keyboardToolChars[0]) { if (text == AppConst.keyboardToolChars[0]) {
val view = window?.decorView?.findFocus() showHelpDialog()
view?.clearFocus()
} else { } else {
insertText(text) insertText(text)
} }
} }
private fun showHelpDialog() {
val items = arrayListOf("正则教程")
selector(getString(R.string.help), items) { _, index ->
when (index) {
0 -> showRegexHelp()
}
}
}
private fun showRegexHelp() {
val mdText = String(assets.open("help/regexHelp.md").readBytes())
TextDialog.show(supportFragmentManager, mdText, TextDialog.MD)
}
private fun showKeyboardTopPopupWindow() { private fun showKeyboardTopPopupWindow() {
mSoftKeyboardTool?.let { mSoftKeyboardTool?.let {
if (it.isShowing) return if (it.isShowing) return

@ -16,6 +16,9 @@ import io.legado.app.base.VMBaseActivity
import io.legado.app.lib.theme.DrawableUtils import io.legado.app.lib.theme.DrawableUtils
import io.legado.app.lib.theme.primaryTextColor import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.service.help.Download import io.legado.app.service.help.Download
import io.legado.app.ui.association.ImportBookSourceActivity
import io.legado.app.ui.association.ImportReplaceRuleActivity
import io.legado.app.ui.association.ImportRssSourceActivity
import io.legado.app.ui.filepicker.FilePicker import io.legado.app.ui.filepicker.FilePicker
import io.legado.app.ui.filepicker.FilePickerDialog import io.legado.app.ui.filepicker.FilePickerDialog
import io.legado.app.utils.* import io.legado.app.utils.*
@ -107,11 +110,39 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_r
view: WebView?, view: WebView?,
request: WebResourceRequest? request: WebResourceRequest?
): Boolean { ): Boolean {
if (request?.url?.scheme == "http" || request?.url?.scheme == "https") { request?.let {
return false if (it.url.scheme == "http" || it.url.scheme == "https") {
} return false
request?.url?.let { } else if (it.url.scheme == "yuedu") {
openUrl(it) when (it.url.host) {
"booksource" -> {
val intent = Intent(
this@ReadRssActivity,
ImportBookSourceActivity::class.java
)
intent.data = it.url
startActivity(intent)
}
"rsssource" -> {
val intent = Intent(
this@ReadRssActivity,
ImportRssSourceActivity::class.java
)
intent.data = it.url
startActivity(intent)
}
"replace" -> {
val intent = Intent(
this@ReadRssActivity,
ImportReplaceRuleActivity::class.java
)
intent.data = it.url
startActivity(intent)
}
}
return true
}
openUrl(it.url)
} }
return true return true
} }

@ -20,6 +20,7 @@ import io.legado.app.lib.theme.ATH
import io.legado.app.ui.qrcode.QrCodeActivity import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.ui.rss.source.debug.RssSourceDebugActivity import io.legado.app.ui.rss.source.debug.RssSourceDebugActivity
import io.legado.app.ui.widget.KeyboardToolPop import io.legado.app.ui.widget.KeyboardToolPop
import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_rss_source_edit.* import kotlinx.android.synthetic.main.activity_rss_source_edit.*
import org.jetbrains.anko.* import org.jetbrains.anko.*
@ -56,7 +57,7 @@ class RssSourceEditActivity :
negativeButton(R.string.no) { negativeButton(R.string.no) {
super.finish() super.finish()
} }
}.show().applyTint() }.show()
} else { } else {
super.finish() super.finish()
} }
@ -187,12 +188,28 @@ class RssSourceEditActivity :
override fun sendText(text: String) { override fun sendText(text: String) {
if (text == AppConst.keyboardToolChars[0]) { if (text == AppConst.keyboardToolChars[0]) {
insertText(AppConst.urlOption) showHelpDialog()
} else { } else {
insertText(text) insertText(text)
} }
} }
private fun showHelpDialog() {
val items = arrayListOf("插入URL参数", "订阅源教程", "正则教程")
selector(getString(R.string.help), items) { _, index ->
when (index) {
0 -> insertText(AppConst.urlOption)
1 -> openUrl("https://alanskycn.gitee.io/teachme/Rule/rss.html")
2 -> showRegexHelp()
}
}
}
private fun showRegexHelp() {
val mdText = String(assets.open("help/regexHelp.md").readBytes())
TextDialog.show(supportFragmentManager, mdText, TextDialog.MD)
}
private fun showKeyboardTopPopupWindow() { private fun showKeyboardTopPopupWindow() {
mSoftKeyboardTool?.let { mSoftKeyboardTool?.let {
if (it.isShowing) return if (it.isShowing) return

@ -17,9 +17,6 @@ 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.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
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.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.ui.widget.recycler.VerticalDivider
@ -96,7 +93,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
} }
} }
noButton() noButton()
}.show().applyTint().requestInputMethod() }.show().requestInputMethod()
} }
@SuppressLint("InflateParams") @SuppressLint("InflateParams")
@ -115,7 +112,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
viewModel.upGroup(group, editText?.text?.toString()) viewModel.upGroup(group, editText?.text?.toString())
} }
noButton() noButton()
}.show().applyTint().requestInputMethod() }.show().requestInputMethod()
} }
private inner class GroupAdapter(context: Context) : private inner class GroupAdapter(context: Context) :

@ -20,7 +20,7 @@ import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.help.IntentDataHelp import io.legado.app.help.IntentDataHelp
import io.legado.app.lib.dialogs.* import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.primaryTextColor import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.ui.association.ImportRssSourceActivity import io.legado.app.ui.association.ImportRssSourceActivity
@ -180,9 +180,8 @@ class RssSourceActivity : VMBaseActivity<RssSourceViewModel>(R.layout.activity_r
private fun delSourceDialog() { private fun delSourceDialog() {
alert(titleResource = R.string.draw, messageResource = R.string.sure_del) { alert(titleResource = R.string.draw, messageResource = R.string.sure_del) {
okButton { viewModel.delSelection(adapter.getSelection()) } okButton { viewModel.delSelection(adapter.getSelection()) }
noButton { } noButton()
} }.show()
.show().applyTint()
} }
private fun upGroupMenu() { private fun upGroupMenu() {
@ -243,7 +242,7 @@ class RssSourceActivity : VMBaseActivity<RssSourceViewModel>(R.layout.activity_r
} }
} }
cancelButton() cancelButton()
}.show().applyTint() }.show()
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

@ -28,6 +28,7 @@ import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.io.File import java.io.File
import java.util.* import java.util.*
import kotlin.collections.ArrayList
class FontSelectDialog : BaseDialogFragment(), class FontSelectDialog : BaseDialogFragment(),
FilePickerDialog.CallBack, FilePickerDialog.CallBack,
@ -150,8 +151,7 @@ class FontSelectDialog : BaseDialogFragment(),
fontItems.add(item) fontItems.add(item)
} }
} }
fontItems.addAll(getLocalFonts()) mergeFontItems(fontItems, getLocalFonts())
fontItems.sortedBy { it.name }
}.onSuccess { }.onSuccess {
adapter?.setItems(it) adapter?.setItems(it)
}.onError { }.onError {
@ -186,8 +186,7 @@ class FontSelectDialog : BaseDialogFragment(),
) )
) )
} }
fontItems.addAll(getLocalFonts()) mergeFontItems(fontItems, getLocalFonts())
fontItems.sortedBy { it.name }
}.onSuccess { }.onSuccess {
adapter?.setItems(it) adapter?.setItems(it)
}.onError { }.onError {
@ -195,6 +194,27 @@ class FontSelectDialog : BaseDialogFragment(),
} }
} }
private fun mergeFontItems(
items1: ArrayList<DocItem>,
items2: ArrayList<DocItem>
): ArrayList<DocItem> {
val items = ArrayList(items1)
items2.forEach { item2 ->
var isInFirst = false
items1.forEach for1@{ item1 ->
if (item2.name == item1.name) {
isInFirst = true
return@for1
}
}
if (!isInFirst) {
items.add(item2)
}
}
items.sortBy { it.name }
return items
}
override fun onClick(docItem: DocItem) { override fun onClick(docItem: DocItem) {
execute { execute {
FileUtils.deleteFile(fontFolder.absolutePath) FileUtils.deleteFile(fontFolder.absolutePath)

@ -9,6 +9,7 @@ import io.legado.app.utils.ColorUtils
import io.legado.app.utils.dp import io.legado.app.utils.dp
import io.legado.app.utils.getCompatColor import io.legado.app.utils.getCompatColor
@Suppress("unused")
open class StrokeTextView(context: Context, attrs: AttributeSet?) : open class StrokeTextView(context: Context, attrs: AttributeSet?) :
AppCompatTextView(context, attrs) { AppCompatTextView(context, attrs) {
@ -40,7 +41,7 @@ open class StrokeTextView(context: Context, attrs: AttributeSet?) :
.setSelectedStrokeColor(context.getCompatColor(R.color.accent)) .setSelectedStrokeColor(context.getCompatColor(R.color.accent))
.setPressedBgColor(context.getCompatColor(R.color.transparent30)) .setPressedBgColor(context.getCompatColor(R.color.transparent30))
.create() .create()
this.setTextColor( setTextColor(
Selector.colorBuild() Selector.colorBuild()
.setDefaultColor(context.getCompatColor(R.color.secondaryText)) .setDefaultColor(context.getCompatColor(R.color.secondaryText))
.setSelectedColor(context.getCompatColor(R.color.accent)) .setSelectedColor(context.getCompatColor(R.color.accent))
@ -58,7 +59,7 @@ open class StrokeTextView(context: Context, attrs: AttributeSet?) :
.setSelectedStrokeColor(context.accentColor) .setSelectedStrokeColor(context.accentColor)
.setPressedBgColor(context.getCompatColor(R.color.transparent30)) .setPressedBgColor(context.getCompatColor(R.color.transparent30))
.create() .create()
this.setTextColor( setTextColor(
Selector.colorBuild() Selector.colorBuild()
.setDefaultColor(context.getPrimaryTextColor(isLight)) .setDefaultColor(context.getPrimaryTextColor(isLight))
.setSelectedColor(context.accentColor) .setSelectedColor(context.accentColor)
@ -75,7 +76,7 @@ open class StrokeTextView(context: Context, attrs: AttributeSet?) :
.setSelectedStrokeColor(ThemeStore.accentColor(context)) .setSelectedStrokeColor(ThemeStore.accentColor(context))
.setPressedBgColor(context.getCompatColor(R.color.transparent30)) .setPressedBgColor(context.getCompatColor(R.color.transparent30))
.create() .create()
this.setTextColor( setTextColor(
Selector.colorBuild() Selector.colorBuild()
.setDefaultColor(ThemeStore.textColorSecondary(context)) .setDefaultColor(ThemeStore.textColorSecondary(context))
.setSelectedColor(ThemeStore.accentColor(context)) .setSelectedColor(ThemeStore.accentColor(context))

@ -1,7 +1,6 @@
package io.legado.app.utils package io.legado.app.utils
import android.util.Base64 import android.util.Base64
import java.nio.charset.StandardCharsets
@Suppress("unused") @Suppress("unused")
object EncoderUtils { object EncoderUtils {
@ -28,11 +27,7 @@ object EncoderUtils {
@JvmOverloads @JvmOverloads
fun base64Decode(str: String, flags: Int = Base64.DEFAULT): String { fun base64Decode(str: String, flags: Int = Base64.DEFAULT): String {
val bytes = Base64.decode(str, flags) val bytes = Base64.decode(str, flags)
return try { return String(bytes)
String(bytes, StandardCharsets.UTF_8)
} catch (e: Exception) {
String(bytes)
}
} }
@JvmOverloads @JvmOverloads

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

Loading…
Cancel
Save