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 {
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'])
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test:runner:1.3.0'

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

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

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

@ -1,4 +1,4 @@
## 正则表达式学习
# 正则表达式学习
- [基本匹配](#1-基本匹配)
- [元字符](#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)【我的】->【备份与恢复】,选择【导入旧版本数据】。
**2020/11/13**
* 正文规则添加字体规则,返回ByteArray
* js添加方法:
```
base64DecodeToByteArray(str: String?): ByteArray?
base64DecodeToByteArray(str: String?, flags: Int): ByteArray?
```
**2020/11/08**
* 优化书源,订阅源导入,添加保持原名选项
* 优化使用体验,初次使用是自动弹出帮助文档
* 添加阅读界面的点击区域设置
* 滚动翻页时自动翻页也采用滚动模式
**2020/11/07**
* 详情页菜单添加拷贝URL
* 解决一些书名太长缓存报错的bug

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

@ -5,8 +5,15 @@ object PreferKey {
const val language = "language"
const val themeMode = "themeMode"
const val hideStatusBar = "hideStatusBar"
const val clickTurnPage = "clickTurnPage"
const val clickAllNext = "clickAllNext"
const val clickActionTopLeft = "clickActionTopLeft"
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 precisionSearch = "precisionSearch"
const val speakEngine = "speakEngine"
@ -53,6 +60,7 @@ object PreferKey {
const val showBrightnessView = "showBrightnessView"
const val autoClearExpired = "autoClearExpired"
const val autoChangeSource = "autoChangeSource"
const val importKeepName = "importKeepName"
const val cPrimary = "colorPrimary"
const val cAccent = "colorAccent"

@ -6,6 +6,7 @@ import androidx.room.ForeignKey
import androidx.room.Ignore
import androidx.room.Index
import io.legado.app.utils.GSON
import io.legado.app.utils.MD5Utils
import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.fromJsonObject
import kotlinx.android.parcel.IgnoredOnParcel
@ -62,5 +63,9 @@ data class BookChapter(
fun getAbsoluteURL(): String {
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 sourceRegex: String? = null,
var replaceRegex: String? = null,
var imageStyle: String? = null //默认大小居中,FULL最大宽度
var imageStyle: String? = null, //默认大小居中,FULL最大宽度
var font: String? = null //必须返回ByteArray
) : Parcelable

@ -134,5 +134,19 @@ object AppConfig {
val changeSourceLoadInfo 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 downloadImages = CopyOnWriteArraySet<String>()
fun formatChapterName(bookChapter: BookChapter): String {
return String.format(
"%05d-%s.nb",
bookChapter.index,
MD5Utils.md5Encode16(bookChapter.title)
)
}
fun clearCache() {
FileUtils.deleteFile(
FileUtils.getPath(downloadDir, cacheFolderName)
@ -75,7 +67,7 @@ object BookHelp {
downloadDir,
cacheFolderName,
book.getFolderName(),
formatChapterName(bookChapter),
bookChapter.getFileName(),
).writeText(content)
//保存图片
content.split("\n").forEach {
@ -91,6 +83,30 @@ object BookHelp {
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) {
while (downloadImages.contains(src)) {
delay(100)
@ -158,7 +174,7 @@ object BookHelp {
downloadDir,
cacheFolderName,
book.getFolderName(),
formatChapterName(bookChapter)
bookChapter.getFileName()
)
}
}
@ -171,7 +187,7 @@ object BookHelp {
downloadDir,
cacheFolderName,
book.getFolderName(),
formatChapterName(bookChapter)
bookChapter.getFileName()
)
if (file.exists()) {
return file.readText()
@ -188,7 +204,7 @@ object BookHelp {
downloadDir,
cacheFolderName,
book.getFolderName(),
formatChapterName(bookChapter)
bookChapter.getFileName()
).delete()
}
}

@ -121,10 +121,10 @@ interface JsExtensions {
.execute()
}
/**
*js实现读取cookie
*/
fun getCookie(tag: String, key: String? = null): String {
/**
*js实现读取cookie
*/
fun getCookie(tag: String, key: String? = null): String {
val cookie = CookieStore.getCookie(tag)
val cookieMap = CookieStore.cookieToMap(cookie)
return if (key != null) {
@ -132,7 +132,7 @@ interface JsExtensions {
} else {
cookie
}
}
}
/**
* js实现解码,不能删
@ -191,4 +191,19 @@ interface JsExtensions {
fun htmlFormat(str: String): String {
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)
}
}
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 textBottomJustify get() = App.INSTANCE.getPrefBoolean(PreferKey.textBottomJustify, true)
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")
package io.legado.app.lib.dialogs
@ -63,19 +47,34 @@ interface AlertBuilder<out D : DialogInterface> {
@Deprecated(NO_GETTER, level = ERROR) get
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(@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(@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 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(
items: List<T>,
onItemSelected: (dialog: DialogInterface, item: T, index: Int) -> Unit
@ -95,24 +94,25 @@ interface AlertBuilder<out D : DialogInterface> {
fun build(): D
fun show(): D
}
fun AlertBuilder<*>.customTitle(view: () -> View) {
customTitle = view()
}
fun AlertBuilder<*>.customView(view: () -> View) {
customView = view()
}
fun customTitle(view: () -> View) {
customTitle = view()
}
fun customView(view: () -> View) {
customView = view()
}
inline fun AlertBuilder<*>.okButton(noinline handler: ((dialog: DialogInterface) -> Unit)? = null) =
positiveButton(android.R.string.ok, handler)
fun okButton(handler: ((dialog: DialogInterface) -> Unit)? = null) =
positiveButton(android.R.string.ok, handler)
inline fun AlertBuilder<*>.cancelButton(noinline handler: ((dialog: DialogInterface) -> Unit)? = null) =
negativeButton(android.R.string.cancel, handler)
fun cancelButton(handler: ((dialog: DialogInterface) -> Unit)? = null) =
negativeButton(android.R.string.cancel, handler)
inline fun AlertBuilder<*>.yesButton(noinline handler: ((dialog: DialogInterface) -> Unit)? = null) =
positiveButton(R.string.yes, handler)
fun yesButton(handler: ((dialog: DialogInterface) -> Unit)? = null) =
positiveButton(R.string.yes, handler)
inline fun AlertBuilder<*>.noButton(noinline handler: ((dialog: DialogInterface) -> Unit)? = null) =
negativeButton(R.string.no, handler)
fun noButton(handler: ((dialog: DialogInterface) -> Unit)? = null) =
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
import android.content.Context
@ -22,12 +6,11 @@ import android.graphics.drawable.Drawable
import android.view.KeyEvent
import android.view.View
import androidx.appcompat.app.AlertDialog
import io.legado.app.utils.applyTint
import org.jetbrains.anko.internals.AnkoInternals
import org.jetbrains.anko.internals.AnkoInternals.NO_GETTER
import kotlin.DeprecationLevel.ERROR
val Android: AlertBuilderFactory<AlertDialog> = ::AndroidAlertBuilder
internal class AndroidAlertBuilder(override val ctx: Context) : AlertBuilder<AlertDialog> {
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) }
}
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) }
}
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) }
}
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 ->
onItemSelected(dialog, which)
}
@ -136,5 +132,5 @@ internal class AndroidAlertBuilder(override val ctx: Context) : AlertBuilder<Ale
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")
package io.legado.app.lib.dialogs

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

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

@ -17,22 +17,22 @@ import java.util.*
* 书源规则解析
*/
@Keep
class AnalyzeByJSoup {
class AnalyzeByJSoup(doc: Any) {
companion object {
val validKeys = arrayOf("class", "id", "tag", "text", "children")
}
private lateinit var element: Element
fun parse(doc: Any): AnalyzeByJSoup {
element = when (doc) {
is Element -> doc
is JXNode -> if (doc.isElement) doc.asElement() else Jsoup.parse(doc.toString())
else -> Jsoup.parse(doc.toString())
fun parse(doc: Any): Element {
return when (doc) {
is Element -> doc
is JXNode -> if (doc.isElement) doc.asElement() 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.*
@Keep
class AnalyzeByXPath {
private lateinit var jxNode: Any
class AnalyzeByXPath(doc: Any) {
private var jxNode: Any = parse(doc)
fun parse(doc: Any): AnalyzeByXPath {
jxNode = when (doc) {
private fun parse(doc: Any): Any {
return when (doc) {
is JXNode -> if (doc.isElement) doc else strToJXDocument(doc.toString())
is Document -> JXDocument.create(doc)
is Element -> JXDocument.create(Elements(doc))
is Elements -> JXDocument.create(doc)
else -> strToJXDocument(doc.toString())
}
return this
}
private fun strToJXDocument(html: String): JXDocument {

@ -65,17 +65,14 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
*/
private fun getAnalyzeByXPath(o: Any): AnalyzeByXPath {
return if (o != content) {
AnalyzeByXPath().parse(o)
} else getAnalyzeByXPath()
}
private fun getAnalyzeByXPath(): AnalyzeByXPath {
if (analyzeByXPath == null || objectChangedXP) {
analyzeByXPath = AnalyzeByXPath()
analyzeByXPath?.parse(content!!)
objectChangedXP = false
AnalyzeByXPath(o)
} else {
if (analyzeByXPath == null || objectChangedXP) {
analyzeByXPath = AnalyzeByXPath(content!!)
objectChangedXP = false
}
analyzeByXPath!!
}
return analyzeByXPath as AnalyzeByXPath
}
/**
@ -83,17 +80,14 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
*/
private fun getAnalyzeByJSoup(o: Any): AnalyzeByJSoup {
return if (o != content) {
AnalyzeByJSoup().parse(o)
} else getAnalyzeByJSoup()
}
private fun getAnalyzeByJSoup(): AnalyzeByJSoup {
if (analyzeByJSoup == null || objectChangedJS) {
analyzeByJSoup = AnalyzeByJSoup()
analyzeByJSoup?.parse(content!!)
objectChangedJS = false
AnalyzeByJSoup(o)
} else {
if (analyzeByJSoup == null || objectChangedJS) {
analyzeByJSoup = AnalyzeByJSoup(content!!)
objectChangedJS = false
}
analyzeByJSoup!!
}
return analyzeByJSoup as AnalyzeByJSoup
}
/**
@ -101,17 +95,14 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
*/
private fun getAnalyzeByJSonPath(o: Any): AnalyzeByJSonPath {
return if (o != content) {
AnalyzeByJSonPath().parse(o)
} else getAnalyzeByJSonPath()
}
private fun getAnalyzeByJSonPath(): AnalyzeByJSonPath {
if (analyzeByJSonPath == null || objectChangedJP) {
analyzeByJSonPath = AnalyzeByJSonPath()
analyzeByJSonPath?.parse(content!!)
objectChangedJP = false
AnalyzeByJSonPath(o)
} else {
if (analyzeByJSonPath == null || objectChangedJP) {
analyzeByJSonPath = AnalyzeByJSonPath(content!!)
objectChangedJP = false
}
analyzeByJSonPath!!
}
return analyzeByJSonPath as AnalyzeByJSonPath
}
/**
@ -124,6 +115,7 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
return getStringList(ruleList, isUrl)
}
@JvmOverloads
fun getStringList(ruleList: List<SourceRule>, isUrl: Boolean = false): List<String>? {
var result: Any? = null
val content = this.content
@ -181,6 +173,7 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
/**
* 获取文本
*/
@JvmOverloads
fun getString(ruleStr: String?, isUrl: Boolean = false): String {
if (TextUtils.isEmpty(ruleStr)) return ""
val ruleList = splitSourceRule(ruleStr)
@ -302,6 +295,33 @@ class AnalyzeRule(var book: BaseBook? = null) : JsExtensions {
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
) : JsExtensions {
companion object {
private val splitUrlRegex = Regex(",\\s*(?=\\{)")
private val pagePattern = Pattern.compile("<(.*?)>")
private val jsonType = MediaType.parse("application/json; charset=utf-8")
}
@ -59,7 +60,6 @@ class AnalyzeUrl(
private var charset: String? = null
private var requestBody: RequestBody? = null
private var method = RequestMethod.GET
private val splitUrlRegex = Regex(",\\s*(?=\\{)")
private var proxy: String? = null
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.BookSource
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.analyzeRule.AnalyzeRule
import io.legado.app.model.analyzeRule.AnalyzeUrl
@ -33,6 +34,14 @@ object BookContent {
val content = StringBuilder()
val nextUrlList = arrayListOf(baseUrl)
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(
book, baseUrl, body, contentRule, bookChapter, bookSource
)
@ -54,12 +63,10 @@ object BookContent {
ruleUrl = nextUrl,
book = book,
headerMapF = bookSource.getHeaderMap()
).getResponseAwait(bookSource.bookSourceUrl)
.body?.let { nextBody ->
contentData =
analyzeContent(
book, nextUrl, nextBody, contentRule, bookChapter, bookSource, false
)
).getResponseAwait(bookSource.bookSourceUrl).body?.let { nextBody ->
contentData = analyzeContent(
book, nextUrl, nextBody, contentRule, bookChapter, bookSource, false
)
nextUrl =
if (contentData.nextUrl.isNotEmpty()) contentData.nextUrl[0] else ""
content.append(contentData.content).append("\n")
@ -78,14 +85,12 @@ object BookContent {
ruleUrl = item.nextUrl,
book = book,
headerMapF = bookSource.getHeaderMap()
).getResponseAwait(bookSource.bookSourceUrl)
.body?.let {
contentData =
analyzeContent(
book, item.nextUrl, it, contentRule, bookChapter, bookSource, false
)
item.content = contentData.content
}
).getResponseAwait(bookSource.bookSourceUrl).body?.let {
contentData = analyzeContent(
book, item.nextUrl, it, contentRule, bookChapter, bookSource, false
)
item.content = contentData.content
}
}
}
for (item in contentDataList) {
@ -105,6 +110,9 @@ object BookContent {
Debug.log(bookSource.bookSourceUrl, "${bookChapter.title}")
Debug.log(bookSource.bookSourceUrl, "┌获取正文内容")
Debug.log(bookSource.bookSourceUrl, "\n$contentStr")
if (contentStr.isNotBlank()) {
BookHelp.saveContent(book, bookChapter, contentStr)
}
return contentStr
}

@ -24,7 +24,6 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction
import io.legado.app.constant.Status
import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
import io.legado.app.help.IntentHelp
import io.legado.app.help.MediaHelp
import io.legado.app.model.analyzeRule.AnalyzeUrl
@ -284,23 +283,25 @@ class AudioPlayService : BaseService(),
}
private fun loadContent(chapter: BookChapter) {
AudioPlay.book?.let { book ->
AudioPlay.webBook?.getContent(book, chapter, scope = this)
?.onSuccess(IO) { content ->
val book = AudioPlay.book
val webBook = AudioPlay.webBook
if (book != null && webBook != null) {
webBook.getContent(book, chapter, scope = this@AudioPlayService)
.onSuccess { content ->
removeLoading(chapter.index)
if (content.isEmpty()) {
withContext(Main) {
toast("未获取到资源链接")
}
removeLoading(chapter.index)
} else {
BookHelp.saveContent(book, chapter, content)
contentLoadFinish(chapter, content)
removeLoading(chapter.index)
}
}?.onError {
}.onError {
contentLoadFinish(chapter, it.localizedMessage ?: toString())
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.service.help.CacheBook
import io.legado.app.utils.postEvent
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.isActive
import org.jetbrains.anko.toast
@ -197,8 +196,7 @@ class CacheBookService : BaseService() {
CacheBook.addLog("getContentError${it.localizedMessage}")
updateNotification("getContentError${it.localizedMessage}")
}
.onSuccess(IO) { content ->
BookHelp.saveContent(book, bookChapter, content)
.onSuccess {
synchronized(this@CacheBookService) {
downloadCount[book.bookUrl]?.increaseSuccess()
downloadCount[book.bookUrl]?.increaseFinished()
@ -221,7 +219,7 @@ class CacheBookService : BaseService() {
downloadCount.remove(book.bookUrl)
}
}
}.onFinally(IO) {
}.onFinally {
postDownloading(true)
}
} else {

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

@ -11,7 +11,6 @@ import io.legado.app.App
import io.legado.app.R
import io.legado.app.lib.dialogs.alert
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.sendMail
import io.legado.app.utils.sendToClip
@ -85,7 +84,7 @@ class AboutFragment : PreferenceFragmentCompat() {
}
}
}
}.show().applyTint()
}.show()
}
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.data.entities.ReadRecordShow
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.item_read_record.*
import kotlinx.android.synthetic.main.item_read_record.view.*
@ -41,7 +38,7 @@ class ReadRecordActivity : BaseActivity(R.layout.activity_read_record) {
initData()
}
noButton()
}.show().applyTint()
}.show()
}
}
@ -83,7 +80,7 @@ class ReadRecordActivity : BaseActivity(R.layout.activity_read_record) {
}
}
noButton()
}.show().applyTint()
}.show()
}
}
}

@ -1,38 +1,13 @@
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.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.data.entities.BookSource
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.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.visible
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
@ -97,167 +72,14 @@ class ImportBookSourceActivity : VMBaseActivity<ImportBookSourceViewModel>(
private fun errorDialog(msg: String) {
alert(getString(R.string.error), msg) {
okButton { }
}.show().applyTint().setOnDismissListener {
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()
}
onDismiss {
finish()
}
}
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())
}
}.show()
}
class SourcesAdapter(context: Context) :
SimpleRecyclerAdapter<BookSource>(context, R.layout.item_source_import) {
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
}
}
}
}
private fun successDialog() {
ImportBookSourceDialog().show(supportFragmentManager, "SourceDialog")
}
}

@ -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.base.BaseViewModel
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.storage.OldRule
import io.legado.app.help.storage.Restore
@ -16,14 +18,41 @@ import io.legado.app.utils.*
import java.io.File
class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) {
var groupName: String? = null
val errorLiveData = MutableLiveData<String>()
val successLiveData = MutableLiveData<Int>()
val allSources = arrayListOf<BookSource>()
val sourceCheckState = arrayListOf<Boolean>()
val checkSources = arrayListOf<BookSource?>()
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) {
execute {
val content = if (path.isContentScheme()) {
@ -106,9 +135,9 @@ class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) {
private fun comparisonSource() {
execute {
allSources.forEach {
val has = App.db.bookSourceDao().getBookSource(it.bookSourceUrl) != null
sourceCheckState.add(has)
selectStatus.add(!has)
val has = App.db.bookSourceDao().getBookSource(it.bookSourceUrl)
checkSources.add(has)
selectStatus.add(has == null)
}
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.help.IntentDataHelp
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 kotlinx.android.synthetic.main.activity_translucence.*
import org.jetbrains.anko.toast
@ -76,9 +73,10 @@ class ImportReplaceRuleActivity : VMBaseActivity<ImportReplaceRuleViewModel>(
private fun errorDialog(msg: String) {
alert("导入出错", msg) {
okButton { }
}.show().applyTint().setOnDismissListener {
finish()
}
onDismiss {
finish()
}
}.show()
}
private fun successDialog(allSource: ArrayList<ReplaceRule>) {
@ -86,11 +84,10 @@ class ImportReplaceRuleActivity : VMBaseActivity<ImportReplaceRuleViewModel>(
okButton {
App.db.replaceRuleDao().insert(*allSource.toTypedArray())
}
noButton {
noButton()
onDismiss {
finish()
}
}.show().applyTint().setOnDismissListener {
finish()
}
}.show()
}
}

@ -1,33 +1,13 @@
package io.legado.app.ui.association
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.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.data.entities.RssSource
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.okButton
import io.legado.app.utils.applyTint
import io.legado.app.utils.getSize
import io.legado.app.utils.getViewModel
import io.legado.app.utils.visible
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
class ImportRssSourceActivity : VMBaseActivity<ImportRssSourceViewModel>(
@ -91,145 +71,14 @@ class ImportRssSourceActivity : VMBaseActivity<ImportRssSourceViewModel>(
private fun errorDialog(msg: String) {
alert(getString(R.string.error), msg) {
okButton { }
}.show().applyTint().setOnDismissListener {
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()
}
onDismiss {
finish()
}
}
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())
}
}.show()
}
class SourcesAdapter(context: Context) :
SimpleRecyclerAdapter<RssSource>(context, R.layout.item_source_import) {
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
}
}
}
}
private fun successDialog() {
ImportRssSourceDialog().show(supportFragmentManager, "SourceDialog")
}
}

@ -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.base.BaseViewModel
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.storage.Restore
import io.legado.app.utils.*
import java.io.File
class ImportRssSourceViewModel(app: Application) : BaseViewModel(app) {
var groupName: String? = null
val errorLiveData = MutableLiveData<String>()
val successLiveData = MutableLiveData<Int>()
val allSources = arrayListOf<RssSource>()
val sourceCheckState = arrayListOf<Boolean>()
val checkSources = arrayListOf<RssSource?>()
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) {
execute {
val content = if (path.isContentScheme()) {
@ -101,9 +128,9 @@ class ImportRssSourceViewModel(app: Application) : BaseViewModel(app) {
private fun comparisonSource() {
execute {
allSources.forEach {
val has = App.db.rssSourceDao().getByKey(it.sourceUrl) != null
sourceCheckState.add(has)
selectStatus.add(!has)
val has = App.db.rssSourceDao().getByKey(it.sourceUrl)
checkSources.add(has)
selectStatus.add(has == null)
}
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.ImageLoader
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.help.AudioPlay
import io.legado.app.ui.book.changesource.ChangeSourceDialog
@ -150,14 +148,14 @@ class AudioPlayActivity :
override fun finish() {
AudioPlay.book?.let {
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)
okButton {
AudioPlay.inBookshelf = true
setResult(Activity.RESULT_OK)
}
noButton { viewModel.removeFromBookshelf { super.finish() } }
}.show().applyTint()
}.show()
} else {
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.BookGroup
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.ui.book.group.GroupManageDialog
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.ItemTouchCallback
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.getViewModel
import kotlinx.android.synthetic.main.activity_arrange_book.*
@ -149,8 +146,8 @@ class ArrangeBookActivity : VMBaseActivity<ArrangeBookViewModel>(R.layout.activi
R.id.menu_del_selection ->
alert(titleResource = R.string.draw, messageResource = R.string.sure_del) {
okButton { viewModel.deleteBook(*adapter.selectedBooks()) }
noButton { }
}.show().applyTint()
noButton()
}.show()
R.id.menu_update_enable ->
viewModel.upCanUpdate(adapter.selectedBooks(), true)
R.id.menu_update_disable ->
@ -210,7 +207,7 @@ class ArrangeBookActivity : VMBaseActivity<ArrangeBookViewModel>(R.layout.activi
okButton {
viewModel.deleteBook(book)
}
}.show().applyTint()
}.show()
}
}

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

@ -157,7 +157,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
webBook.getBookInfo(book, this)
.onSuccess {
if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
loadChapter(webBook, book)
loadBookToc(webBook, book)
} else {
//从详情页里获取最新章节
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)
.onSuccess(IO) { chapters ->
if (chapters.isNotEmpty()) {

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

@ -65,6 +65,7 @@ class BookInfoActivity :
scroll_view.setBackgroundColor(backgroundColor)
fl_action.setBackgroundColor(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.chapterListData.observe(this, { upLoading(false, it) })
viewModel.initData(intent)
@ -136,7 +137,6 @@ class BookInfoActivity :
tv_author.text = getString(R.string.author_show, book.getRealAuthor())
tv_origin.text = getString(R.string.origin_show, book.originName)
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()
upTvBookshelf()
val kinds = book.getKindList()

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

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

@ -4,9 +4,6 @@ import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
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.Handler
import android.os.Looper
@ -18,21 +15,15 @@ import androidx.core.view.size
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
import io.legado.app.BuildConfig
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.constant.Status
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
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.SyncBookProgress
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.receiver.TimeBatteryReceiver
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.delegate.PageDelegate
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.login.SourceLogin
import io.legado.app.ui.replace.ReplaceRuleActivity
@ -63,12 +53,11 @@ import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.startActivity
import org.jetbrains.anko.startActivityForResult
import org.jetbrains.anko.toast
class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_book_read),
class ReadBookActivity : ReadBookBaseActivity(),
View.OnTouchListener,
PageView.CallBack,
TextActionMenu.CallBack,
@ -81,21 +70,17 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
TocRegexDialog.CallBack,
ColorPickerDialogListener {
private val requestCodeChapterList = 568
private val requestCodeEditSource = 111
private val requestCodeReplace = 312
private val requestCodeSearchResult = 123
private var menu: Menu? = null
private var textActionMenu: TextActionMenu? = null
override val viewModel: ReadBookViewModel
get() = getViewModel(ReadBookViewModel::class.java)
override val scope: CoroutineScope get() = this
override val isInitFinish: Boolean get() = viewModel.isInitFinish
override val isScroll: Boolean get() = page_view.isScroll
private val mHandler = Handler(Looper.getMainLooper())
private val keepScreenRunnable: Runnable =
Runnable { ReadBookActivityHelp.keepScreenOn(window, false) }
Runnable { keepScreenOn(window, false) }
private val autoPageRunnable: Runnable = Runnable { autoPagePlus() }
override var autoPageProgress = 0
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 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?) {
ReadBookActivityHelp.upLayoutInDisplayCutoutMode(window)
initView()
super.onActivityCreated(savedInstanceState)
cursor_left.setColorFilter(accentColor)
cursor_right.setColorFilter(accentColor)
cursor_left.setOnTouchListener(this)
cursor_right.setOnTouchListener(this)
upScreenTimeOut()
ReadBook.callBack = 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 {
menuInflater.inflate(R.menu.read_book, menu)
return super.onCompatCreateOptionsMenu(menu)
@ -257,8 +190,8 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
viewModel.refreshContent(it)
}
}
R.id.menu_download -> ReadBookActivityHelp.showDownloadDialog(this)
R.id.menu_add_bookmark -> ReadBookActivityHelp.showBookMark(this)
R.id.menu_download -> showDownloadDialog()
R.id.menu_add_bookmark -> showBookMark()
R.id.menu_copy_text ->
TextDialog.show(supportFragmentManager, ReadBook.curTextChapter?.getContent())
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 {
it.setUseReplaceRule(!it.getUseReplaceRule())
menu?.findItem(R.id.menu_enable_replace)?.isChecked = it.getUseReplaceRule()
onReplaceRuleSave()
viewModel.replaceRuleChanged()
}
R.id.menu_re_segment -> ReadBook.book?.let {
it.setReSegment(!it.getReSegment())
menu?.findItem(R.id.menu_re_segment)?.isChecked = it.getReSegment()
ReadBook.loadContent(false)
}
R.id.menu_page_anim -> ReadBookActivityHelp.showPageAnimConfig(this) {
R.id.menu_page_anim -> showPageAnimConfig {
page_view.upPageAnim()
}
R.id.menu_book_info -> ReadBook.book?.let {
@ -293,7 +226,8 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
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)
}
@ -324,13 +258,13 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
*/
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
when {
ReadBookActivityHelp.isPrevKey(this, keyCode) -> {
isPrevKey(keyCode) -> {
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
page_view.pageDelegate?.keyTurnPage(PageDelegate.Direction.PREV)
return true
}
}
ReadBookActivityHelp.isNextKey(this, keyCode) -> {
isNextKey(keyCode) -> {
if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
page_view.pageDelegate?.keyTurnPage(PageDelegate.Direction.NEXT)
return true
@ -622,7 +556,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
viewModel.changeTo(book)
}
override fun clickCenter() {
override fun showActionMenu() {
when {
BaseReadAloudService.isRun -> {
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() {
ReadAloud.stop(this)
if (isAutoPage) {
autoPageStop()
} else {
@ -666,14 +606,18 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
private fun autoPagePlus() {
mHandler.removeCallbacks(autoPageRunnable)
autoPageProgress++
if (autoPageProgress >= ReadBookConfig.autoReadSpeed * 10) {
autoPageProgress = 0
page_view.fillPage(PageDelegate.Direction.NEXT)
if (page_view.isScroll) {
page_view.curPage.scroll(-page_view.height / ReadBookConfig.autoReadSpeed / 50)
} 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() {
ReadBookActivityHelp.upSystemUiVisibility(this, isInMultiWindow, !read_menu.isVisible)
upSystemUiVisibility(isInMultiWindow, !read_menu.isVisible)
upNavigationBarColor()
}
@ -744,6 +678,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
* 朗读按钮
*/
override fun onClickReadAloud() {
autoPageStop()
when {
!BaseReadAloudService.isRun -> ReadBook.readAloud()
BaseReadAloudService.pause -> ReadAloud.resume(this)
@ -784,7 +719,6 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
requestCodeEditSource -> viewModel.upBookSource()
requestCodeChapterList ->
data?.getIntExtra("index", ReadBook.durChapterIndex)?.let { index ->
if (index != ReadBook.durChapterIndex) {
@ -840,23 +774,22 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
}
}
}
requestCodeReplace -> onReplaceRuleSave()
requestCodeReplace -> viewModel.replaceRuleChanged()
}
}
}
override fun finish() {
ReadBook.book?.let {
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)
okButton {
ReadBook.inBookshelf = true
setResult(Activity.RESULT_OK)
}
noButton { viewModel.removeFromBookshelf { super.finish() } }
}.show().applyTint()
}.show()
} else {
super.finish()
}
@ -934,7 +867,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
read_menu.upBrightnessState()
}
observeEvent<String>(EventBus.REPLACE_RULE_SAVE) {
onReplaceRuleSave()
viewModel.replaceRuleChanged()
}
}
@ -950,16 +883,16 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
*/
override fun screenOffTimerStart() {
if (screenTimeOut < 0) {
ReadBookActivityHelp.keepScreenOn(window, true)
keepScreenOn(window, true)
return
}
val t = screenTimeOut - sysScreenOffTime
if (t > 0) {
mHandler.removeCallbacks(keepScreenRunnable)
ReadBookActivityHelp.keepScreenOn(window, true)
keepScreenOn(window, true)
mHandler.postDelayed(keepScreenRunnable, screenTimeOut)
} 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.app.Activity
import android.content.Context
import android.content.Intent
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.Bundle
import android.view.*
import android.widget.EditText
import androidx.core.view.isVisible
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Bookmark
import io.legado.app.help.AppConfig
import io.legado.app.help.LocalConfig
import io.legado.app.help.ReadBookConfig
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.lib.dialogs.*
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.ThemeStore
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.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.utils.applyTint
import io.legado.app.utils.getPrefString
import io.legado.app.utils.getViewModel
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_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(
activity: Activity,
isInMultiWindow: Boolean,
toolBarHide: Boolean = true
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
activity.window.insetsController?.let {
window.insetsController?.let {
if (toolBarHide) {
if (ReadBookConfig.hideStatusBar) {
it.hide(WindowInsets.Type.statusBars())
@ -54,20 +143,19 @@ object ReadBookActivityHelp {
}
}
}
upSystemUiVisibilityO(activity, isInMultiWindow, toolBarHide)
upSystemUiVisibilityO(isInMultiWindow, toolBarHide)
if (toolBarHide) {
ATH.setLightStatusBar(activity, ReadBookConfig.durConfig.curStatusIconDark())
ATH.setLightStatusBar(this, ReadBookConfig.durConfig.curStatusIconDark())
} else {
ATH.setLightStatusBarAuto(
activity,
ThemeStore.statusBarColor(activity, AppConfig.isTransparentStatusBar)
this,
ThemeStore.statusBarColor(this, AppConfig.isTransparentStatusBar)
)
}
}
@Suppress("DEPRECATION")
private fun upSystemUiVisibilityO(
activity: Activity,
isInMultiWindow: Boolean,
toolBarHide: Boolean = true
) {
@ -88,23 +176,23 @@ object ReadBookActivityHelp {
flag = flag or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
}
}
activity.window.decorView.systemUiVisibility = flag
window.decorView.systemUiVisibility = flag
}
/**
* 屏幕方向
*/
@SuppressLint("SourceLockedOrientationActivity")
fun setOrientation(activity: Activity) = activity.apply {
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
override fun upNavigationBarColor() {
when {
read_menu == null -> return
read_menu.isVisible -> ATH.setNavigationBarColorAuto(this)
bottomDialog > 0 -> ATH.setNavigationBarColorAuto(this, bottomBackground)
ReadBookConfig.bg is ColorDrawable -> {
ATH.setNavigationBarColorAuto(this, ReadBookConfig.bgMeanColor)
}
else -> {
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) {
window.attributes = window.attributes.apply {
layoutInDisplayCutoutMode =
@ -129,12 +217,13 @@ object ReadBookActivityHelp {
}
@SuppressLint("InflateParams")
fun showDownloadDialog(context: Context) {
fun showDownloadDialog() {
ReadBook.book?.let { book ->
context.alert(titleResource = R.string.offline_cache) {
alert(titleResource = R.string.offline_cache) {
var view: View? = null
customView {
LayoutInflater.from(context).inflate(R.layout.dialog_download_choice, null)
LayoutInflater.from(this@ReadBookBaseActivity)
.inflate(R.layout.dialog_download_choice, null)
.apply {
view = this
setBackgroundColor(context.backgroundColor)
@ -150,15 +239,15 @@ object ReadBookActivityHelp {
}
}
noButton()
}.show().applyTint()
}.show()
}
}
@SuppressLint("InflateParams")
fun showBookMark(context: Context) = with(context) {
fun showBookMark() {
val book = ReadBook.book ?: 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
message = book.name + "" + textChapter.title
customView {
@ -184,11 +273,11 @@ object ReadBookActivityHelp {
}
}
noButton()
}.show().applyTint().requestInputMethod()
}.show().requestInputMethod()
}
@SuppressLint("InflateParams")
fun showCharsetConfig(context: Context) = with(context) {
fun showCharsetConfig() {
val charsets =
arrayListOf("UTF-8", "GB2312", "GBK", "Unicode", "UTF-16", "UTF-16LE", "ASCII")
alert(R.string.set_charset) {
@ -207,10 +296,10 @@ object ReadBookActivityHelp {
}
}
cancelButton()
}.show().applyTint()
}.show()
}
fun showPageAnimConfig(context: Context, success: () -> Unit) = with(context) {
fun showPageAnimConfig(success: () -> Unit) {
val items = arrayListOf<String>()
items.add(getString(R.string.btn_default_s))
items.add(getString(R.string.page_anim_cover))
@ -224,13 +313,13 @@ object ReadBookActivityHelp {
}
}
fun isPrevKey(context: Context, keyCode: Int): Boolean {
val prevKeysStr = context.getPrefString(PreferKey.prevKeys)
fun isPrevKey(keyCode: Int): Boolean {
val prevKeysStr = getPrefString(PreferKey.prevKeys)
return prevKeysStr?.split(",")?.contains(keyCode.toString()) ?: false
}
fun isNextKey(context: Context, keyCode: Int): Boolean {
val nextKeysStr = context.getPrefString(PreferKey.nextKeys)
fun isNextKey(keyCode: Int): Boolean {
val nextKeysStr = getPrefString(PreferKey.nextKeys)
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() {
super.onCleared()
if (BaseReadAloudService.pause) {

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

@ -1,5 +1,6 @@
package io.legado.app.ui.book.read.config
import android.content.DialogInterface
import android.os.Bundle
import android.view.*
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.service.BaseReadAloudService
import io.legado.app.service.help.ReadAloud
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.utils.ColorUtils
import kotlinx.android.synthetic.main.dialog_auto_read.*
import org.jetbrains.anko.sdk27.listeners.onClick
@ -19,6 +21,7 @@ class AutoReadDialog : BaseDialogFragment() {
override fun onStart() {
super.onStart()
(activity as ReadBookActivity).bottomDialog++
dialog?.window?.let {
it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
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(
inflater: LayoutInflater,
container: ViewGroup?,

@ -17,7 +17,7 @@ import io.legado.app.help.ReadBookConfig
import io.legado.app.help.http.HttpHelper
import io.legado.app.help.permission.Permissions
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.getPrimaryTextColor
import io.legado.app.lib.theme.getSecondaryTextColor
@ -50,6 +50,7 @@ class BgTextConfigDialog : BaseDialogFragment(), FilePickerDialog.CallBack {
override fun onStart() {
super.onStart()
(activity as ReadBookActivity).bottomDialog++
dialog?.window?.let {
it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
it.setBackgroundDrawableResource(R.color.background)
@ -79,6 +80,7 @@ class BgTextConfigDialog : BaseDialogFragment(), FilePickerDialog.CallBack {
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
ReadBookConfig.save()
(activity as ReadBookActivity).bottomDialog--
}
private fun initView() {
@ -131,7 +133,7 @@ class BgTextConfigDialog : BaseDialogFragment(), FilePickerDialog.CallBack {
}
}
cancelButton()
}.show().applyTint()
}.show()
}
sw_dark_status_icon.onCheckedChange { buttonView, isChecked ->
if (buttonView?.isPressed == true) {
@ -283,8 +285,8 @@ class BgTextConfigDialog : BaseDialogFragment(), FilePickerDialog.CallBack {
importNetConfig(url)
}
}
noButton { }
}.show().applyTint()
noButton()
}.show()
}
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.theme.accentColor
import io.legado.app.ui.widget.text.StrokeTextView
import io.legado.app.utils.applyTint
import org.jetbrains.anko.sdk27.listeners.onClick
class ChineseConverter(context: Context, attrs: AttributeSet?) : StrokeTextView(context, attrs) {
@ -45,7 +44,7 @@ class ChineseConverter(context: Context, attrs: AttributeSet?) : StrokeTextView(
upUi(i)
onChanged?.invoke()
}
}.show().applyTint()
}.show()
}
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
import android.annotation.SuppressLint
import android.content.DialogInterface
import android.content.SharedPreferences
import android.os.Bundle
import android.view.*
@ -14,7 +15,7 @@ import io.legado.app.constant.PreferKey
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.ATH
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.getPrefBoolean
import io.legado.app.utils.postEvent
@ -24,6 +25,7 @@ class MoreConfigDialog : DialogFragment() {
override fun onStart() {
super.onStart()
(activity as ReadBookActivity).bottomDialog++
dialog?.window?.let {
it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
it.setBackgroundDrawableResource(R.color.background)
@ -57,6 +59,11 @@ class MoreConfigDialog : DialogFragment() {
.commit()
}
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
(activity as ReadBookActivity).bottomDialog--
}
class ReadPreferenceFragment : BasePreferenceFragment(),
SharedPreferences.OnSharedPreferenceChangeListener {
@ -101,9 +108,7 @@ class MoreConfigDialog : DialogFragment() {
PreferKey.keepLight -> postEvent(key, true)
PreferKey.textSelectAble -> postEvent(key, getPrefBoolean(key))
getString(R.string.pk_requested_direction) -> {
activity?.let {
ReadBookActivityHelp.setOrientation(it)
}
(activity as? ReadBookActivity)?.setOrientation()
}
PreferKey.textFullJustify,
PreferKey.textBottomJustify -> {
@ -118,6 +123,9 @@ class MoreConfigDialog : DialogFragment() {
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
when (preference?.key) {
"customPageKey" -> PageKeyDialog(requireContext()).show()
"clickRegionalConfig" -> {
(activity as? ReadBookActivity)?.showClickRegionalConfig()
}
}
return super.onPreferenceTreeClick(preference)
}

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

@ -1,5 +1,6 @@
package io.legado.app.ui.book.read.config
import android.content.DialogInterface
import android.os.Bundle
import android.view.*
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.help.ReadAloud
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.getPrefBoolean
import io.legado.app.utils.observeEvent
@ -24,6 +26,7 @@ class ReadAloudDialog : BaseDialogFragment() {
override fun onStart() {
super.onStart()
(activity as ReadBookActivity).bottomDialog++
dialog?.window?.let {
it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
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(
inflater: LayoutInflater,
container: ViewGroup?,

@ -35,6 +35,7 @@ class ReadStyleDialog : BaseDialogFragment(), FontSelectDialog.CallBack {
override fun onStart() {
super.onStart()
(activity as ReadBookActivity).bottomDialog++
dialog?.window?.let {
it.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
it.setBackgroundDrawableResource(R.color.background)
@ -64,6 +65,7 @@ class ReadStyleDialog : BaseDialogFragment(), FontSelectDialog.CallBack {
override fun onDismiss(dialog: DialogInterface) {
super.onDismiss(dialog)
ReadBookConfig.save()
(activity as ReadBookActivity).bottomDialog--
}
private fun initView() {
@ -190,7 +192,7 @@ class ReadStyleDialog : BaseDialogFragment(), FontSelectDialog.CallBack {
}
}
customView = rootView
}.show().applyTint()
}.show()
}
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.data.entities.HttpTTS
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.service.help.ReadAloud
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.item_http_tts.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
import java.io.File
class SpeakEngineDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
@ -128,11 +124,11 @@ class SpeakEngineDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
}
neutralButton(R.string.help) {
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)
}
}.show().applyTint()
}.show()
}
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.theme.accentColor
import io.legado.app.ui.widget.text.StrokeTextView
import io.legado.app.utils.applyTint
import org.jetbrains.anko.sdk27.listeners.onClick
class TextFontWeightConverter(context: Context, attrs: AttributeSet?) : StrokeTextView(context, attrs) {
@ -46,7 +45,7 @@ class TextFontWeightConverter(context: Context, attrs: AttributeSet?) : StrokeTe
upUi(i)
onChanged?.invoke()
}
}.show().applyTint()
}.show()
}
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.data.entities.TxtTocRule
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.primaryColor
import io.legado.app.ui.widget.recycler.ItemTouchCallback
@ -167,7 +164,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
}
}
cancelButton()
}.show().applyTint()
}.show()
}
@SuppressLint("InflateParams")
@ -191,7 +188,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
}
}
cancelButton()
}.show().applyTint()
}.show()
}
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.lib.theme.accentColor
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.TextLine
import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.ui.book.read.page.entities.*
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.widget.dialog.PhotoDialog
@ -40,19 +38,21 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
private val visibleRect = RectF()
private val selectStart = arrayOf(0, 0, 0)
private val selectEnd = arrayOf(0, 0, 0)
private var textChapter: TextChapter? = null
private var textPage: TextPage = TextPage()
//滚动参数
private val pageFactory: TextPageFactory get() = callBack.pageFactory
private var pageOffset = 0f
private var pageOffset = 0
init {
callBack = activity as CallBack
contentDescription = textPage.text
}
fun setContent(textPage: TextPage) {
this.textPage = textPage
fun setContent(pageData: PageData) {
this.textChapter = pageData.textChapter
this.textPage = pageData.textPage
contentDescription = textPage.text
invalidate()
}
@ -139,7 +139,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
isTitle: 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 =
if (isReadAloud) context.accentColor else ReadBookConfig.textColor
textChars.forEach {
@ -173,33 +177,33 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
/**
* 滚动事件
*/
fun onScroll(mOffset: Float) {
if (mOffset == 0f) return
fun scroll(mOffset: Int) {
if (mOffset == 0) return
pageOffset += mOffset
if (!pageFactory.hasPrev() && pageOffset > 0) {
pageOffset = 0f
pageOffset = 0
} else if (!pageFactory.hasNext()
&& pageOffset < 0
&& pageOffset + textPage.height < ChapterProvider.visibleHeight
) {
val offset = ChapterProvider.visibleHeight - textPage.height
pageOffset = min(0f, offset)
val offset = (ChapterProvider.visibleHeight - textPage.height).toInt()
pageOffset = min(0, offset)
} else if (pageOffset > 0) {
pageFactory.moveToPrev(false)
textPage = pageFactory.currentPage
pageOffset -= textPage.height
textPage = pageFactory.curData.textPage
pageOffset -= textPage.height.toInt()
upView?.invoke(textPage)
} else if (pageOffset < -textPage.height) {
pageOffset += textPage.height
pageOffset += textPage.height.toInt()
pageFactory.moveToNext(false)
textPage = pageFactory.currentPage
textPage = pageFactory.curData.textPage
upView?.invoke(textPage)
}
invalidate()
}
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 {
return when (relativePos) {
0 -> pageOffset
0 -> pageOffset.toFloat()
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 {
return when (relativePos) {
0 -> textPage
1 -> pageFactory.nextPage
else -> pageFactory.nextPagePlus
1 -> pageFactory.nextData.textPage
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.ReadTipConfig
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.provider.ChapterProvider
import io.legado.app.ui.widget.BatteryView
@ -217,11 +218,12 @@ class ContentView(context: Context) : FrameLayout(context) {
tvBattery?.setBattery(battery)
}
fun setContent(textPage: TextPage, resetPageOffset: Boolean = true) {
setProgress(textPage)
if (resetPageOffset)
fun setContent(pageData: PageData, resetPageOffset: Boolean = true) {
setProgress(pageData.textPage)
if (resetPageOffset) {
resetPageOffset()
content_text_view.setContent(textPage)
}
content_text_view.setContent(pageData)
}
fun resetPageOffset() {
@ -237,8 +239,8 @@ class ContentView(context: Context) : FrameLayout(context) {
tvPageAndTotal?.text = "${index.plus(1)}/$pageSize $readProgress"
}
fun onScroll(offset: Float) {
content_text_view.onScroll(offset)
fun scroll(offset: Int) {
content_text_view.scroll(offset)
}
fun upSelectAble(selectAble: Boolean) {

@ -10,13 +10,13 @@ abstract class PageFactory<DATA>(protected val dataSource: DataSource) {
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

@ -10,6 +10,7 @@ import android.util.AttributeSet
import android.view.MotionEvent
import android.view.ViewConfiguration
import android.widget.FrameLayout
import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.accentColor
import io.legado.app.service.help.ReadBook
@ -71,7 +72,18 @@ class PageView(context: Context, attrs: AttributeSet) :
private var firstCharIndex: Int = 0
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 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 autoPagePint by lazy {
Paint().apply {
@ -90,7 +102,15 @@ class PageView(context: Context, attrs: AttributeSet) :
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
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)
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()
pageDelegate?.setViewSize(w, h)
if (oldw != 0 && oldh != 0) {
@ -101,10 +121,10 @@ class PageView(context: Context, attrs: AttributeSet) :
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
pageDelegate?.onDraw(canvas)
if (callBack.isAutoPage) {
if (callBack.isAutoPage && !isScroll) {
nextPage.screenshot()?.let {
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)
canvas.drawBitmap(it, autoPageRect, autoPageRect, null)
canvas.drawRect(
@ -239,24 +259,49 @@ class PageView(context: Context, attrs: AttributeSet) :
* 单击
*/
private fun onSingleTapUp(): Boolean {
if (isTextSelected) {
isTextSelected = false
return true
}
if (centerRectF.contains(startX, startY)) {
if (!isAbortAnim) {
callBack.clickCenter()
when {
isTextSelected -> isTextSelected = false
centerRectF.contains(startX, startY) -> if (!isAbortAnim) {
click(AppConfig.clickActionMiddleCenter)
}
bottomCenterRectF.contains(startX, startY) -> {
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) {
if (startX > width / 2 || ReadBookConfig.clickAllNext) {
pageDelegate?.nextPageByAnim(defaultAnimationSpeed)
} else {
pageDelegate?.prevPageByAnim(defaultAnimationSpeed)
topLeftRectF.contains(startX, startY) -> {
click(AppConfig.clickActionTopLeft)
}
topCenterRectF.contains(startX, startY) -> {
click(AppConfig.clickActionTopCenter)
}
topRightRectF.contains(startX, startY) -> {
click(AppConfig.clickActionTopRight)
}
}
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) {
if (isScroll && !callBack.isAutoPage) {
curPage.setContent(pageFactory.currentPage, resetPageOffset)
curPage.setContent(pageFactory.curData, resetPageOffset)
} else {
curPage.resetPageOffset()
when (relativePosition) {
-1 -> prevPage.setContent(pageFactory.prevPage)
1 -> nextPage.setContent(pageFactory.nextPage)
-1 -> prevPage.setContent(pageFactory.prevData)
1 -> nextPage.setContent(pageFactory.nextData)
else -> {
curPage.setContent(pageFactory.currentPage)
nextPage.setContent(pageFactory.nextPage)
prevPage.setContent(pageFactory.prevPage)
curPage.setContent(pageFactory.curData)
nextPage.setContent(pageFactory.nextData)
prevPage.setContent(pageFactory.prevData)
}
}
}
@ -408,7 +453,7 @@ class PageView(context: Context, attrs: AttributeSet) :
val isInitFinish: Boolean
val isAutoPage: Boolean
val autoPageProgress: Int
fun clickCenter()
fun showActionMenu()
fun screenOffTimerStart()
fun showTextActionMenu()
}

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

@ -122,7 +122,7 @@ class SearchContentActivity :
observeEvent<BookChapter>(EventBus.SAVE_CONTENT) { chapter ->
viewModel.book?.bookUrl?.let { bookUrl ->
if (chapter.bookUrl == bookUrl) {
adapter.cacheFileNames.add(BookHelp.formatChapterName(chapter))
adapter.cacheFileNames.add(chapter.getFileName())
adapter.notifyItemChanged(chapter.index, true)
}
}
@ -143,7 +143,7 @@ class SearchContentActivity :
App.db.bookChapterDao().getChapterList(viewModel.bookUrl).map { chapter ->
val job = async(Dispatchers.IO) {
if (isLocalBook
|| adapter.cacheFileNames.contains(BookHelp.formatChapterName(chapter))
|| adapter.cacheFileNames.contains(chapter.getFileName())
) {
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.qrcode.QrCodeActivity
import io.legado.app.ui.widget.KeyboardToolPop
import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_book_source_edit.*
import org.jetbrains.anko.*
@ -63,7 +64,7 @@ class BookSourceEditActivity :
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_save -> getSource().let { source ->
if (!source.equal(viewModel.bookSource ?: BookSource())){
if (!source.equal(viewModel.bookSource ?: BookSource())) {
source.lastUpdateTime = System.currentTimeMillis()
}
if (checkSource(source)) {
@ -81,7 +82,10 @@ class BookSourceEditActivity :
R.id.menu_paste_source -> viewModel.pasteSource { upRecyclerView(it) }
R.id.menu_qr_code_camera -> startActivityForResult<QrCodeActivity>(qrRequestCode)
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 -> {
try {
val intent = Intent(Intent.ACTION_VIEW)
@ -138,7 +142,7 @@ class BookSourceEditActivity :
negativeButton(R.string.no) {
super.finish()
}
}.show().applyTint()
}.show()
} else {
super.finish()
}
@ -229,6 +233,7 @@ class BookSourceEditActivity :
add(EditEntity("sourceRegex", cr?.sourceRegex, R.string.rule_source_regex))
add(EditEntity("replaceRegex", cr?.replaceRegex, R.string.rule_replace_regex))
add(EditEntity("imageStyle", cr?.imageStyle, R.string.rule_image_style))
add(EditEntity("font", cr?.font, R.string.rule_font))
}
//发现
val er = source?.getExploreRule()
@ -332,6 +337,7 @@ class BookSourceEditActivity :
"sourceRegex" -> contentRule.sourceRegex = it.value
"replaceRegex" -> contentRule.replaceRegex = it.value
"imageStyle" -> contentRule.imageStyle = it.value
"font" -> contentRule.font = it.value
}
}
source.ruleSearch = searchRule
@ -344,7 +350,7 @@ class BookSourceEditActivity :
private fun checkSource(source: BookSource): Boolean {
if (source.bookSourceUrl.isBlank() || source.bookSourceName.isBlank()) {
toast("书源名称和URL不能为空")
toast(R.string.non_null_name_url)
return false
}
return true
@ -367,12 +373,28 @@ class BookSourceEditActivity :
override fun sendText(text: String) {
if (text == AppConst.keyboardToolChars[0]) {
insertText(AppConst.urlOption)
showHelpDialog()
} else {
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() {
mSoftKeyboardTool?.let {
if (it.isShowing) return

@ -18,11 +18,9 @@ class BookSourceEditAdapter : RecyclerView.Adapter<BookSourceEditAdapter.MyViewH
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(
LayoutInflater.from(
parent.context
).inflate(R.layout.item_source_edit, parent, false)
)
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.item_source_edit, parent, false)
return MyViewHolder(view)
}
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.data.entities.BookSource
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.primaryTextColor
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.qrcode.QrCodeActivity
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.ItemTouchCallback
import io.legado.app.ui.widget.recycler.VerticalDivider
@ -75,6 +77,9 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
initLiveDataBookSource()
initLiveDataGroup()
initSelectActionBar()
if (LocalConfig.isFirstOpenBookSources) {
showHelp()
}
}
override fun onCompatCreateOptionsMenu(menu: Menu): Boolean {
@ -154,6 +159,7 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
R.id.menu_disabled_group -> {
search_view.setQuery(getString(R.string.disabled), true)
}
R.id.menu_help -> showHelp()
}
if (item.groupId == R.id.source_group) {
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) {
if (sort == sortId) {
sortAscending += 1
@ -260,8 +271,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
override fun onClickMainAction() {
alert(titleResource = R.string.draw, messageResource = R.string.sure_del) {
okButton { viewModel.delSelection(adapter.getSelection()) }
noButton { }
}.show().applyTint()
noButton()
}.show()
}
private fun initSelectActionBar() {
@ -305,8 +316,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
}
CheckSource.start(this@BookSourceActivity, adapter.getSelection())
}
noButton { }
}.show().applyTint()
noButton()
}.show()
}
@SuppressLint("InflateParams")
@ -326,8 +337,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
}
}
}
noButton { }
}.show().applyTint()
noButton()
}.show()
}
@SuppressLint("InflateParams")
@ -347,8 +358,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
}
}
}
noButton { }
}.show().applyTint()
noButton()
}.show()
}
private fun upGroupMenu() {
@ -389,7 +400,7 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
}
}
cancelButton()
}.show().applyTint()
}.show()
}
override fun observeLiveBus() {
@ -403,10 +414,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
}
}
observeEvent<Int>(EventBus.CHECK_SOURCE_DONE) {
snackBar?.let {
it.dismiss()
snackBar = null
}
snackBar?.dismiss()
snackBar = null
groups.map { group ->
if (group.contains("失效")) {
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.constant.AppPattern
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.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
@ -99,7 +96,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
}
}
noButton()
}.show().applyTint().requestInputMethod()
}.show().requestInputMethod()
}
@SuppressLint("InflateParams")
@ -118,7 +115,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
viewModel.upGroup(group, editText?.text?.toString())
}
noButton()
}.show().applyTint().requestInputMethod()
}.show().requestInputMethod()
}
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.data.entities.Bookmark
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.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.requestInputMethod
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
@ -109,7 +105,7 @@ class BookmarkFragment : VMBaseFragment<ChapterListViewModel>(R.layout.fragment_
neutralButton(R.string.delete) {
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.SimpleRecyclerAdapter
import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
import io.legado.app.lib.theme.accentColor
import io.legado.app.utils.getCompatColor
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>) {
with(holder.itemView) {
val isDur = callback.durChapterIndex() == item.index
val cached = callback.isLocalBook
|| cacheFileNames.contains(BookHelp.formatChapterName(item))
val cached = callback.isLocalBook || cacheFileNames.contains(item.getFileName())
if (payloads.isEmpty()) {
if (isDur) {
tv_chapter_name.setTextColor(context.accentColor)

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

@ -4,6 +4,9 @@ import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.text.InputType
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import androidx.preference.EditTextPreference
import androidx.preference.ListPreference
@ -11,11 +14,13 @@ import androidx.preference.Preference
import io.legado.app.R
import io.legado.app.base.BasePreferenceFragment
import io.legado.app.constant.PreferKey
import io.legado.app.help.LocalConfig
import io.legado.app.help.storage.Restore
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.accentColor
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.getPrefString
@ -55,6 +60,28 @@ class BackupConfigFragment : BasePreferenceFragment(),
super.onViewCreated(view, savedInstanceState)
preferenceManager.sharedPreferences.registerOnSharedPreferenceChangeListener(this)
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() {
@ -126,11 +153,10 @@ class BackupConfigFragment : BasePreferenceFragment(),
multiChoiceItems(Restore.ignoreTitle, checkedItems) { _, which, isChecked ->
Restore.ignoreConfig[Restore.ignoreKeys[which]] = isChecked
}
}.show()
.applyTint()
.setOnDismissListener {
onDismiss {
Restore.saveIgnoreConfig()
}
}.show()
}
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.PermissionsCompat
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.theme.ATH
import io.legado.app.receiver.SharedReceiverActivity
@ -149,15 +147,17 @@ class OtherConfigFragment : BasePreferenceFragment(),
}
private fun clearCache() {
requireContext().alert(titleResource = R.string.clear_cache,
messageResource = R.string.sure_del) {
requireContext().alert(
titleResource = R.string.clear_cache,
messageResource = R.string.sure_del
) {
okButton {
BookHelp.clearCache()
FileUtils.deleteFile(requireActivity().cacheDir.absolutePath)
toast(R.string.clear_cache_success)
}
noButton()
}.show().applyTint()
}.show()
}
private fun selectDefaultCover() {

@ -18,9 +18,6 @@ import io.legado.app.help.AppConfig
import io.legado.app.help.LauncherIconHelp
import io.legado.app.help.ThemeConfig
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.ui.widget.number.NumberPickerDialog
import io.legado.app.ui.widget.prefs.ColorPreference
@ -195,8 +192,8 @@ class ThemeConfigFragment : BasePreferenceFragment(),
}
}
}
noButton { }
}.show().applyTint()
noButton()
}.show()
}
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.help.ThemeConfig
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.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.*
@ -88,7 +86,7 @@ class ThemeListDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
initData()
}
noButton()
}.show().applyTint()
}.show()
}
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.PermissionsCompat
import io.legado.app.lib.dialogs.alert
import io.legado.app.utils.applyTint
@Suppress("unused")
object FilePicker {
@ -62,7 +61,7 @@ object FilePicker {
}
}
}
}.show().applyTint()
}.show()
}
fun selectFolder(
@ -113,7 +112,7 @@ object FilePicker {
}
}
}
}.show().applyTint()
}.show()
}
fun selectFile(
@ -171,7 +170,7 @@ object FilePicker {
}
}
}
}.show().applyTint()
}.show()
}
fun selectFile(
@ -229,7 +228,7 @@ object FilePicker {
}
}
}
}.show().applyTint()
}.show()
}
private fun createSelectFileIntent(): Intent {

@ -17,6 +17,7 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp
import io.legado.app.help.LocalConfig
import io.legado.app.help.storage.Backup
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.elevation
@ -102,7 +103,10 @@ class MainActivity : VMBaseActivity<MainViewModel>(R.layout.activity_main),
private fun upVersion() {
if (getPrefInt(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())
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.data.entities.BookGroup
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.accentColor
import io.legado.app.ui.book.arrange.ArrangeBookActivity
@ -145,7 +142,7 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
@SuppressLint("InflateParams")
private fun configBookshelf() {
requireContext().alert(titleResource = R.string.bookshelf_layout) {
alert(titleResource = R.string.bookshelf_layout) {
val bookshelfLayout = getPrefInt(PreferKey.bookshelfLayout)
val bookshelfSort = getPrefInt(PreferKey.bookshelfSort)
val root = LayoutInflater.from(requireContext())
@ -171,7 +168,7 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
}
}
noButton()
}.show().applyTint()
}.show()
}
@SuppressLint("InflateParams")
@ -187,9 +184,9 @@ class BookshelfFragment : VMBaseFragment<BookshelfViewModel>(R.layout.fragment_b
editText?.text?.toString()?.let {
viewModel.addBookByUrl(it)
}
}
noButton { }
}.show().applyTint()
}
noButton()
}.show()
}
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) {
when (item.itemId) {
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)
}
}

@ -17,9 +17,6 @@ import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.constant.AppPattern
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.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
@ -99,7 +96,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
}
}
noButton()
}.show().applyTint().requestInputMethod()
}.show().requestInputMethod()
}
@SuppressLint("InflateParams")
@ -118,7 +115,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
viewModel.upGroup(group, editText?.text?.toString())
}
noButton()
}.show().applyTint().requestInputMethod()
}.show().requestInputMethod()
}
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.IntentDataHelp
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.primaryTextColor
import io.legado.app.ui.association.ImportReplaceRuleActivity
@ -134,9 +134,8 @@ class ReplaceRuleActivity :
private fun delSourceDialog() {
alert(titleResource = R.string.draw, messageResource = R.string.sure_del) {
okButton { viewModel.delSelection(adapter.getSelection()) }
noButton { }
}
.show().applyTint()
noButton()
}.show()
}
private fun observeReplaceRuleData(key: String? = null) {
@ -233,7 +232,7 @@ class ReplaceRuleActivity :
}
}
cancelButton()
}.show().applyTint()
}.show()
}
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 org.jetbrains.anko.displayMetrics
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.selector
import org.jetbrains.anko.toast
import kotlin.math.abs
@ -64,8 +65,7 @@ class ReplaceEditActivity :
upReplaceView(it)
}
iv_help.onClick {
val mdText = String(assets.open("help/regex.md").readBytes())
TextDialog.show(supportFragmentManager, mdText, TextDialog.MD)
showRegexHelp()
}
}
@ -128,13 +128,26 @@ class ReplaceEditActivity :
override fun sendText(text: String) {
if (text == AppConst.keyboardToolChars[0]) {
val view = window?.decorView?.findFocus()
view?.clearFocus()
showHelpDialog()
} else {
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() {
mSoftKeyboardTool?.let {
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.primaryTextColor
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.FilePickerDialog
import io.legado.app.utils.*
@ -107,11 +110,39 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_r
view: WebView?,
request: WebResourceRequest?
): Boolean {
if (request?.url?.scheme == "http" || request?.url?.scheme == "https") {
return false
}
request?.url?.let {
openUrl(it)
request?.let {
if (it.url.scheme == "http" || it.url.scheme == "https") {
return false
} else if (it.url.scheme == "yuedu") {
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
}

@ -20,6 +20,7 @@ import io.legado.app.lib.theme.ATH
import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.ui.rss.source.debug.RssSourceDebugActivity
import io.legado.app.ui.widget.KeyboardToolPop
import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_rss_source_edit.*
import org.jetbrains.anko.*
@ -56,7 +57,7 @@ class RssSourceEditActivity :
negativeButton(R.string.no) {
super.finish()
}
}.show().applyTint()
}.show()
} else {
super.finish()
}
@ -187,12 +188,28 @@ class RssSourceEditActivity :
override fun sendText(text: String) {
if (text == AppConst.keyboardToolChars[0]) {
insertText(AppConst.urlOption)
showHelpDialog()
} else {
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() {
mSoftKeyboardTool?.let {
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.constant.AppPattern
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.primaryColor
import io.legado.app.ui.widget.recycler.VerticalDivider
@ -96,7 +93,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
}
}
noButton()
}.show().applyTint().requestInputMethod()
}.show().requestInputMethod()
}
@SuppressLint("InflateParams")
@ -115,7 +112,7 @@ class GroupManageDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener
viewModel.upGroup(group, editText?.text?.toString())
}
noButton()
}.show().applyTint().requestInputMethod()
}.show().requestInputMethod()
}
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.data.entities.RssSource
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.primaryTextColor
import io.legado.app.ui.association.ImportRssSourceActivity
@ -180,9 +180,8 @@ class RssSourceActivity : VMBaseActivity<RssSourceViewModel>(R.layout.activity_r
private fun delSourceDialog() {
alert(titleResource = R.string.draw, messageResource = R.string.sure_del) {
okButton { viewModel.delSelection(adapter.getSelection()) }
noButton { }
}
.show().applyTint()
noButton()
}.show()
}
private fun upGroupMenu() {
@ -243,7 +242,7 @@ class RssSourceActivity : VMBaseActivity<RssSourceViewModel>(R.layout.activity_r
}
}
cancelButton()
}.show().applyTint()
}.show()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

@ -28,6 +28,7 @@ import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
import java.io.File
import java.util.*
import kotlin.collections.ArrayList
class FontSelectDialog : BaseDialogFragment(),
FilePickerDialog.CallBack,
@ -150,8 +151,7 @@ class FontSelectDialog : BaseDialogFragment(),
fontItems.add(item)
}
}
fontItems.addAll(getLocalFonts())
fontItems.sortedBy { it.name }
mergeFontItems(fontItems, getLocalFonts())
}.onSuccess {
adapter?.setItems(it)
}.onError {
@ -186,8 +186,7 @@ class FontSelectDialog : BaseDialogFragment(),
)
)
}
fontItems.addAll(getLocalFonts())
fontItems.sortedBy { it.name }
mergeFontItems(fontItems, getLocalFonts())
}.onSuccess {
adapter?.setItems(it)
}.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) {
execute {
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.getCompatColor
@Suppress("unused")
open class StrokeTextView(context: Context, attrs: AttributeSet?) :
AppCompatTextView(context, attrs) {
@ -40,7 +41,7 @@ open class StrokeTextView(context: Context, attrs: AttributeSet?) :
.setSelectedStrokeColor(context.getCompatColor(R.color.accent))
.setPressedBgColor(context.getCompatColor(R.color.transparent30))
.create()
this.setTextColor(
setTextColor(
Selector.colorBuild()
.setDefaultColor(context.getCompatColor(R.color.secondaryText))
.setSelectedColor(context.getCompatColor(R.color.accent))
@ -58,7 +59,7 @@ open class StrokeTextView(context: Context, attrs: AttributeSet?) :
.setSelectedStrokeColor(context.accentColor)
.setPressedBgColor(context.getCompatColor(R.color.transparent30))
.create()
this.setTextColor(
setTextColor(
Selector.colorBuild()
.setDefaultColor(context.getPrimaryTextColor(isLight))
.setSelectedColor(context.accentColor)
@ -75,7 +76,7 @@ open class StrokeTextView(context: Context, attrs: AttributeSet?) :
.setSelectedStrokeColor(ThemeStore.accentColor(context))
.setPressedBgColor(context.getCompatColor(R.color.transparent30))
.create()
this.setTextColor(
setTextColor(
Selector.colorBuild()
.setDefaultColor(ThemeStore.textColorSecondary(context))
.setSelectedColor(ThemeStore.accentColor(context))

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

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

Loading…
Cancel
Save