Merge pull request #1 from gedoor/master

1
pull/842/head
DIANSO 4 years ago committed by GitHub
commit 645abe3315
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 20
      app/build.gradle
  2. 4
      app/src/main/AndroidManifest.xml
  3. 203
      app/src/main/assets/18PlusList.txt
  4. 6
      app/src/main/assets/help/replaceRuleHelp.md
  5. 35
      app/src/main/assets/updateLog.md
  6. 66
      app/src/main/java/io/legado/app/App.kt
  7. 21
      app/src/main/java/io/legado/app/api/controller/BookshelfController.kt
  8. 12
      app/src/main/java/io/legado/app/api/controller/SourceController.kt
  9. 29
      app/src/main/java/io/legado/app/base/BaseViewModel.kt
  10. 7
      app/src/main/java/io/legado/app/base/adapter/DiffRecyclerAdapter.kt
  11. 7
      app/src/main/java/io/legado/app/base/adapter/RecyclerAdapter.kt
  12. 11
      app/src/main/java/io/legado/app/constant/AppConst.kt
  13. 27
      app/src/main/java/io/legado/app/constant/Const.kt
  14. 8
      app/src/main/java/io/legado/app/data/AppDatabase.kt
  15. 2
      app/src/main/java/io/legado/app/data/dao/BookGroupDao.kt
  16. 6
      app/src/main/java/io/legado/app/data/entities/Book.kt
  17. 4
      app/src/main/java/io/legado/app/data/entities/BookSource.kt
  18. 4
      app/src/main/java/io/legado/app/help/AppConfig.kt
  19. 7
      app/src/main/java/io/legado/app/help/BookHelp.kt
  20. 11
      app/src/main/java/io/legado/app/help/CacheManager.kt
  21. 11
      app/src/main/java/io/legado/app/help/ContentProcessor.kt
  22. 23
      app/src/main/java/io/legado/app/help/DefaultData.kt
  23. 6
      app/src/main/java/io/legado/app/help/IntentHelp.kt
  24. 4
      app/src/main/java/io/legado/app/help/JsExtensions.kt
  25. 24
      app/src/main/java/io/legado/app/help/LauncherIconHelp.kt
  26. 10
      app/src/main/java/io/legado/app/help/LocalConfig.kt
  27. 4
      app/src/main/java/io/legado/app/help/ReadBookConfig.kt
  28. 4
      app/src/main/java/io/legado/app/help/ReadTipConfig.kt
  29. 15
      app/src/main/java/io/legado/app/help/SourceHelp.kt
  30. 25
      app/src/main/java/io/legado/app/help/ThemeConfig.kt
  31. 10
      app/src/main/java/io/legado/app/help/http/AjaxWebView.kt
  32. 10
      app/src/main/java/io/legado/app/help/http/CookieStore.kt
  33. 4
      app/src/main/java/io/legado/app/help/permission/PermissionActivity.kt
  34. 40
      app/src/main/java/io/legado/app/help/permission/Request.kt
  35. 35
      app/src/main/java/io/legado/app/help/storage/Backup.kt
  36. 17
      app/src/main/java/io/legado/app/help/storage/BookWebDav.kt
  37. 36
      app/src/main/java/io/legado/app/help/storage/ImportOldData.kt
  38. 4
      app/src/main/java/io/legado/app/help/storage/OldBook.kt
  39. 70
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  40. 34
      app/src/main/java/io/legado/app/lib/dialogs/AlertBuilder.kt
  41. 62
      app/src/main/java/io/legado/app/lib/dialogs/AndroidAlertBuilder.kt
  42. 10
      app/src/main/java/io/legado/app/lib/dialogs/AndroidDialogs.kt
  43. 4
      app/src/main/java/io/legado/app/lib/dialogs/AndroidSelectors.kt
  44. 8
      app/src/main/java/io/legado/app/lib/dialogs/Dialogs.kt
  45. 2
      app/src/main/java/io/legado/app/lib/dialogs/Selectors.kt
  46. 8
      app/src/main/java/io/legado/app/lib/theme/ATH.kt
  47. 10
      app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt
  48. 600
      app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.java
  49. 633
      app/src/main/java/io/legado/app/model/analyzeRule/QueryTTF.kt
  50. 9
      app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt
  51. 6
      app/src/main/java/io/legado/app/model/localBook/EPUBFile.kt
  52. 19
      app/src/main/java/io/legado/app/model/localBook/LocalBook.kt
  53. 4
      app/src/main/java/io/legado/app/model/rss/RssParserByRule.kt
  54. 4
      app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt
  55. 7
      app/src/main/java/io/legado/app/model/webBook/BookContent.kt
  56. 4
      app/src/main/java/io/legado/app/model/webBook/BookInfo.kt
  57. 4
      app/src/main/java/io/legado/app/model/webBook/BookList.kt
  58. 9
      app/src/main/java/io/legado/app/model/webBook/SearchBookModel.kt
  59. 4
      app/src/main/java/io/legado/app/receiver/MediaButtonReceiver.kt
  60. 10
      app/src/main/java/io/legado/app/receiver/SharedReceiverActivity.kt
  61. 17
      app/src/main/java/io/legado/app/service/AudioPlayService.kt
  62. 4
      app/src/main/java/io/legado/app/service/BaseReadAloudService.kt
  63. 15
      app/src/main/java/io/legado/app/service/CacheBookService.kt
  64. 12
      app/src/main/java/io/legado/app/service/CheckSourceService.kt
  65. 6
      app/src/main/java/io/legado/app/service/DownloadService.kt
  66. 24
      app/src/main/java/io/legado/app/service/HttpReadAloudService.kt
  67. 4
      app/src/main/java/io/legado/app/service/TTSReadAloudService.kt
  68. 13
      app/src/main/java/io/legado/app/service/WebService.kt
  69. 10
      app/src/main/java/io/legado/app/service/help/AudioPlay.kt
  70. 4
      app/src/main/java/io/legado/app/service/help/CacheBook.kt
  71. 4
      app/src/main/java/io/legado/app/service/help/CheckSource.kt
  72. 9
      app/src/main/java/io/legado/app/service/help/ReadAloud.kt
  73. 25
      app/src/main/java/io/legado/app/service/help/ReadBook.kt
  74. 2
      app/src/main/java/io/legado/app/ui/about/AboutActivity.kt
  75. 8
      app/src/main/java/io/legado/app/ui/about/AboutFragment.kt
  76. 4
      app/src/main/java/io/legado/app/ui/about/DonateFragment.kt
  77. 23
      app/src/main/java/io/legado/app/ui/about/ReadRecordActivity.kt
  78. 11
      app/src/main/java/io/legado/app/ui/association/FileAssociationActivity.kt
  79. 9
      app/src/main/java/io/legado/app/ui/association/ImportBookSourceActivity.kt
  80. 23
      app/src/main/java/io/legado/app/ui/association/ImportBookSourceDialog.kt
  81. 4
      app/src/main/java/io/legado/app/ui/association/ImportBookSourceViewModel.kt
  82. 12
      app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleActivity.kt
  83. 9
      app/src/main/java/io/legado/app/ui/association/ImportRssSourceActivity.kt
  84. 21
      app/src/main/java/io/legado/app/ui/association/ImportRssSourceDialog.kt
  85. 4
      app/src/main/java/io/legado/app/ui/association/ImportRssSourceViewModel.kt
  86. 43
      app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt
  87. 28
      app/src/main/java/io/legado/app/ui/audio/AudioPlayViewModel.kt
  88. 20
      app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt
  89. 10
      app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookAdapter.kt
  90. 8
      app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookViewModel.kt
  91. 21
      app/src/main/java/io/legado/app/ui/book/cache/CacheActivity.kt
  92. 6
      app/src/main/java/io/legado/app/ui/book/cache/CacheAdapter.kt
  93. 11
      app/src/main/java/io/legado/app/ui/book/cache/CacheViewModel.kt
  94. 6
      app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt
  95. 8
      app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt
  96. 4
      app/src/main/java/io/legado/app/ui/book/changecover/CoverAdapter.kt
  97. 6
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceAdapter.kt
  98. 8
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt
  99. 27
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt
  100. 14
      app/src/main/java/io/legado/app/ui/book/explore/ExploreShowActivity.kt
  101. Some files were not shown because too many files have changed in this diff Show More

@ -125,17 +125,26 @@ dependencies {
implementation 'com.google.firebase:firebase-crashlytics-ktx:17.3.0' implementation 'com.google.firebase:firebase-crashlytics-ktx:17.3.0'
//androidX //androidX
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.media:media:1.2.1' implementation 'androidx.core:core-ktx:1.3.2'
implementation "androidx.activity:activity-ktx:1.1.0"
implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation 'androidx.preference:preference-ktx:1.1.1' implementation 'androidx.preference:preference-ktx:1.1.1'
implementation "androidx.collection:collection-ktx:1.1.0"
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.viewpager2:viewpager2:1.0.0' implementation 'androidx.viewpager2:viewpager2:1.0.0'
implementation 'androidx.media:media:1.2.1'
implementation 'com.google.android.material:material:1.2.1' implementation 'com.google.android.material:material:1.2.1'
implementation 'com.google.android:flexbox:1.1.0' implementation 'com.google.android:flexbox:1.1.0'
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.8.6'
//Splitties
def splitties_version = '2.1.1'
implementation("com.louiscad.splitties:splitties-appctx:$splitties_version")
implementation("com.louiscad.splitties:splitties-systemservices:$splitties_version")
implementation("com.louiscad.splitties:splitties-views:$splitties_version")
//lifecycle //lifecycle
def lifecycle_version = '2.2.0' def lifecycle_version = '2.2.0'
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
@ -150,11 +159,6 @@ dependencies {
//paging //paging
implementation 'androidx.paging:paging-runtime-ktx:2.1.2' implementation 'androidx.paging:paging-runtime-ktx:2.1.2'
//anko
def anko_version = '0.10.8'
implementation "org.jetbrains.anko:anko-sdk27:$anko_version"
implementation "org.jetbrains.anko:anko-sdk27-listeners:$anko_version"
//liveEventBus //liveEventBus
implementation 'com.jeremyliao:live-event-bus-x:1.5.7' implementation 'com.jeremyliao:live-event-bus-x:1.5.7'
@ -185,7 +189,7 @@ dependencies {
implementation 'org.nanohttpd:nanohttpd-websocket:2.3.1' implementation 'org.nanohttpd:nanohttpd-websocket:2.3.1'
// //
implementation 'cn.bingoogolapple:bga-qrcode-zxing:1.3.7' implementation 'com.king.zxing:zxing-lite:2.0.2'
// //
implementation 'com.jaredrummler:colorpicker:1.1.0' implementation 'com.jaredrummler:colorpicker:1.1.0'

@ -418,6 +418,10 @@
<intent> <intent>
<action android:name="android.intent.action.TTS_SERVICE" /> <action android:name="android.intent.action.TTS_SERVICE" />
</intent> </intent>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT" />
<data android:mimeType="text/plain" />
</intent>
</queries> </queries>
</manifest> </manifest>

@ -80,3 +80,206 @@ cm5neHM=
OTl3ZW5rdQ== OTl3ZW5rdQ==
bGFvc2lqaXhz bGFvc2lqaXhz
ZnVzaHV6aGFpMQ== ZnVzaHV6aGFpMQ==
cG8xOA==
czUyMTc=
czUyMTc=
c2FuaGFveHM=
NTJrc2h1
NDhyeA==
ZWNub3ZlbA==
bGllaHVvenc=
eGlhb3FpYW5nd3g=
NTJrc2h1
NDh3eA==
NTJrc2h1
MDB1aQ==
MDFieg==
c2h1YmFvMQ==
ZG54aWFvc2h1b2E=
am5zaHViYQ==
MThzaHV3dQ==
bGV4cw==
MzM1eHM=
dXB1
ZnVndW9kdQ==
ODB0eHQ=
YWFyZWFk
eWlkdWR1MQ==
YmFuemh1d2FuZw==
cWloYW9xaWhhbw==
OHhpYW54cw==
amluamlzaHV3dQ==
d21wOA==
ZXl1c2h1d3U=
NTB4c2Y=
aGF4d3g1
cG93YW5qdWFu
d2luMTBjaXR5
eWV5ZXhzdw==
bXlzaHVnZQ==
eGlhbmd0eHN3
Y3Vpd2VpanV4
MzY2eHN3
aHVheXVld2Vua3U=
eW91ZGlhbmxlbg==
c291Nzg=
bGFucm91Mg==
cXFib29r
eW91d3V4cw==
cnVpbGlzYWxl
MzY1bXd3
ZnV3ZW5o
bGVzYmw=
YXd1Ym9vaw==
bGl5dXhpYW5nMjAyMA==
OTJwb3Bv
ZnVzaHV0dWFu
ODhkYW5tZWk=
ZG14cw==
eXVsaW56aGFueWU=
M2hlYmFv
eGd1YWd1YXhz
ZGl5aWJhbnpodTY=
aXJlYWR4cw==
c2h1YmFvOTY=
ZGl5aWJhbnpodTU1NQ==
c2Fuaml1enc=
N3Fpbmc3
NjZsZXdlbg==
a3l4czU=
MjIyMjJ4cw==
c2hhb3NodWdl
amlsaW41NQ==
bWt4czY=
amluc2h1bG91
eGlhbndhbmdz
eWlkdWR1
cWR0eHQ=
MTZib29rMQ==
am1zaHV3dQ==
MzY2eHN3
ZHliejk=
c2hvdWRhOA==
ZnlxMTg=
eWlzaHVn
eXV6aGFpd3VsYQ==
MTFiYW56aHU=
MTIzeGlhb3FpYW5n
ZGl5aWJhbnpodTk=
ZGl5aWJhbnpodQ==
MzY2eHN3
ODdzaHV3dQ==
NnF3eA==
emhlbmh1bnhpYW9zaHVv
bG9uZ3Rlbmc1Mg==
eGlueGluZ3hpYW5nemhpZmE=
ZHliejk=
ZHVvemhla2Fu
MTIzeGlhb3FpYW5n
MzM1eHM=
am1zaHV3dQ==
c2hhb3NodWdl
bGF3ZW54cw==
cnVzaHV3dQ==
MzY2eHN3
NTB4c2Y=
bGV3ZW41NQ==
aGFpdGFuZzEyMw==
aGViYW81MjA=
bHVvcWl1enc=
c3NzeHN3
c2h1c2h1d3V4cw==
cm5neHM=
cWR4aWFvc2h1bw==
dHl1ZQ==
Y2hlNDM=
bG9uZ3Rlbmcy
amZ5eHNo
aGV0dTI=
bGFvc2lqaXhz
bG9uZ3Rlbmd4cw==
bGllaHVvenc=
c2h1YmFvYW4=
eHNodW9zaHVv
NTIxZGFubWVp
YmFuemh1MjI=
cWtzaHU=
eWZ4aWFvc2h1b2U=
a3lnc28=
c2h1bG91YmE=
NXRucw==
N3Fpbmc3
bWlhb2R1NQ==
eXVzaHV3ZW4=
YWFyZWFk
cXRzaHU=
MTdzaHV3dQ==
c2h1YmFvMnM=
YnowMDE=
ZGFtb2dl
MTMxdGI=
aXhpYW9z
bXlzaHVnZQ==
OXhpYW53ZW4=
ZHVvemhla2Fu
MTIwdw==
c2h1c2h1d3U1MjA=
c2h1YmFvMnM=
YWd4c3c=
OTR4c3c=
cG8xOA==
eWFvY2hpeHM=
eGlhb3FpYW5neHM=
Ym9va2Js
c2Fuaml1eHM=
d29kZXNodWJhbw==
em9uZ2NhaXhpYW9zaHVvMg==
OWI4OTEzOTRkZjVi
MThub3ZlbA==
YWFib29r
YjF0eHQ=
eXVjYWl6dw==
Yzl0eHQ=
ZGl5aWJhbnpodTU1NQ==
MzBtYw==
eGlueXVzaHV3dQ==
c2h1YmFvd2FuZzEyMw==
YWd4cw==
YmlxdWdlbmw=
c2hpcWlzaHV3dQ==
c2lsdWtl
ZGl5aWJhbnpodTg=
ZGl5aWJhbnpodTk=
aGV0dW54cw==
OTl3ZW5rdQ==
aGFpdGFuZ3NodXd1
OTd5ZA==
eXV6aGFpd3UxMQ==
Y3Vpd2VpanV4cw==
Y2JpcXU=
NTIxZGFubWVp
c2h1YmFvMzM=
c2FuaGFvMQ==
dGlhbm1lbmd3ZW5rdQ==
eXVzaHV3dTUyMA==
c2h1YmFvMjIy
c2h1YmFvd2FuZzEyMw==
eXVib29r
Y2JpcXU=
MWxld2Vu
MTV4c3c=
eG5jd3h3
c2h1YmFvd2FuZzEyMw==
c2FuaGFveHM=
eXV3YW5nc2hl
YmlxdXRz
bGFtZWl4cw==
eGJhbnpodQ==
cWR4aWFvc2h1bw==
bWh0bGE=
OTl3ZW5rdQ==
eGlhb3FpYW5nNTIw
dGlhbm1lbmd3ZW5rdQ==
YWlmdXNodQ==
bWlhb2R1NQ==
bWlmZW5neHM=

@ -0,0 +1,6 @@
# 替换管理界面帮助
* 替换规则是用来替换正文内容的一种规则
* 菜单可以新建和导入规则
* 可以拖动排序
* 可以选择操作

@ -3,6 +3,16 @@
* 关注合作公众号 **[小说拾遗]** 获取好看的小说。 * 关注合作公众号 **[小说拾遗]** 获取好看的小说。
* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
**2021/02/03**
* 排版导出文件名修改为配置名称
* 取消在线朗读下载文件检测,会导致朗读中断
* 修复其它一些bug
**2021/01/30**
* 优化阅读记录界面
* 自定义分组可以隐藏,删除按钮移到编辑对话框
* 修复其它一些bug
**2021/01/23** **2021/01/23**
* 优化书源校验,从搜索到正文全部校验 * 优化书源校验,从搜索到正文全部校验
* play版可以设置背景图片 * play版可以设置背景图片
@ -264,27 +274,4 @@ getTxtInFolder(unzipPath: String): String
**2020/09/10** **2020/09/10**
* 修复自动换源的bug * 修复自动换源的bug
* 修复保存主题的bug * 修复保存主题的bug
* 书源排序,分享,注释优化 by [h11128](https://github.com/h11128) * 书源排序,分享,注释优化 by [h11128](https://github.com/h11128)
**2020/09/09**
* 修复主题导入导出bug
* 优化分屏模式状态栏
* 书源基本属性增加“书源注释”
* 页眉页脚跟随背景
* 主题导入导出
* 订阅源和替换规则添加滑动选择
* 修复排版配置导入导出
* 订阅界面添加下载文件功能
* 优化翻页
* EInk模式独立背景
* 阅读排版配置导入导出,包括背景和字体,支持网络导入
* 修复替换中的回车消失的bug
* 所有内容恢复htmlFormat, 在想其它办法解决丢失一些内容的问题
* 图片(漫画)支持导出
* 搜索url支持put,get,js里使用java.put,java.get
* 正文合并后替换规则支持所有规则写法,包括js
* 导入书源列表添加全不选
* 详情页菜单添加清理缓存,清理当前书籍缓存
* 修复滑动选择,选择数量不更新的bug
* 字体跟随背景,每个背景对应一个字体
* 优化图片下载

@ -5,58 +5,33 @@ import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.os.Build import android.os.Build
import android.provider.Settings
import androidx.appcompat.app.AppCompatDelegate
import androidx.multidex.MultiDexApplication import androidx.multidex.MultiDexApplication
import com.jeremyliao.liveeventbus.LiveEventBus import com.jeremyliao.liveeventbus.LiveEventBus
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppConst.channelIdDownload import io.legado.app.constant.AppConst.channelIdDownload
import io.legado.app.constant.AppConst.channelIdReadAloud import io.legado.app.constant.AppConst.channelIdReadAloud
import io.legado.app.constant.AppConst.channelIdWeb import io.legado.app.constant.AppConst.channelIdWeb
import io.legado.app.constant.EventBus import io.legado.app.help.ActivityHelp
import io.legado.app.data.AppDatabase import io.legado.app.help.AppConfig
import io.legado.app.help.* import io.legado.app.help.CrashHandler
import io.legado.app.help.ThemeConfig.applyDayNight
import io.legado.app.help.http.HttpHelper import io.legado.app.help.http.HttpHelper
import io.legado.app.utils.LanguageUtils import io.legado.app.utils.LanguageUtils
import io.legado.app.utils.postEvent import io.legado.app.utils.defaultSharedPreferences
import org.jetbrains.anko.defaultSharedPreferences
import rxhttp.wrapper.param.RxHttp import rxhttp.wrapper.param.RxHttp
@Suppress("DEPRECATION")
class App : MultiDexApplication() { class App : MultiDexApplication() {
companion object {
@JvmStatic
lateinit var INSTANCE: App
private set
@JvmStatic
lateinit var db: AppDatabase
private set
lateinit var androidId: String
var versionCode = 0
var versionName = ""
var navigationBarHeight = 0
}
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
INSTANCE = this
androidId = Settings.System.getString(contentResolver, Settings.Secure.ANDROID_ID)
CrashHandler(this) CrashHandler(this)
LanguageUtils.setConfiguration(this) LanguageUtils.setConfiguration(this)
db = AppDatabase.createDatabase(INSTANCE)
RxHttp.init(HttpHelper.client, BuildConfig.DEBUG) RxHttp.init(HttpHelper.client, BuildConfig.DEBUG)
RxHttp.setOnParamAssembly { RxHttp.setOnParamAssembly {
it.addHeader(AppConst.UA_NAME, AppConfig.userAgent) it.addHeader(AppConst.UA_NAME, AppConfig.userAgent)
} }
packageManager.getPackageInfo(packageName, 0)?.let {
versionCode = it.versionCode
versionName = it.versionName
}
createNotificationChannels() createNotificationChannels()
applyDayNight() applyDayNight(this)
LiveEventBus.config() LiveEventBus.config()
.supportBroadcast(this) .supportBroadcast(this)
.lifecycleObserverAlwaysActive(true) .lifecycleObserverAlwaysActive(true)
@ -69,27 +44,10 @@ class App : MultiDexApplication() {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
when (newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK) { when (newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
Configuration.UI_MODE_NIGHT_YES, Configuration.UI_MODE_NIGHT_YES,
Configuration.UI_MODE_NIGHT_NO -> applyDayNight() Configuration.UI_MODE_NIGHT_NO -> applyDayNight(this)
} }
} }
fun applyDayNight() {
ReadBookConfig.upBg()
ThemeConfig.applyTheme(this)
initNightMode()
postEvent(EventBus.RECREATE, "")
}
private fun initNightMode() {
val targetMode =
if (AppConfig.isNightTheme) {
AppCompatDelegate.MODE_NIGHT_YES
} else {
AppCompatDelegate.MODE_NIGHT_NO
}
AppCompatDelegate.setDefaultNightMode(targetMode)
}
/** /**
* 创建通知ID * 创建通知ID
*/ */
@ -99,7 +57,7 @@ class App : MultiDexApplication() {
val downloadChannel = NotificationChannel( val downloadChannel = NotificationChannel(
channelIdDownload, channelIdDownload,
getString(R.string.action_download), getString(R.string.action_download),
NotificationManager.IMPORTANCE_LOW NotificationManager.IMPORTANCE_DEFAULT
).apply { ).apply {
enableLights(false) enableLights(false)
enableVibration(false) enableVibration(false)
@ -109,7 +67,7 @@ class App : MultiDexApplication() {
val readAloudChannel = NotificationChannel( val readAloudChannel = NotificationChannel(
channelIdReadAloud, channelIdReadAloud,
getString(R.string.read_aloud), getString(R.string.read_aloud),
NotificationManager.IMPORTANCE_LOW NotificationManager.IMPORTANCE_DEFAULT
).apply { ).apply {
enableLights(false) enableLights(false)
enableVibration(false) enableVibration(false)
@ -119,7 +77,7 @@ class App : MultiDexApplication() {
val webChannel = NotificationChannel( val webChannel = NotificationChannel(
channelIdWeb, channelIdWeb,
getString(R.string.web_service), getString(R.string.web_service),
NotificationManager.IMPORTANCE_LOW NotificationManager.IMPORTANCE_DEFAULT
).apply { ).apply {
enableLights(false) enableLights(false)
enableVibration(false) enableVibration(false)
@ -131,4 +89,8 @@ class App : MultiDexApplication() {
} }
} }
companion object {
var navigationBarHeight = 0
}
} }

@ -1,8 +1,8 @@
package io.legado.app.api.controller package io.legado.app.api.controller
import io.legado.app.App
import io.legado.app.api.ReturnData import io.legado.app.api.ReturnData
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
@ -12,17 +12,18 @@ import io.legado.app.utils.cnCompare
import io.legado.app.utils.fromJsonObject import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.getPrefInt import io.legado.app.utils.getPrefInt
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import splitties.init.appCtx
object BookshelfController { object BookshelfController {
val bookshelf: ReturnData val bookshelf: ReturnData
get() { get() {
val books = App.db.bookDao.all val books = appDb.bookDao.all
val returnData = ReturnData() val returnData = ReturnData()
return if (books.isEmpty()) { return if (books.isEmpty()) {
returnData.setErrorMsg("还没有添加小说") returnData.setErrorMsg("还没有添加小说")
} else { } else {
val data = when (App.INSTANCE.getPrefInt(PreferKey.bookshelfSort)) { val data = when (appCtx.getPrefInt(PreferKey.bookshelfSort)) {
1 -> books.sortedByDescending { it.latestChapterTime } 1 -> books.sortedByDescending { it.latestChapterTime }
2 -> books.sortedWith { o1, o2 -> 2 -> books.sortedWith { o1, o2 ->
o1.name.cnCompare(o2.name) o1.name.cnCompare(o2.name)
@ -40,7 +41,7 @@ object BookshelfController {
if (bookUrl.isNullOrEmpty()) { if (bookUrl.isNullOrEmpty()) {
return returnData.setErrorMsg("参数url不能为空,请指定书籍地址") return returnData.setErrorMsg("参数url不能为空,请指定书籍地址")
} }
val chapterList = App.db.bookChapterDao.getChapterList(bookUrl) val chapterList = appDb.bookChapterDao.getChapterList(bookUrl)
return returnData.setData(chapterList) return returnData.setData(chapterList)
} }
@ -54,8 +55,8 @@ object BookshelfController {
if (index == null) { if (index == null) {
return returnData.setErrorMsg("参数index不能为空, 请指定目录序号") return returnData.setErrorMsg("参数index不能为空, 请指定目录序号")
} }
val book = App.db.bookDao.getBook(bookUrl) val book = appDb.bookDao.getBook(bookUrl)
val chapter = App.db.bookChapterDao.getChapter(bookUrl, index) val chapter = appDb.bookChapterDao.getChapter(bookUrl, index)
if (book == null || chapter == null) { if (book == null || chapter == null) {
returnData.setErrorMsg("未找到") returnData.setErrorMsg("未找到")
} else { } else {
@ -64,7 +65,7 @@ object BookshelfController {
saveBookReadIndex(book, index) saveBookReadIndex(book, index)
returnData.setData(content) returnData.setData(content)
} else { } else {
App.db.bookSourceDao.getBookSource(book.origin)?.let { source -> appDb.bookSourceDao.getBookSource(book.origin)?.let { source ->
runBlocking { runBlocking {
WebBook(source).getContentAwait(this, book, chapter) WebBook(source).getContentAwait(this, book, chapter)
}.let { }.let {
@ -81,7 +82,7 @@ object BookshelfController {
val book = GSON.fromJsonObject<Book>(postData) val book = GSON.fromJsonObject<Book>(postData)
val returnData = ReturnData() val returnData = ReturnData()
if (book != null) { if (book != null) {
App.db.bookDao.insert(book) appDb.bookDao.insert(book)
if (ReadBook.book?.bookUrl == book.bookUrl) { if (ReadBook.book?.bookUrl == book.bookUrl) {
ReadBook.book = book ReadBook.book = book
ReadBook.durChapterIndex = book.durChapterIndex ReadBook.durChapterIndex = book.durChapterIndex
@ -95,10 +96,10 @@ object BookshelfController {
if (index > book.durChapterIndex) { if (index > book.durChapterIndex) {
book.durChapterIndex = index book.durChapterIndex = index
book.durChapterTime = System.currentTimeMillis() book.durChapterTime = System.currentTimeMillis()
App.db.bookChapterDao.getChapter(book.bookUrl, index)?.let { appDb.bookChapterDao.getChapter(book.bookUrl, index)?.let {
book.durChapterTitle = it.title book.durChapterTitle = it.title
} }
App.db.bookDao.update(book) appDb.bookDao.update(book)
if (ReadBook.book?.bookUrl == book.bookUrl) { if (ReadBook.book?.bookUrl == book.bookUrl) {
ReadBook.book = book ReadBook.book = book
ReadBook.durChapterIndex = index ReadBook.durChapterIndex = index

@ -2,8 +2,8 @@ package io.legado.app.api.controller
import android.text.TextUtils import android.text.TextUtils
import io.legado.app.App
import io.legado.app.api.ReturnData import io.legado.app.api.ReturnData
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray import io.legado.app.utils.fromJsonArray
@ -14,7 +14,7 @@ object SourceController {
val sources: ReturnData val sources: ReturnData
get() { get() {
val bookSources = App.db.bookSourceDao.all val bookSources = appDb.bookSourceDao.all
val returnData = ReturnData() val returnData = ReturnData()
return if (bookSources.isEmpty()) { return if (bookSources.isEmpty()) {
returnData.setErrorMsg("设备书源列表为空") returnData.setErrorMsg("设备书源列表为空")
@ -29,7 +29,7 @@ object SourceController {
if (TextUtils.isEmpty(bookSource.bookSourceName) || TextUtils.isEmpty(bookSource.bookSourceUrl)) { if (TextUtils.isEmpty(bookSource.bookSourceName) || TextUtils.isEmpty(bookSource.bookSourceUrl)) {
returnData.setErrorMsg("书源名称和URL不能为空") returnData.setErrorMsg("书源名称和URL不能为空")
} else { } else {
App.db.bookSourceDao.insert(bookSource) appDb.bookSourceDao.insert(bookSource)
returnData.setData("") returnData.setData("")
} }
} else { } else {
@ -50,7 +50,7 @@ object SourceController {
if (bookSource.bookSourceName.isBlank() || bookSource.bookSourceUrl.isBlank()) { if (bookSource.bookSourceName.isBlank() || bookSource.bookSourceUrl.isBlank()) {
continue continue
} }
App.db.bookSourceDao.insert(bookSource) appDb.bookSourceDao.insert(bookSource)
okSources.add(bookSource) okSources.add(bookSource)
} }
} }
@ -64,7 +64,7 @@ object SourceController {
if (url.isNullOrEmpty()) { if (url.isNullOrEmpty()) {
return returnData.setErrorMsg("参数url不能为空,请指定书源地址") return returnData.setErrorMsg("参数url不能为空,请指定书源地址")
} }
val bookSource = App.db.bookSourceDao.getBookSource(url) val bookSource = appDb.bookSourceDao.getBookSource(url)
?: return returnData.setErrorMsg("未找到书源,请检查书源地址") ?: return returnData.setErrorMsg("未找到书源,请检查书源地址")
return returnData.setData(bookSource) return returnData.setData(bookSource)
} }
@ -73,7 +73,7 @@ object SourceController {
kotlin.runCatching { kotlin.runCatching {
GSON.fromJsonArray<BookSource>(postData)?.let { GSON.fromJsonArray<BookSource>(postData)?.let {
it.forEach { source -> it.forEach { source ->
App.db.bookSourceDao.delete(source) appDb.bookSourceDao.delete(source)
} }
} }
} }

@ -6,15 +6,13 @@ import androidx.annotation.CallSuper
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import io.legado.app.App import io.legado.app.App
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.jetbrains.anko.AnkoLogger
import org.jetbrains.anko.toast
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
@Suppress("unused") @Suppress("unused")
open class BaseViewModel(application: Application) : AndroidViewModel(application), open class BaseViewModel(application: Application) : AndroidViewModel(application),
CoroutineScope by MainScope(), CoroutineScope by MainScope() {
AnkoLogger {
val context: Context by lazy { this.getApplication<App>() } val context: Context by lazy { this.getApplication<App>() }
@ -40,27 +38,12 @@ open class BaseViewModel(application: Application) : AndroidViewModel(applicatio
cancel() cancel()
} }
open fun toast(message: Int) { open fun toastOnUi(message: Int) {
launch { context.toastOnUi(message)
context.toast(message)
}
} }
open fun toast(message: CharSequence?) { open fun toastOnUi(message: CharSequence?) {
launch { context.toastOnUi(message ?: toString())
context.toast(message ?: toString())
}
} }
open fun longToast(message: Int) {
launch {
context.toast(message)
}
}
open fun longToast(message: CharSequence?) {
launch {
context.toast(message ?: toString())
}
}
} }

@ -8,6 +8,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import splitties.views.onLongClick
/** /**
* Created by Invincible on 2017/12/15. * Created by Invincible on 2017/12/15.
@ -130,10 +131,10 @@ abstract class DiffRecyclerAdapter<ITEM, VB : ViewBinding>(protected val context
} }
if (itemLongClickListener != null) { if (itemLongClickListener != null) {
holder.itemView.setOnLongClickListener { holder.itemView.onLongClick {
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
itemLongClickListener?.invoke(holder, it) ?: true itemLongClickListener?.invoke(holder, it)
} ?: true }
} }
} }

@ -8,6 +8,7 @@ import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding import androidx.viewbinding.ViewBinding
import splitties.views.onLongClick
import java.util.* import java.util.*
/** /**
@ -351,10 +352,10 @@ abstract class RecyclerAdapter<ITEM, VB : ViewBinding>(protected val context: Co
} }
if (itemLongClickListener != null) { if (itemLongClickListener != null) {
holder.itemView.setOnLongClickListener { holder.itemView.onLongClick {
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
itemLongClickListener?.invoke(holder, it) ?: true itemLongClickListener?.invoke(holder, it)
} ?: true }
} }
} }

@ -1,8 +1,8 @@
package io.legado.app.constant package io.legado.app.constant
import android.annotation.SuppressLint import android.annotation.SuppressLint
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import splitties.init.appCtx
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import javax.script.ScriptEngine import javax.script.ScriptEngine
import javax.script.ScriptEngineManager import javax.script.ScriptEngineManager
@ -68,5 +68,12 @@ object AppConst {
"androidx.appcompat.view.menu.ListMenuItemView" "androidx.appcompat.view.menu.ListMenuItemView"
) )
val sysElevation = App.INSTANCE.resources.getDimension(R.dimen.design_appbar_elevation).toInt() val sysElevation = appCtx.resources.getDimension(R.dimen.design_appbar_elevation).toInt()
val darkWebViewJs by lazy {
"""
document.body.style.backgroundColor = "#222222";
document.getElementsByTagName('body')[0].style.webkitTextFillColor = '#8a8a8a';
""".trimIndent()
}
} }

@ -0,0 +1,27 @@
package io.legado.app.constant
import android.provider.Settings
import splitties.init.appCtx
val androidId: String by lazy {
Settings.System.getString(appCtx.contentResolver, Settings.Secure.ANDROID_ID)
}
val appInfo: AppInfo by lazy {
val appInfo = AppInfo()
appCtx.packageManager.getPackageInfo(appCtx.packageName, 0)?.let {
appInfo.versionName = it.versionName
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) {
appInfo.versionCode = it.longVersionCode
} else {
@Suppress("DEPRECATION")
appInfo.versionCode = it.versionCode.toLong()
}
}
appInfo
}
data class AppInfo(
var versionCode: Long = 0L,
var versionName: String = ""
)

@ -6,12 +6,16 @@ import androidx.room.Room
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.migration.Migration import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase import androidx.sqlite.db.SupportSQLiteDatabase
import io.legado.app.App
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.androidId
import io.legado.app.data.dao.* import io.legado.app.data.dao.*
import io.legado.app.data.entities.* import io.legado.app.data.entities.*
import splitties.init.appCtx
import java.util.* import java.util.*
val appDb by lazy {
AppDatabase.createDatabase(appCtx)
}
@Database( @Database(
entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class, entities = [Book::class, BookGroup::class, BookSource::class, BookChapter::class,
@ -151,7 +155,7 @@ abstract class AppDatabase : RoomDatabase() {
"""CREATE TABLE IF NOT EXISTS `readRecordNew` (`androidId` TEXT NOT NULL, `bookName` TEXT NOT NULL, `readTime` INTEGER NOT NULL, """CREATE TABLE IF NOT EXISTS `readRecordNew` (`androidId` TEXT NOT NULL, `bookName` TEXT NOT NULL, `readTime` INTEGER NOT NULL,
PRIMARY KEY(`androidId`, `bookName`))""" PRIMARY KEY(`androidId`, `bookName`))"""
) )
database.execSQL("INSERT INTO readRecordNew(androidId, bookName, readTime) select '${App.androidId}' as androidId, bookName, readTime from readRecord") database.execSQL("INSERT INTO readRecordNew(androidId, bookName, readTime) select '${androidId}' as androidId, bookName, readTime from readRecord")
database.execSQL("DROP TABLE readRecord") database.execSQL("DROP TABLE readRecord")
database.execSQL("ALTER TABLE readRecordNew RENAME TO readRecord") database.execSQL("ALTER TABLE readRecordNew RENAME TO readRecord")
} }

@ -19,7 +19,7 @@ interface BookGroupDao {
@Query( @Query(
""" """
SELECT * FROM book_groups where groupId >= 0 SELECT * FROM book_groups where (groupId >= 0 and show > 0)
or (groupId = -4 and show > 0 and (select count(bookUrl) from books where type != ${BookType.audio} and origin != '${BookType.local}' and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0) > 0) or (groupId = -4 and show > 0 and (select count(bookUrl) from books where type != ${BookType.audio} and origin != '${BookType.local}' and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0) > 0)
or (groupId = -3 and show > 0 and (select count(bookUrl) from books where type = ${BookType.audio}) > 0) or (groupId = -3 and show > 0 and (select count(bookUrl) from books where type = ${BookType.audio}) > 0)
or (groupId = -2 and show > 0 and (select count(bookUrl) from books where origin = '${BookType.local}') > 0) or (groupId = -2 and show > 0 and (select count(bookUrl) from books where origin = '${BookType.local}') > 0)

@ -2,9 +2,9 @@ package io.legado.app.data.entities
import android.os.Parcelable import android.os.Parcelable
import androidx.room.* import androidx.room.*
import io.legado.app.App
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.constant.BookType import io.legado.app.constant.BookType
import io.legado.app.data.appDb
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
@ -182,14 +182,14 @@ data class Book(
newBook.canUpdate = canUpdate newBook.canUpdate = canUpdate
newBook.readConfig = readConfig newBook.readConfig = readConfig
delete() delete()
App.db.bookDao.insert(newBook) appDb.bookDao.insert(newBook)
} }
fun delete() { fun delete() {
if (ReadBook.book?.bookUrl == bookUrl) { if (ReadBook.book?.bookUrl == bookUrl) {
ReadBook.book = null ReadBook.book = null
} }
App.db.bookDao.delete(this) appDb.bookDao.delete(this)
} }
fun upInfoFromOld(oldBook: Book?) { fun upInfoFromOld(oldBook: Book?) {

@ -3,7 +3,6 @@ package io.legado.app.data.entities
import android.os.Parcelable import android.os.Parcelable
import android.text.TextUtils import android.text.TextUtils
import androidx.room.* import androidx.room.*
import io.legado.app.App
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.BookType import io.legado.app.constant.BookType
import io.legado.app.data.entities.rule.* import io.legado.app.data.entities.rule.*
@ -16,6 +15,7 @@ import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonObject import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.splitNotBlank import io.legado.app.utils.splitNotBlank
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import splitties.init.appCtx
import javax.script.SimpleBindings import javax.script.SimpleBindings
@Parcelize @Parcelize
@ -106,7 +106,7 @@ data class BookSource(
if (a.isNotBlank()) { if (a.isNotBlank()) {
kotlin.runCatching { kotlin.runCatching {
if (urlRule.startsWith("<js>", false)) { if (urlRule.startsWith("<js>", false)) {
val aCache = ACache.get(App.INSTANCE, "explore") val aCache = ACache.get(appCtx, "explore")
a = aCache.getAsString(bookSourceUrl) ?: "" a = aCache.getAsString(bookSourceUrl) ?: ""
if (a.isBlank()) { if (a.isBlank()) {
val bindings = SimpleBindings() val bindings = SimpleBindings()

@ -2,15 +2,15 @@ package io.legado.app.help
import android.content.Context import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.utils.* import io.legado.app.utils.*
import splitties.init.appCtx
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener { object AppConfig : SharedPreferences.OnSharedPreferenceChangeListener {
private val context get() = App.INSTANCE private val context get() = appCtx
val isGooglePlay = context.channel == "google" val isGooglePlay = context.channel == "google"
var userAgent: String = getPrefUserAgent() var userAgent: String = getPrefUserAgent()
var replaceEnableDefault = context.getPrefBoolean(PreferKey.replaceEnableDefault, true) var replaceEnableDefault = context.getPrefBoolean(PreferKey.replaceEnableDefault, true)

@ -1,8 +1,8 @@
package io.legado.app.help package io.legado.app.help
import io.legado.app.App
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
@ -11,6 +11,7 @@ import io.legado.app.model.localBook.LocalBook
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import org.apache.commons.text.similarity.JaccardSimilarity import org.apache.commons.text.similarity.JaccardSimilarity
import splitties.init.appCtx
import java.io.File import java.io.File
import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.CopyOnWriteArraySet
import java.util.regex.Matcher import java.util.regex.Matcher
@ -22,7 +23,7 @@ import kotlin.math.min
object BookHelp { object BookHelp {
private const val cacheFolderName = "book_cache" private const val cacheFolderName = "book_cache"
private const val cacheImageFolderName = "images" private const val cacheImageFolderName = "images"
private val downloadDir: File = App.INSTANCE.externalFilesDir private val downloadDir: File = appCtx.externalFilesDir
private val downloadImages = CopyOnWriteArraySet<String>() private val downloadImages = CopyOnWriteArraySet<String>()
fun clearCache() { fun clearCache() {
@ -42,7 +43,7 @@ object BookHelp {
fun clearRemovedCache() { fun clearRemovedCache() {
Coroutine.async { Coroutine.async {
val bookFolderNames = arrayListOf<String>() val bookFolderNames = arrayListOf<String>()
App.db.bookDao.all.forEach { appDb.bookDao.all.forEach {
bookFolderNames.add(it.getFolderName()) bookFolderNames.add(it.getFolderName())
} }
val file = FileUtils.getFile(downloadDir, cacheFolderName) val file = FileUtils.getFile(downloadDir, cacheFolderName)

@ -1,9 +1,10 @@
package io.legado.app.help package io.legado.app.help
import io.legado.app.App import io.legado.app.data.appDb
import io.legado.app.data.entities.Cache import io.legado.app.data.entities.Cache
import io.legado.app.model.analyzeRule.QueryTTF import io.legado.app.model.analyzeRule.QueryTTF
import io.legado.app.utils.ACache import io.legado.app.utils.ACache
import splitties.init.appCtx
@Suppress("unused") @Suppress("unused")
object CacheManager { object CacheManager {
@ -19,16 +20,16 @@ object CacheManager {
if (saveTime == 0) 0 else System.currentTimeMillis() + saveTime * 1000 if (saveTime == 0) 0 else System.currentTimeMillis() + saveTime * 1000
when (value) { when (value) {
is QueryTTF -> queryTTFMap[key] = Pair(deadline, value) is QueryTTF -> queryTTFMap[key] = Pair(deadline, value)
is ByteArray -> ACache.get(App.INSTANCE).put(key, value, saveTime) is ByteArray -> ACache.get(appCtx).put(key, value, saveTime)
else -> { else -> {
val cache = Cache(key, value.toString(), deadline) val cache = Cache(key, value.toString(), deadline)
App.db.cacheDao.insert(cache) appDb.cacheDao.insert(cache)
} }
} }
} }
fun get(key: String): String? { fun get(key: String): String? {
return App.db.cacheDao.get(key, System.currentTimeMillis()) return appDb.cacheDao.get(key, System.currentTimeMillis())
} }
fun getInt(key: String): Int? { fun getInt(key: String): Int? {
@ -48,7 +49,7 @@ object CacheManager {
} }
fun getByteArray(key: String): ByteArray? { fun getByteArray(key: String): ByteArray? {
return ACache.get(App.INSTANCE).getAsBinary(key) return ACache.get(appCtx).getAsBinary(key)
} }
fun getQueryTTF(key: String): QueryTTF? { fun getQueryTTF(key: String): QueryTTF? {

@ -1,12 +1,13 @@
package io.legado.app.help package io.legado.app.help
import com.hankcs.hanlp.HanLP import com.hankcs.hanlp.HanLP
import io.legado.app.App import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.ReplaceRule import io.legado.app.data.entities.ReplaceRule
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jetbrains.anko.toast import splitties.init.appCtx
class ContentProcessor(private val bookName: String, private val bookOrigin: String) { class ContentProcessor(private val bookName: String, private val bookOrigin: String) {
@ -19,7 +20,7 @@ class ContentProcessor(private val bookName: String, private val bookOrigin: Str
@Synchronized @Synchronized
fun upReplaceRules() { fun upReplaceRules() {
replaceRules.clear() replaceRules.clear()
replaceRules.addAll(App.db.replaceRuleDao.findEnabledByScope(bookName, bookOrigin)) replaceRules.addAll(appDb.replaceRuleDao.findEnabledByScope(bookName, bookOrigin))
} }
suspend fun getContent( suspend fun getContent(
@ -40,7 +41,7 @@ class ContentProcessor(private val bookName: String, private val bookOrigin: Str
} }
} catch (e: Exception) { } catch (e: Exception) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
App.INSTANCE.toast("${item.name}替换出错") appCtx.toastOnUi("${item.name}替换出错")
} }
} }
} }
@ -57,7 +58,7 @@ class ContentProcessor(private val bookName: String, private val bookOrigin: Str
} }
} catch (e: Exception) { } catch (e: Exception) {
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
App.INSTANCE.toast("简繁转换出错") appCtx.toastOnUi("简繁转换出错")
} }
} }
} }

@ -1,11 +1,12 @@
package io.legado.app.help package io.legado.app.help
import io.legado.app.App import io.legado.app.data.appDb
import io.legado.app.data.entities.HttpTTS import io.legado.app.data.entities.HttpTTS
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.data.entities.TxtTocRule import io.legado.app.data.entities.TxtTocRule
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray import io.legado.app.utils.fromJsonArray
import splitties.init.appCtx
import java.io.File import java.io.File
object DefaultData { object DefaultData {
@ -16,7 +17,7 @@ object DefaultData {
val httpTTS by lazy { val httpTTS by lazy {
val json = val json =
String( String(
App.INSTANCE.assets.open("defaultData${File.separator}$httpTtsFileName") appCtx.assets.open("defaultData${File.separator}$httpTtsFileName")
.readBytes() .readBytes()
) )
GSON.fromJsonArray<HttpTTS>(json)!! GSON.fromJsonArray<HttpTTS>(json)!!
@ -24,7 +25,7 @@ object DefaultData {
val readConfigs by lazy { val readConfigs by lazy {
val json = String( val json = String(
App.INSTANCE.assets.open("defaultData${File.separator}${ReadBookConfig.configFileName}") appCtx.assets.open("defaultData${File.separator}${ReadBookConfig.configFileName}")
.readBytes() .readBytes()
) )
GSON.fromJsonArray<ReadBookConfig.Config>(json)!! GSON.fromJsonArray<ReadBookConfig.Config>(json)!!
@ -32,7 +33,7 @@ object DefaultData {
val txtTocRules by lazy { val txtTocRules by lazy {
val json = String( val json = String(
App.INSTANCE.assets.open("defaultData${File.separator}$txtTocRuleFileName") appCtx.assets.open("defaultData${File.separator}$txtTocRuleFileName")
.readBytes() .readBytes()
) )
GSON.fromJsonArray<TxtTocRule>(json)!! GSON.fromJsonArray<TxtTocRule>(json)!!
@ -40,7 +41,7 @@ object DefaultData {
val themeConfigs by lazy { val themeConfigs by lazy {
val json = String( val json = String(
App.INSTANCE.assets.open("defaultData${File.separator}${ThemeConfig.configFileName}") appCtx.assets.open("defaultData${File.separator}${ThemeConfig.configFileName}")
.readBytes() .readBytes()
) )
GSON.fromJsonArray<ThemeConfig.Config>(json)!! GSON.fromJsonArray<ThemeConfig.Config>(json)!!
@ -48,23 +49,23 @@ object DefaultData {
val rssSources by lazy { val rssSources by lazy {
val json = String( val json = String(
App.INSTANCE.assets.open("defaultData${File.separator}rssSources.json") appCtx.assets.open("defaultData${File.separator}rssSources.json")
.readBytes() .readBytes()
) )
GSON.fromJsonArray<RssSource>(json)!! GSON.fromJsonArray<RssSource>(json)!!
} }
fun importDefaultHttpTTS() { fun importDefaultHttpTTS() {
App.db.httpTTSDao.deleteDefault() appDb.httpTTSDao.deleteDefault()
App.db.httpTTSDao.insert(*httpTTS.toTypedArray()) appDb.httpTTSDao.insert(*httpTTS.toTypedArray())
} }
fun importDefaultTocRules() { fun importDefaultTocRules() {
App.db.txtTocRule.deleteDefault() appDb.txtTocRule.deleteDefault()
App.db.txtTocRule.insert(*txtTocRules.toTypedArray()) appDb.txtTocRule.insert(*txtTocRules.toTypedArray())
} }
fun importDefaultRssSources() { fun importDefaultRssSources() {
App.db.rssSourceDao.insert(*rssSources.toTypedArray()) appDb.rssSourceDao.insert(*rssSources.toTypedArray())
} }
} }

@ -5,7 +5,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import io.legado.app.R import io.legado.app.R
import org.jetbrains.anko.toast import io.legado.app.utils.toastOnUi
@Suppress("unused") @Suppress("unused")
object IntentHelp { object IntentHelp {
@ -19,7 +19,7 @@ object IntentHelp {
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent) context.startActivity(intent)
}.onFailure { }.onFailure {
context.toast(R.string.tip_cannot_jump_setting_page) context.toastOnUi(R.string.tip_cannot_jump_setting_page)
} }
} }
@ -30,7 +30,7 @@ object IntentHelp {
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent) context.startActivity(intent)
}.onFailure { }.onFailure {
context.toast("无法打开设置") context.toastOnUi("无法打开设置")
} }
} }

@ -3,7 +3,6 @@ package io.legado.app.help
import android.net.Uri import android.net.Uri
import android.util.Base64 import android.util.Base64
import androidx.annotation.Keep import androidx.annotation.Keep
import io.legado.app.App
import io.legado.app.constant.AppConst.dateFormat import io.legado.app.constant.AppConst.dateFormat
import io.legado.app.help.http.CookieStore import io.legado.app.help.http.CookieStore
import io.legado.app.help.http.SSLHelper import io.legado.app.help.http.SSLHelper
@ -16,6 +15,7 @@ import org.jsoup.Connection
import org.jsoup.Jsoup import org.jsoup.Jsoup
import rxhttp.wrapper.param.RxHttp import rxhttp.wrapper.param.RxHttp
import rxhttp.wrapper.param.toByteArray import rxhttp.wrapper.param.toByteArray
import splitties.init.appCtx
import java.io.File import java.io.File
import java.net.URLEncoder import java.net.URLEncoder
import java.util.* import java.util.*
@ -273,7 +273,7 @@ interface JsExtensions {
} }
return@runBlocking x return@runBlocking x
} }
str.isContentScheme() -> Uri.parse(str).readBytes(App.INSTANCE) str.isContentScheme() -> Uri.parse(str).readBytes(appCtx)
str.startsWith("/storage") -> File(str).readBytes() str.startsWith("/storage") -> File(str).readBytes()
else -> base64DecodeToByteArray(str) else -> base64DecodeToByteArray(str)
} }

@ -3,30 +3,30 @@ package io.legado.app.help
import android.content.ComponentName import android.content.ComponentName
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.ui.welcome.* import io.legado.app.ui.welcome.*
import org.jetbrains.anko.toast import io.legado.app.utils.toastOnUi
import splitties.init.appCtx
/** /**
* Created by GKF on 2018/2/27. * Created by GKF on 2018/2/27.
* 更换图标 * 更换图标
*/ */
object LauncherIconHelp { object LauncherIconHelp {
private val packageManager: PackageManager = App.INSTANCE.packageManager private val packageManager: PackageManager = appCtx.packageManager
private val componentNames = arrayListOf( private val componentNames = arrayListOf(
ComponentName(App.INSTANCE, Launcher1::class.java.name), ComponentName(appCtx, Launcher1::class.java.name),
ComponentName(App.INSTANCE, Launcher2::class.java.name), ComponentName(appCtx, Launcher2::class.java.name),
ComponentName(App.INSTANCE, Launcher3::class.java.name), ComponentName(appCtx, Launcher3::class.java.name),
ComponentName(App.INSTANCE, Launcher4::class.java.name), ComponentName(appCtx, Launcher4::class.java.name),
ComponentName(App.INSTANCE, Launcher5::class.java.name), ComponentName(appCtx, Launcher5::class.java.name),
ComponentName(App.INSTANCE, Launcher6::class.java.name) ComponentName(appCtx, Launcher6::class.java.name)
) )
fun changeIcon(icon: String?) { fun changeIcon(icon: String?) {
if (icon.isNullOrEmpty()) return if (icon.isNullOrEmpty()) return
if (Build.VERSION.SDK_INT < 26) { if (Build.VERSION.SDK_INT < 26) {
App.INSTANCE.toast(R.string.change_icon_error) appCtx.toastOnUi(R.string.change_icon_error)
return return
} }
var hasEnabled = false var hasEnabled = false
@ -50,13 +50,13 @@ object LauncherIconHelp {
} }
if (hasEnabled) { if (hasEnabled) {
packageManager.setComponentEnabledSetting( packageManager.setComponentEnabledSetting(
ComponentName(App.INSTANCE, WelcomeActivity::class.java.name), ComponentName(appCtx, WelcomeActivity::class.java.name),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP PackageManager.DONT_KILL_APP
) )
} else { } else {
packageManager.setComponentEnabledSetting( packageManager.setComponentEnabledSetting(
ComponentName(App.INSTANCE, WelcomeActivity::class.java.name), ComponentName(appCtx, WelcomeActivity::class.java.name),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP PackageManager.DONT_KILL_APP
) )

@ -2,19 +2,19 @@ package io.legado.app.help
import android.content.Context import android.content.Context
import androidx.core.content.edit import androidx.core.content.edit
import io.legado.app.App import splitties.init.appCtx
object LocalConfig { object LocalConfig {
private const val versionCodeKey = "versionCode" private const val versionCodeKey = "appVersionCode"
private val localConfig = private val localConfig =
App.INSTANCE.getSharedPreferences("local", Context.MODE_PRIVATE) appCtx.getSharedPreferences("local", Context.MODE_PRIVATE)
var versionCode var versionCode
get() = localConfig.getInt(versionCodeKey, 0) get() = localConfig.getLong(versionCodeKey, 0)
set(value) { set(value) {
localConfig.edit { localConfig.edit {
putInt(versionCodeKey, value) putLong(versionCodeKey, value)
} }
} }

@ -5,12 +5,12 @@ import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import androidx.annotation.Keep import androidx.annotation.Keep
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.utils.* import io.legado.app.utils.*
import splitties.init.appCtx
import java.io.File import java.io.File
/** /**
@ -20,7 +20,7 @@ import java.io.File
object ReadBookConfig { object ReadBookConfig {
const val configFileName = "readConfig.json" const val configFileName = "readConfig.json"
const val shareConfigFileName = "shareReadConfig.json" const val shareConfigFileName = "shareReadConfig.json"
val context get() = App.INSTANCE val context get() = appCtx
val configFilePath = FileUtils.getPath(context.filesDir, configFileName) val configFilePath = FileUtils.getPath(context.filesDir, configFileName)
val shareConfigFilePath = FileUtils.getPath(context.filesDir, shareConfigFileName) val shareConfigFilePath = FileUtils.getPath(context.filesDir, shareConfigFileName)
val configList: ArrayList<Config> = arrayListOf() val configList: ArrayList<Config> = arrayListOf()

@ -1,11 +1,11 @@
package io.legado.app.help package io.legado.app.help
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import splitties.init.appCtx
object ReadTipConfig { object ReadTipConfig {
val tips by lazy { val tips by lazy {
App.INSTANCE.resources.getStringArray(R.array.read_tip).toList() appCtx.resources.getStringArray(R.array.read_tip).toList()
} }
val headerModes by lazy { val headerModes by lazy {
linkedMapOf(0 to "状态栏显示时隐藏", 1 to "显示", 2 to "隐藏") linkedMapOf(0 to "状态栏显示时隐藏", 1 to "显示", 2 to "隐藏")

@ -2,20 +2,21 @@ package io.legado.app.help
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import io.legado.app.App import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.utils.EncoderUtils import io.legado.app.utils.EncoderUtils
import io.legado.app.utils.NetworkUtils import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.splitNotBlank import io.legado.app.utils.splitNotBlank
import org.jetbrains.anko.toast import io.legado.app.utils.toastOnUi
import splitties.init.appCtx
object SourceHelp { object SourceHelp {
private val handler = Handler(Looper.getMainLooper()) private val handler = Handler(Looper.getMainLooper())
private val list18Plus by lazy { private val list18Plus by lazy {
try { try {
return@lazy String(App.INSTANCE.assets.open("18PlusList.txt").readBytes()) return@lazy String(appCtx.assets.open("18PlusList.txt").readBytes())
.splitNotBlank("\n") .splitNotBlank("\n")
} catch (e: Exception) { } catch (e: Exception) {
return@lazy arrayOf<String>() return@lazy arrayOf<String>()
@ -26,10 +27,10 @@ object SourceHelp {
rssSources.forEach { rssSource -> rssSources.forEach { rssSource ->
if (is18Plus(rssSource.sourceUrl)) { if (is18Plus(rssSource.sourceUrl)) {
handler.post { handler.post {
App.INSTANCE.toast("${rssSource.sourceName}是18+网址,禁止导入.") appCtx.toastOnUi("${rssSource.sourceName}是18+网址,禁止导入.")
} }
} else { } else {
App.db.rssSourceDao.insert(rssSource) appDb.rssSourceDao.insert(rssSource)
} }
} }
} }
@ -38,10 +39,10 @@ object SourceHelp {
bookSources.forEach { bookSource -> bookSources.forEach { bookSource ->
if (is18Plus(bookSource.bookSourceUrl)) { if (is18Plus(bookSource.bookSourceUrl)) {
handler.post { handler.post {
App.INSTANCE.toast("${bookSource.bookSourceName}是18+网址,禁止导入.") appCtx.toastOnUi("${bookSource.bookSourceName}是18+网址,禁止导入.")
} }
} else { } else {
App.db.bookSourceDao.insert(bookSource) appDb.bookSourceDao.insert(bookSource)
} }
} }
} }

@ -5,24 +5,42 @@ import android.graphics.Color
import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import androidx.annotation.Keep import androidx.annotation.Keep
import io.legado.app.App import androidx.appcompat.app.AppCompatDelegate
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.constant.Theme import io.legado.app.constant.Theme
import io.legado.app.lib.theme.ThemeStore import io.legado.app.lib.theme.ThemeStore
import io.legado.app.utils.* import io.legado.app.utils.*
import splitties.init.appCtx
import java.io.File import java.io.File
object ThemeConfig { object ThemeConfig {
const val configFileName = "themeConfig.json" const val configFileName = "themeConfig.json"
val configFilePath = FileUtils.getPath(App.INSTANCE.filesDir, configFileName) val configFilePath = FileUtils.getPath(appCtx.filesDir, configFileName)
val configList: ArrayList<Config> by lazy { val configList: ArrayList<Config> by lazy {
val cList = getConfigs() ?: DefaultData.themeConfigs val cList = getConfigs() ?: DefaultData.themeConfigs
ArrayList(cList) ArrayList(cList)
} }
fun applyDayNight(context: Context) {
ReadBookConfig.upBg()
applyTheme(context)
initNightMode()
postEvent(EventBus.RECREATE, "")
}
private fun initNightMode() {
val targetMode =
if (AppConfig.isNightTheme) {
AppCompatDelegate.MODE_NIGHT_YES
} else {
AppCompatDelegate.MODE_NIGHT_NO
}
AppCompatDelegate.setDefaultNightMode(targetMode)
}
fun getBgImage(context: Context): Drawable? { fun getBgImage(context: Context): Drawable? {
val bgPath = when (Theme.getTheme()) { val bgPath = when (Theme.getTheme()) {
Theme.Light -> context.getPrefString(PreferKey.bgImage) Theme.Light -> context.getPrefString(PreferKey.bgImage)
@ -101,8 +119,7 @@ object ThemeConfig {
context.putPrefInt(PreferKey.cBBackground, bBackground) context.putPrefInt(PreferKey.cBBackground, bBackground)
} }
AppConfig.isNightTheme = config.isNightTheme AppConfig.isNightTheme = config.isNightTheme
App.INSTANCE.applyDayNight() applyDayNight(context)
postEvent(EventBus.RECREATE, "")
} }
fun saveDayTheme(context: Context, name: String) { fun saveDayTheme(context: Context, name: String) {

@ -9,19 +9,15 @@ import android.webkit.CookieManager
import android.webkit.WebSettings import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import io.legado.app.App
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import org.apache.commons.text.StringEscapeUtils import org.apache.commons.text.StringEscapeUtils
import splitties.init.appCtx
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
class AjaxWebView { class AjaxWebView {
var callback: Callback? = null var callback: Callback? = null
private var mHandler: AjaxHandler private var mHandler: AjaxHandler = AjaxHandler(this)
init {
mHandler = AjaxHandler(this)
}
class AjaxHandler(private val ajaxWebView: AjaxWebView) : Handler(Looper.getMainLooper()) { class AjaxHandler(private val ajaxWebView: AjaxWebView) : Handler(Looper.getMainLooper()) {
@ -51,7 +47,7 @@ class AjaxWebView {
@SuppressLint("SetJavaScriptEnabled", "JavascriptInterface") @SuppressLint("SetJavaScriptEnabled", "JavascriptInterface")
fun createAjaxWebView(params: AjaxParams, handler: Handler): WebView { fun createAjaxWebView(params: AjaxParams, handler: Handler): WebView {
val webView = WebView(App.INSTANCE) val webView = WebView(appCtx)
val settings = webView.settings val settings = webView.settings
settings.javaScriptEnabled = true settings.javaScriptEnabled = true
settings.domStorageEnabled = true settings.domStorageEnabled = true

@ -3,7 +3,7 @@
package io.legado.app.help.http package io.legado.app.help.http
import android.text.TextUtils import android.text.TextUtils
import io.legado.app.App import io.legado.app.data.appDb
import io.legado.app.data.entities.Cookie import io.legado.app.data.entities.Cookie
import io.legado.app.help.http.api.CookieManager import io.legado.app.help.http.api.CookieManager
import io.legado.app.utils.NetworkUtils import io.legado.app.utils.NetworkUtils
@ -12,7 +12,7 @@ object CookieStore : CookieManager {
override fun setCookie(url: String, cookie: String?) { override fun setCookie(url: String, cookie: String?) {
val cookieBean = Cookie(NetworkUtils.getSubDomain(url), cookie ?: "") val cookieBean = Cookie(NetworkUtils.getSubDomain(url), cookie ?: "")
App.db.cookieDao.insert(cookieBean) appDb.cookieDao.insert(cookieBean)
} }
override fun replaceCookie(url: String, cookie: String) { override fun replaceCookie(url: String, cookie: String) {
@ -31,12 +31,12 @@ object CookieStore : CookieManager {
} }
override fun getCookie(url: String): String { override fun getCookie(url: String): String {
val cookieBean = App.db.cookieDao.get(NetworkUtils.getSubDomain(url)) val cookieBean = appDb.cookieDao.get(NetworkUtils.getSubDomain(url))
return cookieBean?.cookie ?: "" return cookieBean?.cookie ?: ""
} }
override fun removeCookie(url: String) { override fun removeCookie(url: String) {
App.db.cookieDao.delete(NetworkUtils.getSubDomain(url)) appDb.cookieDao.delete(NetworkUtils.getSubDomain(url))
} }
override fun cookieToMap(cookie: String): MutableMap<String, String> { override fun cookieToMap(cookie: String): MutableMap<String, String> {
@ -77,7 +77,7 @@ object CookieStore : CookieManager {
} }
fun clear() { fun clear() {
App.db.cookieDao.deleteOkHttp() appDb.cookieDao.deleteOkHttp()
} }
} }

@ -8,7 +8,7 @@ import android.view.KeyEvent
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import io.legado.app.R import io.legado.app.R
import org.jetbrains.anko.toast import io.legado.app.utils.toastOnUi
class PermissionActivity : AppCompatActivity() { class PermissionActivity : AppCompatActivity() {
@ -32,7 +32,7 @@ class PermissionActivity : AppCompatActivity() {
settingIntent.data = Uri.fromParts("package", packageName, null) settingIntent.data = Uri.fromParts("package", packageName, null)
startActivityForResult(settingIntent, Request.TYPE_REQUEST_SETTING) startActivityForResult(settingIntent, Request.TYPE_REQUEST_SETTING)
} catch (e: Exception) { } catch (e: Exception) {
toast(R.string.tip_cannot_jump_setting_page) toastOnUi(R.string.tip_cannot_jump_setting_page)
finish() finish()
} }

@ -9,7 +9,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import io.legado.app.R import io.legado.app.R
import org.jetbrains.anko.startActivity import io.legado.app.utils.startActivity
import java.util.* import java.util.*
internal class Request : OnRequestPermissionsResultCallback { internal class Request : OnRequestPermissionsResultCallback {
@ -77,20 +77,26 @@ internal class Request : OnRequestPermissionsResultCallback {
if (deniedPermissions == null) { if (deniedPermissions == null) {
onPermissionsGranted(requestCode) onPermissionsGranted(requestCode)
} else { } else {
val rationale = if (rationaleResId != 0) source?.context?.getText(rationaleResId) else rationale val rationale =
if (rationaleResId != 0) source?.context?.getText(rationaleResId) else rationale
if (rationale != null) { if (rationale != null) {
showSettingDialog(rationale) { onPermissionsDenied(requestCode, deniedPermissions) } showSettingDialog(rationale) {
onPermissionsDenied(
requestCode,
deniedPermissions
)
}
} else { } else {
onPermissionsDenied(requestCode, deniedPermissions) onPermissionsDenied(requestCode, deniedPermissions)
} }
} }
} else { } else {
if (deniedPermissions != null) { if (deniedPermissions != null) {
source?.context?.startActivity<PermissionActivity>( source?.context?.startActivity<PermissionActivity> {
PermissionActivity.KEY_INPUT_REQUEST_TYPE to TYPE_REQUEST_PERMISSION, putExtra(PermissionActivity.KEY_INPUT_REQUEST_TYPE, TYPE_REQUEST_PERMISSION)
PermissionActivity.KEY_INPUT_PERMISSIONS_CODE to requestCode, putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS_CODE, requestCode)
PermissionActivity.KEY_INPUT_PERMISSIONS to deniedPermissions putExtra(PermissionActivity.KEY_INPUT_PERMISSIONS, deniedPermissions)
) }
} else { } else {
onPermissionsGranted(requestCode) onPermissionsGranted(requestCode)
} }
@ -132,9 +138,12 @@ internal class Request : OnRequestPermissionsResultCallback {
.setTitle(R.string.dialog_title) .setTitle(R.string.dialog_title)
.setMessage(rationale) .setMessage(rationale)
.setPositiveButton(R.string.dialog_setting) { _, _ -> .setPositiveButton(R.string.dialog_setting) { _, _ ->
it.startActivity<PermissionActivity>( it.startActivity<PermissionActivity> {
PermissionActivity.KEY_INPUT_REQUEST_TYPE to TYPE_REQUEST_SETTING putExtra(
) PermissionActivity.KEY_INPUT_REQUEST_TYPE,
TYPE_REQUEST_SETTING
)
}
} }
.setNegativeButton(R.string.dialog_cancel) { _, _ -> cancel() } .setNegativeButton(R.string.dialog_cancel) { _, _ -> cancel() }
.show() .show()
@ -160,10 +169,15 @@ internal class Request : OnRequestPermissionsResultCallback {
RequestPlugins.sResultCallback?.onPermissionsDenied(requestCode, deniedPermissions) RequestPlugins.sResultCallback?.onPermissionsDenied(requestCode, deniedPermissions)
} }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String>, grantResults: IntArray) { override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
val deniedPermissions = getDeniedPermissions(permissions) val deniedPermissions = getDeniedPermissions(permissions)
if (deniedPermissions != null) { if (deniedPermissions != null) {
val rationale = if (rationaleResId != 0) source?.context?.getText(rationaleResId) else rationale val rationale =
if (rationaleResId != 0) source?.context?.getText(rationaleResId) else rationale
if (rationale != null) { if (rationale != null) {
showSettingDialog(rationale) { onPermissionsDenied(requestCode, deniedPermissions) } showSettingDialog(rationale) { onPermissionsDenied(requestCode, deniedPermissions) }
} else { } else {

@ -3,8 +3,8 @@ package io.legado.app.help.storage
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import io.legado.app.App
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.help.DefaultData import io.legado.app.help.DefaultData
import io.legado.app.help.ReadBookConfig import io.legado.app.help.ReadBookConfig
import io.legado.app.help.ThemeConfig import io.legado.app.help.ThemeConfig
@ -12,7 +12,7 @@ import io.legado.app.help.coroutine.Coroutine
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jetbrains.anko.defaultSharedPreferences import splitties.init.appCtx
import java.io.File import java.io.File
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit
object Backup { object Backup {
val backupPath: String by lazy { val backupPath: String by lazy {
FileUtils.getFile(App.INSTANCE.filesDir, "backup").absolutePath FileUtils.getFile(appCtx.filesDir, "backup").absolutePath
} }
val backupFileNames by lazy { val backupFileNames by lazy {
@ -57,32 +57,33 @@ object Backup {
context.putPrefLong(PreferKey.lastBackup, System.currentTimeMillis()) context.putPrefLong(PreferKey.lastBackup, System.currentTimeMillis())
withContext(IO) { withContext(IO) {
FileUtils.deleteFile(backupPath) FileUtils.deleteFile(backupPath)
writeListToJson(App.db.bookDao.all, "bookshelf.json", backupPath) writeListToJson(appDb.bookDao.all, "bookshelf.json", backupPath)
writeListToJson(App.db.bookmarkDao.all, "bookmark.json", backupPath) writeListToJson(appDb.bookmarkDao.all, "bookmark.json", backupPath)
writeListToJson(App.db.bookGroupDao.all, "bookGroup.json", backupPath) writeListToJson(appDb.bookGroupDao.all, "bookGroup.json", backupPath)
writeListToJson(App.db.bookSourceDao.all, "bookSource.json", backupPath) writeListToJson(appDb.bookSourceDao.all, "bookSource.json", backupPath)
writeListToJson(App.db.rssSourceDao.all, "rssSources.json", backupPath) writeListToJson(appDb.rssSourceDao.all, "rssSources.json", backupPath)
writeListToJson(App.db.rssStarDao.all, "rssStar.json", backupPath) writeListToJson(appDb.rssStarDao.all, "rssStar.json", backupPath)
writeListToJson(App.db.replaceRuleDao.all, "replaceRule.json", backupPath) writeListToJson(appDb.replaceRuleDao.all, "replaceRule.json", backupPath)
writeListToJson(App.db.readRecordDao.all, "readRecord.json", backupPath) writeListToJson(appDb.readRecordDao.all, "readRecord.json", backupPath)
writeListToJson(App.db.searchKeywordDao.all, "searchHistory.json", backupPath) writeListToJson(appDb.searchKeywordDao.all, "searchHistory.json", backupPath)
writeListToJson(App.db.ruleSubDao.all, "sourceSub.json", backupPath) writeListToJson(appDb.ruleSubDao.all, "sourceSub.json", backupPath)
writeListToJson(App.db.txtTocRule.all, DefaultData.txtTocRuleFileName, backupPath) writeListToJson(appDb.txtTocRule.all, DefaultData.txtTocRuleFileName, backupPath)
writeListToJson(App.db.httpTTSDao.all, DefaultData.httpTtsFileName, backupPath) writeListToJson(appDb.httpTTSDao.all, DefaultData.httpTtsFileName, backupPath)
GSON.toJson(ReadBookConfig.configList).let { GSON.toJson(ReadBookConfig.configList).let {
FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.configFileName) FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.configFileName)
.writeText(it) .writeText(it)
} }
GSON.toJson(ReadBookConfig.shareConfig).let { GSON.toJson(ReadBookConfig.shareConfig).let {
FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.shareConfigFileName) FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.shareConfigFileName)
.writeText(it)
} }
GSON.toJson(ThemeConfig.configList).let { GSON.toJson(ThemeConfig.configList).let {
FileUtils.createFileIfNotExist(backupPath + File.separator + ThemeConfig.configFileName) FileUtils.createFileIfNotExist(backupPath + File.separator + ThemeConfig.configFileName)
.writeText(it) .writeText(it)
} }
Preferences.getSharedPreferences(App.INSTANCE, backupPath, "config")?.let { sp -> Preferences.getSharedPreferences(appCtx, backupPath, "config")?.let { sp ->
val edit = sp.edit() val edit = sp.edit()
App.INSTANCE.defaultSharedPreferences.all.map { appCtx.defaultSharedPreferences.all.map {
when (val value = it.value) { when (val value = it.value) {
is Int -> edit.putInt(it.key, value) is Int -> edit.putInt(it.key, value)
is Boolean -> edit.putBoolean(it.key, value) is Boolean -> edit.putBoolean(it.key, value)

@ -3,7 +3,6 @@ package io.legado.app.help.storage
import android.content.Context import android.content.Context
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
@ -16,7 +15,7 @@ import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jetbrains.anko.toast import splitties.init.appCtx
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -28,20 +27,20 @@ object BookWebDav {
private val rootWebDavUrl: String private val rootWebDavUrl: String
get() { get() {
var url = App.INSTANCE.getPrefString(PreferKey.webDavUrl) var url = appCtx.getPrefString(PreferKey.webDavUrl)
if (url.isNullOrEmpty()) { if (url.isNullOrEmpty()) {
url = defaultWebDavUrl url = defaultWebDavUrl
} }
if (!url.endsWith("/")) url = "${url}/" if (!url.endsWith("/")) url = "${url}/"
if (App.INSTANCE.getPrefBoolean(PreferKey.webDavCreateDir, true)) { if (appCtx.getPrefBoolean(PreferKey.webDavCreateDir, true)) {
url = "${url}legado/" url = "${url}legado/"
} }
return url return url
} }
suspend fun initWebDav(): Boolean { suspend fun initWebDav(): Boolean {
val account = App.INSTANCE.getPrefString(PreferKey.webDavAccount) val account = appCtx.getPrefString(PreferKey.webDavAccount)
val password = App.INSTANCE.getPrefString(PreferKey.webDavPassword) val password = appCtx.getPrefString(PreferKey.webDavPassword)
if (!account.isNullOrBlank() && !password.isNullOrBlank()) { if (!account.isNullOrBlank() && !password.isNullOrBlank()) {
HttpAuth.auth = HttpAuth.Auth(account, password) HttpAuth.auth = HttpAuth.Auth(account, password)
WebDav(rootWebDavUrl).makeAsDir() WebDav(rootWebDavUrl).makeAsDir()
@ -80,7 +79,7 @@ object BookWebDav {
Coroutine.async { Coroutine.async {
restoreWebDav(names[index]) restoreWebDav(names[index])
}.onError { }.onError {
App.INSTANCE.toast("WebDavError:${it.localizedMessage}") appCtx.toastOnUi("WebDavError:${it.localizedMessage}")
} }
} }
} }
@ -118,7 +117,7 @@ object BookWebDav {
} }
} catch (e: Exception) { } catch (e: Exception) {
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
App.INSTANCE.toast("WebDav\n${e.localizedMessage}") appCtx.toastOnUi("WebDav\n${e.localizedMessage}")
} }
} }
} }
@ -139,7 +138,7 @@ object BookWebDav {
} }
} catch (e: Exception) { } catch (e: Exception) {
Handler(Looper.getMainLooper()).post { Handler(Looper.getMainLooper()).post {
App.INSTANCE.toast("WebDav导出\n${e.localizedMessage}") appCtx.toastOnUi("WebDav导出\n${e.localizedMessage}")
} }
} }
} }

@ -3,12 +3,12 @@ package io.legado.app.help.storage
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import io.legado.app.App import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.utils.DocumentUtils import io.legado.app.utils.DocumentUtils
import io.legado.app.utils.FileUtils import io.legado.app.utils.FileUtils
import io.legado.app.utils.isContentScheme import io.legado.app.utils.isContentScheme
import org.jetbrains.anko.toast import io.legado.app.utils.toastOnUi
import java.io.File import java.io.File
object ImportOldData { object ImportOldData {
@ -21,28 +21,28 @@ object ImportOldData {
kotlin.runCatching { kotlin.runCatching {
DocumentUtils.readText(context, doc.uri)?.let { json -> DocumentUtils.readText(context, doc.uri)?.let { json ->
val importCount = importOldBookshelf(json) val importCount = importOldBookshelf(json)
context.toast("成功导入书籍${importCount}") context.toastOnUi("成功导入书籍${importCount}")
} }
}.onFailure { }.onFailure {
context.toast("导入书籍失败\n${it.localizedMessage}") context.toastOnUi("导入书籍失败\n${it.localizedMessage}")
} }
"myBookSource.json" -> "myBookSource.json" ->
kotlin.runCatching { kotlin.runCatching {
DocumentUtils.readText(context, doc.uri)?.let { json -> DocumentUtils.readText(context, doc.uri)?.let { json ->
val importCount = importOldSource(json) val importCount = importOldSource(json)
context.toast("成功导入书源${importCount}") context.toastOnUi("成功导入书源${importCount}")
} }
}.onFailure { }.onFailure {
context.toast("导入源失败\n${it.localizedMessage}") context.toastOnUi("导入源失败\n${it.localizedMessage}")
} }
"myBookReplaceRule.json" -> "myBookReplaceRule.json" ->
kotlin.runCatching { kotlin.runCatching {
DocumentUtils.readText(context, doc.uri)?.let { json -> DocumentUtils.readText(context, doc.uri)?.let { json ->
val importCount = importOldReplaceRule(json) val importCount = importOldReplaceRule(json)
context.toast("成功导入替换规则${importCount}") context.toastOnUi("成功导入替换规则${importCount}")
} }
}.onFailure { }.onFailure {
context.toast("导入替换规则失败\n${it.localizedMessage}") context.toastOnUi("导入替换规则失败\n${it.localizedMessage}")
} }
} }
} }
@ -54,9 +54,9 @@ object ImportOldData {
FileUtils.createFileIfNotExist(file, "myBookShelf.json") FileUtils.createFileIfNotExist(file, "myBookShelf.json")
val json = shelfFile.readText() val json = shelfFile.readText()
val importCount = importOldBookshelf(json) val importCount = importOldBookshelf(json)
context.toast("成功导入书籍${importCount}") context.toastOnUi("成功导入书籍${importCount}")
}.onFailure { }.onFailure {
context.toast("导入书籍失败\n${it.localizedMessage}") context.toastOnUi("导入书籍失败\n${it.localizedMessage}")
} }
kotlin.runCatching {// Book source kotlin.runCatching {// Book source
@ -64,9 +64,9 @@ object ImportOldData {
FileUtils.getFile(file, "myBookSource.json") FileUtils.getFile(file, "myBookSource.json")
val json = sourceFile.readText() val json = sourceFile.readText()
val importCount = importOldSource(json) val importCount = importOldSource(json)
context.toast("成功导入书源${importCount}") context.toastOnUi("成功导入书源${importCount}")
}.onFailure { }.onFailure {
context.toast("导入源失败\n${it.localizedMessage}") context.toastOnUi("导入源失败\n${it.localizedMessage}")
} }
kotlin.runCatching {// Replace rules kotlin.runCatching {// Replace rules
@ -74,12 +74,12 @@ object ImportOldData {
if (ruleFile.exists()) { if (ruleFile.exists()) {
val json = ruleFile.readText() val json = ruleFile.readText()
val importCount = importOldReplaceRule(json) val importCount = importOldReplaceRule(json)
context.toast("成功导入替换规则${importCount}") context.toastOnUi("成功导入替换规则${importCount}")
} else { } else {
context.toast("未找到替换规则") context.toastOnUi("未找到替换规则")
} }
}.onFailure { }.onFailure {
context.toast("导入替换规则失败\n${it.localizedMessage}") context.toastOnUi("导入替换规则失败\n${it.localizedMessage}")
} }
} }
} }
@ -87,7 +87,7 @@ object ImportOldData {
private fun importOldBookshelf(json: String): Int { private fun importOldBookshelf(json: String): Int {
val books = OldBook.toNewBook(json) val books = OldBook.toNewBook(json)
App.db.bookDao.insert(*books.toTypedArray()) appDb.bookDao.insert(*books.toTypedArray())
return books.size return books.size
} }
@ -100,13 +100,13 @@ object ImportOldData {
bookSources.add(it) bookSources.add(it)
} }
} }
App.db.bookSourceDao.insert(*bookSources.toTypedArray()) appDb.bookSourceDao.insert(*bookSources.toTypedArray())
return bookSources.size return bookSources.size
} }
private fun importOldReplaceRule(json: String): Int { private fun importOldReplaceRule(json: String): Int {
val rules = OldReplace.jsonToReplaceRules(json) val rules = OldReplace.jsonToReplaceRules(json)
App.db.replaceRuleDao.insert(*rules.toTypedArray()) appDb.replaceRuleDao.insert(*rules.toTypedArray())
return rules.size return rules.size
} }
} }

@ -1,8 +1,8 @@
package io.legado.app.help.storage package io.legado.app.help.storage
import android.util.Log import android.util.Log
import io.legado.app.App
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.utils.readBool import io.legado.app.utils.readBool
import io.legado.app.utils.readInt import io.legado.app.utils.readInt
@ -14,7 +14,7 @@ object OldBook {
fun toNewBook(json: String): List<Book> { fun toNewBook(json: String): List<Book> {
val books = mutableListOf<Book>() val books = mutableListOf<Book>()
val items: List<Map<String, Any>> = Restore.jsonPath.parse(json).read("$") val items: List<Map<String, Any>> = Restore.jsonPath.parse(json).read("$")
val existingBooks = App.db.bookDao.allBookUrls.toSet() val existingBooks = appDb.bookDao.allBookUrls.toSet()
for (item in items) { for (item in items) {
val jsonItem = Restore.jsonPath.parse(item) val jsonItem = Restore.jsonPath.parse(item)
val book = Book() val book = Book()

@ -7,11 +7,12 @@ import com.jayway.jsonpath.Configuration
import com.jayway.jsonpath.JsonPath import com.jayway.jsonpath.JsonPath
import com.jayway.jsonpath.Option import com.jayway.jsonpath.Option
import com.jayway.jsonpath.ParseContext import com.jayway.jsonpath.ParseContext
import io.legado.app.App
import io.legado.app.BuildConfig import io.legado.app.BuildConfig
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.constant.androidId
import io.legado.app.data.appDb
import io.legado.app.data.entities.* import io.legado.app.data.entities.*
import io.legado.app.help.DefaultData import io.legado.app.help.DefaultData
import io.legado.app.help.LauncherIconHelp import io.legado.app.help.LauncherIconHelp
@ -23,12 +24,11 @@ import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jetbrains.anko.defaultSharedPreferences import splitties.init.appCtx
import org.jetbrains.anko.toast
import java.io.File import java.io.File
object Restore { object Restore {
private val ignoreConfigPath = FileUtils.getPath(App.INSTANCE.filesDir, "restoreIgnore.json") private val ignoreConfigPath = FileUtils.getPath(appCtx.filesDir, "restoreIgnore.json")
val ignoreConfig: HashMap<String, Boolean> by lazy { val ignoreConfig: HashMap<String, Boolean> by lazy {
val file = FileUtils.createFileIfNotExist(ignoreConfigPath) val file = FileUtils.createFileIfNotExist(ignoreConfigPath)
val json = file.readText() val json = file.readText()
@ -46,11 +46,11 @@ object Restore {
//忽略标题 //忽略标题
val ignoreTitle = arrayOf( val ignoreTitle = arrayOf(
App.INSTANCE.getString(R.string.read_config), appCtx.getString(R.string.read_config),
App.INSTANCE.getString(R.string.theme_mode), appCtx.getString(R.string.theme_mode),
App.INSTANCE.getString(R.string.bookshelf_layout), appCtx.getString(R.string.bookshelf_layout),
App.INSTANCE.getString(R.string.show_rss), appCtx.getString(R.string.show_rss),
App.INSTANCE.getString(R.string.thread_count) appCtx.getString(R.string.thread_count)
) )
//默认忽略keys //默认忽略keys
@ -111,48 +111,48 @@ object Restore {
suspend fun restoreDatabase(path: String = Backup.backupPath) { suspend fun restoreDatabase(path: String = Backup.backupPath) {
withContext(IO) { withContext(IO) {
fileToListT<Book>(path, "bookshelf.json")?.let { fileToListT<Book>(path, "bookshelf.json")?.let {
App.db.bookDao.insert(*it.toTypedArray()) appDb.bookDao.insert(*it.toTypedArray())
} }
fileToListT<Bookmark>(path, "bookmark.json")?.let { fileToListT<Bookmark>(path, "bookmark.json")?.let {
App.db.bookmarkDao.insert(*it.toTypedArray()) appDb.bookmarkDao.insert(*it.toTypedArray())
} }
fileToListT<BookGroup>(path, "bookGroup.json")?.let { fileToListT<BookGroup>(path, "bookGroup.json")?.let {
App.db.bookGroupDao.insert(*it.toTypedArray()) appDb.bookGroupDao.insert(*it.toTypedArray())
} }
fileToListT<BookSource>(path, "bookSource.json")?.let { fileToListT<BookSource>(path, "bookSource.json")?.let {
App.db.bookSourceDao.insert(*it.toTypedArray()) appDb.bookSourceDao.insert(*it.toTypedArray())
} }
fileToListT<RssSource>(path, "rssSources.json")?.let { fileToListT<RssSource>(path, "rssSources.json")?.let {
App.db.rssSourceDao.insert(*it.toTypedArray()) appDb.rssSourceDao.insert(*it.toTypedArray())
} }
fileToListT<RssStar>(path, "rssStar.json")?.let { fileToListT<RssStar>(path, "rssStar.json")?.let {
App.db.rssStarDao.insert(*it.toTypedArray()) appDb.rssStarDao.insert(*it.toTypedArray())
} }
fileToListT<ReplaceRule>(path, "replaceRule.json")?.let { fileToListT<ReplaceRule>(path, "replaceRule.json")?.let {
App.db.replaceRuleDao.insert(*it.toTypedArray()) appDb.replaceRuleDao.insert(*it.toTypedArray())
} }
fileToListT<SearchKeyword>(path, "searchHistory.json")?.let { fileToListT<SearchKeyword>(path, "searchHistory.json")?.let {
App.db.searchKeywordDao.insert(*it.toTypedArray()) appDb.searchKeywordDao.insert(*it.toTypedArray())
} }
fileToListT<RuleSub>(path, "sourceSub.json")?.let { fileToListT<RuleSub>(path, "sourceSub.json")?.let {
App.db.ruleSubDao.insert(*it.toTypedArray()) appDb.ruleSubDao.insert(*it.toTypedArray())
} }
fileToListT<TxtTocRule>(path, DefaultData.txtTocRuleFileName)?.let { fileToListT<TxtTocRule>(path, DefaultData.txtTocRuleFileName)?.let {
App.db.txtTocRule.insert(*it.toTypedArray()) appDb.txtTocRule.insert(*it.toTypedArray())
} }
fileToListT<HttpTTS>(path, DefaultData.httpTtsFileName)?.let { fileToListT<HttpTTS>(path, DefaultData.httpTtsFileName)?.let {
App.db.httpTTSDao.insert(*it.toTypedArray()) appDb.httpTTSDao.insert(*it.toTypedArray())
} }
fileToListT<ReadRecord>(path, "readRecord.json")?.let { fileToListT<ReadRecord>(path, "readRecord.json")?.let {
it.forEach { readRecord -> it.forEach { readRecord ->
//判断是不是本机记录 //判断是不是本机记录
if (readRecord.androidId != App.androidId) { if (readRecord.androidId != androidId) {
App.db.readRecordDao.insert(readRecord) appDb.readRecordDao.insert(readRecord)
} else { } else {
val time = App.db.readRecordDao val time = appDb.readRecordDao
.getReadTime(readRecord.androidId, readRecord.bookName) .getReadTime(readRecord.androidId, readRecord.bookName)
if (time == null || time < readRecord.readTime) { if (time == null || time < readRecord.readTime) {
App.db.readRecordDao.insert(readRecord) appDb.readRecordDao.insert(readRecord)
} }
} }
} }
@ -197,8 +197,8 @@ object Restore {
e.printStackTrace() e.printStackTrace()
} }
} }
Preferences.getSharedPreferences(App.INSTANCE, path, "config")?.all?.let { map -> Preferences.getSharedPreferences(appCtx, path, "config")?.all?.let { map ->
val edit = App.INSTANCE.defaultSharedPreferences.edit() val edit = appCtx.defaultSharedPreferences.edit()
map.forEach { map.forEach {
if (keyIsNotIgnore(it.key)) { if (keyIsNotIgnore(it.key)) {
when (val value = it.value) { when (val value = it.value) {
@ -214,22 +214,22 @@ object Restore {
edit.apply() edit.apply()
} }
ReadBookConfig.apply { ReadBookConfig.apply {
styleSelect = App.INSTANCE.getPrefInt(PreferKey.readStyleSelect) styleSelect = appCtx.getPrefInt(PreferKey.readStyleSelect)
shareLayout = App.INSTANCE.getPrefBoolean(PreferKey.shareLayout) shareLayout = appCtx.getPrefBoolean(PreferKey.shareLayout)
hideStatusBar = App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar) hideStatusBar = appCtx.getPrefBoolean(PreferKey.hideStatusBar)
hideNavigationBar = App.INSTANCE.getPrefBoolean(PreferKey.hideNavigationBar) hideNavigationBar = appCtx.getPrefBoolean(PreferKey.hideNavigationBar)
autoReadSpeed = App.INSTANCE.getPrefInt(PreferKey.autoReadSpeed, 46) autoReadSpeed = appCtx.getPrefInt(PreferKey.autoReadSpeed, 46)
} }
ChapterProvider.upStyle() ChapterProvider.upStyle()
ReadBook.loadContent(resetPageOffset = false) ReadBook.loadContent(resetPageOffset = false)
} }
withContext(Main) { withContext(Main) {
App.INSTANCE.toast(R.string.restore_success) appCtx.toastOnUi(R.string.restore_success)
if (!BuildConfig.DEBUG) { if (!BuildConfig.DEBUG) {
LauncherIconHelp.changeIcon(App.INSTANCE.getPrefString(PreferKey.launcherIcon)) LauncherIconHelp.changeIcon(appCtx.getPrefString(PreferKey.launcherIcon))
} }
LanguageUtils.setConfiguration(App.INSTANCE) LanguageUtils.setConfiguration(appCtx)
App.INSTANCE.applyDayNight() ThemeConfig.applyDayNight(appCtx)
postEvent(EventBus.SHOW_RSS, "") postEvent(EventBus.SHOW_RSS, "")
} }
} }

@ -11,40 +11,28 @@ import android.view.View
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.StringRes import androidx.annotation.StringRes
import io.legado.app.R import io.legado.app.R
import org.jetbrains.anko.internals.AnkoInternals.NO_GETTER
import kotlin.DeprecationLevel.ERROR
@SuppressLint("SupportAnnotationUsage") @SuppressLint("SupportAnnotationUsage")
interface AlertBuilder<out D : DialogInterface> { interface AlertBuilder<out D : DialogInterface> {
val ctx: Context val ctx: Context
var title: CharSequence fun setTitle(title: CharSequence)
@Deprecated(NO_GETTER, level = ERROR) get
var titleResource: Int fun setTitle(titleResource: Int)
@Deprecated(NO_GETTER, level = ERROR) get
var message: CharSequence fun setMessage(message: CharSequence)
@Deprecated(NO_GETTER, level = ERROR) get
var messageResource: Int fun setMessage(messageResource: Int)
@Deprecated(NO_GETTER, level = ERROR) get
var icon: Drawable fun setIcon(icon: Drawable)
@Deprecated(NO_GETTER, level = ERROR) get
@setparam:DrawableRes fun setIcon(@DrawableRes iconResource: Int)
var iconResource: Int
@Deprecated(NO_GETTER, level = ERROR) get
var customTitle: View fun setCustomTitle(customTitle: View)
@Deprecated(NO_GETTER, level = ERROR) get
var customView: View fun setCustomView(customView: View)
@Deprecated(NO_GETTER, level = ERROR) get
var isCancelable: Boolean fun setCancelable(isCancelable: Boolean)
@Deprecated(NO_GETTER, level = ERROR) get
fun positiveButton(buttonText: String, onClicked: ((dialog: DialogInterface) -> Unit)? = null) fun positiveButton(buttonText: String, onClicked: ((dialog: DialogInterface) -> Unit)? = null)
fun positiveButton( fun positiveButton(
@ -97,11 +85,11 @@ interface AlertBuilder<out D : DialogInterface> {
fun customTitle(view: () -> View) { fun customTitle(view: () -> View) {
customTitle = view() setCustomTitle(view())
} }
fun customView(view: () -> View) { fun customView(view: () -> View) {
customView = view() setCustomView(view())
} }
fun okButton(handler: ((dialog: DialogInterface) -> Unit)? = null) = fun okButton(handler: ((dialog: DialogInterface) -> Unit)? = null) =

@ -7,48 +7,45 @@ import android.view.KeyEvent
import android.view.View import android.view.View
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import io.legado.app.utils.applyTint import io.legado.app.utils.applyTint
import org.jetbrains.anko.internals.AnkoInternals
import org.jetbrains.anko.internals.AnkoInternals.NO_GETTER
import kotlin.DeprecationLevel.ERROR
internal class AndroidAlertBuilder(override val ctx: Context) : AlertBuilder<AlertDialog> { internal class AndroidAlertBuilder(override val ctx: Context) : AlertBuilder<AlertDialog> {
private val builder = AlertDialog.Builder(ctx) private val builder = AlertDialog.Builder(ctx)
override var title: CharSequence override fun setTitle(title: CharSequence) {
@Deprecated(NO_GETTER, level = ERROR) get() = AnkoInternals.noGetter() builder.setTitle(title)
set(value) { builder.setTitle(value) } }
override var titleResource: Int override fun setTitle(titleResource: Int) {
@Deprecated(NO_GETTER, level = ERROR) get() = AnkoInternals.noGetter() builder.setTitle(titleResource)
set(value) { builder.setTitle(value) } }
override var message: CharSequence override fun setMessage(message: CharSequence) {
@Deprecated(NO_GETTER, level = ERROR) get() = AnkoInternals.noGetter() builder.setMessage(message)
set(value) { builder.setMessage(value) } }
override var messageResource: Int override fun setMessage(messageResource: Int) {
@Deprecated(NO_GETTER, level = ERROR) get() = AnkoInternals.noGetter() builder.setMessage(messageResource)
set(value) { builder.setMessage(value) } }
override var icon: Drawable override fun setIcon(icon: Drawable) {
@Deprecated(NO_GETTER, level = ERROR) get() = AnkoInternals.noGetter() builder.setIcon(icon)
set(value) { builder.setIcon(value) } }
override var iconResource: Int override fun setIcon(iconResource: Int) {
@Deprecated(NO_GETTER, level = ERROR) get() = AnkoInternals.noGetter() builder.setIcon(iconResource)
set(value) { builder.setIcon(value) } }
override var customTitle: View override fun setCustomTitle(customTitle: View) {
@Deprecated(NO_GETTER, level = ERROR) get() = AnkoInternals.noGetter() builder.setCustomTitle(customTitle)
set(value) { builder.setCustomTitle(value) } }
override var customView: View override fun setCustomView(customView: View) {
@Deprecated(NO_GETTER, level = ERROR) get() = AnkoInternals.noGetter() builder.setView(customView)
set(value) { builder.setView(value) } }
override var isCancelable: Boolean override fun setCancelable(isCancelable: Boolean) {
@Deprecated(NO_GETTER, level = ERROR) get() = AnkoInternals.noGetter() builder.setCancelable(isCancelable)
set(value) { builder.setCancelable(value) } }
override fun onCancelled(handler: (DialogInterface) -> Unit) { override fun onCancelled(handler: (DialogInterface) -> Unit) {
builder.setOnCancelListener(handler) builder.setOnCancelListener(handler)
@ -58,7 +55,10 @@ internal class AndroidAlertBuilder(override val ctx: Context) : AlertBuilder<Ale
builder.setOnKeyListener(handler) builder.setOnKeyListener(handler)
} }
override fun positiveButton(buttonText: String, onClicked: ((dialog: DialogInterface) -> Unit)?) { override fun positiveButton(
buttonText: String,
onClicked: ((dialog: DialogInterface) -> Unit)?
) {
builder.setPositiveButton(buttonText) { dialog, _ -> onClicked?.invoke(dialog) } builder.setPositiveButton(buttonText) { dialog, _ -> onClicked?.invoke(dialog) }
} }

@ -7,7 +7,6 @@ import android.content.Context
import android.content.DialogInterface import android.content.DialogInterface
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import org.jetbrains.anko.AnkoContext
inline fun Fragment.alert( inline fun Fragment.alert(
title: CharSequence? = null, title: CharSequence? = null,
@ -22,10 +21,10 @@ fun Context.alert(
): AlertBuilder<AlertDialog> { ): AlertBuilder<AlertDialog> {
return AndroidAlertBuilder(this).apply { return AndroidAlertBuilder(this).apply {
if (title != null) { if (title != null) {
this.title = title this.setTitle(title)
} }
if (message != null) { if (message != null) {
this.message = message this.setMessage(message)
} }
if (init != null) init() if (init != null) init()
} }
@ -44,17 +43,16 @@ fun Context.alert(
): AlertBuilder<AlertDialog> { ): AlertBuilder<AlertDialog> {
return AndroidAlertBuilder(this).apply { return AndroidAlertBuilder(this).apply {
if (titleResource != null) { if (titleResource != null) {
this.titleResource = titleResource this.setTitle(titleResource)
} }
if (messageResource != null) { if (messageResource != null) {
this.messageResource = messageResource this.setMessage(messageResource)
} }
if (init != null) init() if (init != null) init()
} }
} }
inline fun AnkoContext<*>.alert(noinline init: AlertBuilder<DialogInterface>.() -> Unit) = ctx.alert(init)
inline fun Fragment.alert(noinline init: AlertBuilder<DialogInterface>.() -> Unit) = requireContext().alert(init) inline fun Fragment.alert(noinline init: AlertBuilder<DialogInterface>.() -> Unit) = requireContext().alert(init)
fun Context.alert(init: AlertBuilder<AlertDialog>.() -> Unit): AlertBuilder<AlertDialog> = fun Context.alert(init: AlertBuilder<AlertDialog>.() -> Unit): AlertBuilder<AlertDialog> =

@ -35,7 +35,7 @@ fun Context.selector(
) { ) {
with(AndroidAlertBuilder(this)) { with(AndroidAlertBuilder(this)) {
if (title != null) { if (title != null) {
this.title = title this.setTitle(title)
} }
items(items, onClick) items(items, onClick)
show() show()
@ -49,7 +49,7 @@ fun Context.selector(
) { ) {
with(AndroidAlertBuilder(this)) { with(AndroidAlertBuilder(this)) {
if (titleSource != null) { if (titleSource != null) {
this.title = getString(titleSource) this.setTitle(titleSource)
} }
items(items, onClick) items(items, onClick)
show() show()

@ -23,10 +23,10 @@ fun <D : DialogInterface> Context.alert(
): AlertBuilder<D> { ): AlertBuilder<D> {
return factory(this).apply { return factory(this).apply {
if (title != null) { if (title != null) {
this.title = title this.setTitle(title)
} }
if (message != null) { if (message != null) {
this.message = message this.setMessage(message)
} }
if (init != null) init() if (init != null) init()
} }
@ -47,10 +47,10 @@ fun <D : DialogInterface> Context.alert(
): AlertBuilder<D> { ): AlertBuilder<D> {
return factory(this).apply { return factory(this).apply {
if (titleResource != null) { if (titleResource != null) {
this.titleResource = titleResource this.setTitle(titleResource)
} }
if (messageResource != null) { if (messageResource != null) {
this.messageResource = messageResource this.setMessage(messageResource)
} }
if (init != null) init() if (init != null) init()
} }

@ -21,7 +21,7 @@ fun <D : DialogInterface> Context.selector(
) { ) {
with(factory(this)) { with(factory(this)) {
if (title != null) { if (title != null) {
this.title = title this.setTitle(title)
} }
items(items, onClick) items(items, onClick)
show() show()

@ -18,14 +18,12 @@ import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager import androidx.viewpager.widget.ViewPager
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.utils.ColorUtils import io.legado.app.utils.ColorUtils
import io.legado.app.utils.dp import io.legado.app.utils.dp
import io.legado.app.utils.getCompatColor import io.legado.app.utils.getCompatColor
import org.jetbrains.anko.backgroundColor import splitties.init.appCtx
/** /**
* @author Karim Abou Zeid (kabouzeid) * @author Karim Abou Zeid (kabouzeid)
@ -272,7 +270,7 @@ object ATH {
fun applyBackgroundTint(view: View?) { fun applyBackgroundTint(view: View?) {
view?.apply { view?.apply {
if (background == null) { if (background == null) {
backgroundColor = context.backgroundColor setBackgroundColor(context.backgroundColor)
} else { } else {
setBackgroundTint(this, context.backgroundColor) setBackgroundTint(this, context.backgroundColor)
} }
@ -290,7 +288,7 @@ object ATH {
fun getDialogBackground(): GradientDrawable { fun getDialogBackground(): GradientDrawable {
val background = GradientDrawable() val background = GradientDrawable()
background.cornerRadius = 3F.dp background.cornerRadius = 3F.dp
background.setColor(App.INSTANCE.backgroundColor) background.setColor(appCtx.backgroundColor)
return background return background
} }

@ -9,9 +9,9 @@ import androidx.annotation.CheckResult
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
import androidx.annotation.ColorRes import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.utils.ColorUtils import io.legado.app.utils.ColorUtils
import splitties.init.appCtx
/** /**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid) * @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
@ -200,7 +200,7 @@ private constructor(private val mContext: Context) : ThemeStoreInterface {
@CheckResult @CheckResult
@ColorInt @ColorInt
fun primaryColor(context: Context = App.INSTANCE): Int { fun primaryColor(context: Context = appCtx): Int {
return prefs(context).getInt( return prefs(context).getInt(
ThemeStorePrefKeys.KEY_PRIMARY_COLOR, ThemeStorePrefKeys.KEY_PRIMARY_COLOR,
ATHUtils.resolveColor(context, R.attr.colorPrimary, Color.parseColor("#455A64")) ATHUtils.resolveColor(context, R.attr.colorPrimary, Color.parseColor("#455A64"))
@ -218,7 +218,7 @@ private constructor(private val mContext: Context) : ThemeStoreInterface {
@CheckResult @CheckResult
@ColorInt @ColorInt
fun accentColor(context: Context = App.INSTANCE): Int { fun accentColor(context: Context = appCtx): Int {
return prefs(context).getInt( return prefs(context).getInt(
ThemeStorePrefKeys.KEY_ACCENT_COLOR, ThemeStorePrefKeys.KEY_ACCENT_COLOR,
ATHUtils.resolveColor(context, R.attr.colorAccent, Color.parseColor("#263238")) ATHUtils.resolveColor(context, R.attr.colorAccent, Color.parseColor("#263238"))
@ -292,7 +292,7 @@ private constructor(private val mContext: Context) : ThemeStoreInterface {
@CheckResult @CheckResult
@ColorInt @ColorInt
fun backgroundColor(context: Context = App.INSTANCE): Int { fun backgroundColor(context: Context = appCtx): Int {
return prefs(context).getInt( return prefs(context).getInt(
ThemeStorePrefKeys.KEY_BACKGROUND_COLOR, ThemeStorePrefKeys.KEY_BACKGROUND_COLOR,
ATHUtils.resolveColor(context, android.R.attr.colorBackground) ATHUtils.resolveColor(context, android.R.attr.colorBackground)
@ -314,7 +314,7 @@ private constructor(private val mContext: Context) : ThemeStoreInterface {
@CheckResult @CheckResult
@ColorInt @ColorInt
fun bottomBackground(context: Context = App.INSTANCE): Int { fun bottomBackground(context: Context = appCtx): Int {
return prefs(context).getInt( return prefs(context).getInt(
ThemeStorePrefKeys.KEY_BOTTOM_BACKGROUND, ThemeStorePrefKeys.KEY_BOTTOM_BACKGROUND,
ATHUtils.resolveColor(context, android.R.attr.colorBackground) ATHUtils.resolveColor(context, android.R.attr.colorBackground)

@ -0,0 +1,600 @@
package io.legado.app.model.analyzeRule;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class QueryTTF {
private static class Header {
public int majorVersion;
public int minorVersion;
public int numOfTables;
public int searchRange;
public int entrySelector;
public int rangeShift;
}
private static class Directory {
public String tag; // table name
public int checkSum; // Check sum
public int offset; // Offset from beginning of file
public int length; // length of the table in bytes
}
private static class NameLayout {
public int format;
public int count;
public int stringOffset;
public List<NameRecord> records = new LinkedList<>();
}
private static class NameRecord {
public int platformID; // 平台标识符<0:Unicode, 1:Mac, 2:ISO, 3:Windows, 4:Custom>
public int encodingID; // 编码标识符
public int languageID; // 语言标识符
public int nameID; // 名称标识符
public int length; // 名称字符串的长度
public int offset; // 名称字符串相对于stringOffset的字节偏移量
}
private static class HeadLayout {
public int majorVersion;
public int minorVersion;
public int fontRevision;
public int checkSumAdjustment;
public int magicNumber;
public int flags;
public int unitsPerEm;
public long created;
public long modified;
public short xMin;
public short yMin;
public short xMax;
public short yMax;
public int macStyle;
public int lowestRecPPEM;
public short fontDirectionHint;
public short indexToLocFormat; // <0:loca是2字节数组, 1:loca是4字节数组>
public short glyphDataFormat;
}
private static class MaxpLayout {
public int majorVersion;
public int minorVersion;
public int numGlyphs; // 字体中的字形数量
public int maxPoints;
public int maxContours;
public int maxCompositePoints;
public int maxCompositeContours;
public int maxZones;
public int maxTwilightPoints;
public int maxStorage;
public int maxFunctionDefs;
public int maxInstructionDefs;
public int maxStackElements;
public int maxSizeOfInstructions;
public int maxComponentElements;
public int maxComponentDepth;
}
private class CmapLayout {
public int version;
public int numTables;
public List<CmapRecord> records = new LinkedList<>();
public Map<Integer, CmapFormat> tables = new HashMap<>();
}
private static class CmapRecord {
public int platformID;
public int encodingID;
public int offset;
}
private static class CmapFormat {
public int format;
public int length;
public int language;
public byte[] glyphIdArray;
}
private static class CmapFormat4 extends CmapFormat {
public int segCountX2;
public int searchRange;
public int entrySelector;
public int rangeShift;
public int[] endCode;
public int reservedPad;
public int[] startCode;
public short[] idDelta;
public int[] idRangeOffset;
public int[] glyphIdArray;
}
private static class CmapFormat6 extends CmapFormat {
public int firstCode;
public int entryCount;
public int[] glyphIdArray;
}
private static class CmapFormat12 extends CmapFormat {
public int reserved;
public int length;
public int language;
public int numGroups;
public List<Triple<Integer, Integer, Integer>> groups;
}
private static class GlyfLayout {
public short numberOfContours; // 非负值为简单字型,负值为符合字型
public short xMin;
public short yMin;
public short xMax;
public short yMax;
public int[] endPtsOfContours; // length=numberOfContours
public int instructionLength;
public byte[] instructions; // length=instructionLength
public byte[] flags;
public short[] xCoordinates; // length = flags.length
public short[] yCoordinates; // length = flags.length
}
private static class ByteArrayReader {
public int Index;
public byte[] Buffer;
public ByteArrayReader(byte[] buffer, int index) {
Buffer = buffer;
Index = index;
}
public long ReadUIntX(long len) {
long result = 0;
for (long i = 0; i < len; ++i) {
result <<= 8;
result |= Buffer[Index++] & 0xFF;
}
return result;
}
public long ReadUInt64() {
return ReadUIntX(8);
}
public int ReadUInt32() {
return (int) ReadUIntX(4);
}
public int ReadUInt16() {
return (int) ReadUIntX(2);
}
public short ReadInt16() {
return (short) ReadUIntX(2);
}
public short ReadUInt8() {
return (short) ReadUIntX(1);
}
public String ReadStrings(int len, Charset charset) {
byte[] result = len > 0 ? new byte[len] : null;
for (int i = 0; i < len; ++i) result[i] = Buffer[Index++];
return new String(result, charset);
}
public byte GetByte() {
return Buffer[Index++];
}
public byte[] GetBytes(int len) {
byte[] result = len > 0 ? new byte[len] : null;
for (int i = 0; i < len; ++i) result[i] = Buffer[Index++];
return result;
}
public int[] GetUInt16Array(int len) {
int[] result = len > 0 ? new int[len] : null;
for (int i = 0; i < len; ++i) result[i] = ReadUInt16();
return result;
}
public short[] GetInt16Array(int len) {
short[] result = len > 0 ? new short[len] : null;
for (int i = 0; i < len; ++i) result[i] = ReadInt16();
return result;
}
}
private final ByteArrayReader FontReader;
private final Header fileHeader = new Header();
private final List<Directory> Directorys = new LinkedList<>();
private final NameLayout Name = new NameLayout();
private final HeadLayout head = new HeadLayout();
private final MaxpLayout maxp = new MaxpLayout();
private final List<Integer> loca = new LinkedList<>();
private final CmapLayout Cmap = new CmapLayout();
private final List<GlyfLayout> glyf = new LinkedList<>();
private final Pair[] pps = new Pair[]{
Pair.of(3, 10),
Pair.of(0, 4),
Pair.of(3, 1),
Pair.of(1, 0),
Pair.of(0, 3),
Pair.of(0, 1)
};
public final Map<Integer, String> CodeToGlyph = new HashMap<>();
public final Map<String, Integer> GlyphToCode = new HashMap<>();
private int LimitMix = 0;
private int LimitMax = 0;
/**
* 构造函数
*
* @param buffer 传入TTF字体二进制数组
*/
public QueryTTF(byte[] buffer) {
FontReader = new ByteArrayReader(buffer, 0);
// 获取文件头
fileHeader.majorVersion = FontReader.ReadUInt16();
fileHeader.minorVersion = FontReader.ReadUInt16();
fileHeader.numOfTables = FontReader.ReadUInt16();
fileHeader.searchRange = FontReader.ReadUInt16();
fileHeader.entrySelector = FontReader.ReadUInt16();
fileHeader.rangeShift = FontReader.ReadUInt16();
// 获取目录
for (int i = 0; i < fileHeader.numOfTables; ++i) {
Directory d = new Directory();
d.tag = FontReader.ReadStrings(4, StandardCharsets.US_ASCII);
d.checkSum = FontReader.ReadUInt32();
d.offset = FontReader.ReadUInt32();
d.length = FontReader.ReadUInt32();
Directorys.add(d);
}
// 解析表 name (字体信息,包含版权、名称、作者等...)
for (Directory Temp : Directorys) {
if (Temp.tag.equals("name")) {
FontReader.Index = Temp.offset;
Name.format = FontReader.ReadUInt16();
Name.count = FontReader.ReadUInt16();
Name.stringOffset = FontReader.ReadUInt16();
for (int i = 0; i < Name.count; ++i) {
NameRecord record = new NameRecord();
record.platformID = FontReader.ReadUInt16();
record.encodingID = FontReader.ReadUInt16();
record.languageID = FontReader.ReadUInt16();
record.nameID = FontReader.ReadUInt16();
record.length = FontReader.ReadUInt16();
record.offset = FontReader.ReadUInt16();
Name.records.add(record);
}
}
}
// 解析表 head (获取 head.indexToLocFormat)
for (Directory Temp : Directorys) {
if (Temp.tag.equals("head")) {
FontReader.Index = Temp.offset;
head.majorVersion = FontReader.ReadUInt16();
head.minorVersion = FontReader.ReadUInt16();
head.fontRevision = FontReader.ReadUInt32();
head.checkSumAdjustment = FontReader.ReadUInt32();
head.magicNumber = FontReader.ReadUInt32();
head.flags = FontReader.ReadUInt16();
head.unitsPerEm = FontReader.ReadUInt16();
head.created = FontReader.ReadUInt64();
head.modified = FontReader.ReadUInt64();
head.xMin = FontReader.ReadInt16();
head.yMin = FontReader.ReadInt16();
head.xMax = FontReader.ReadInt16();
head.yMax = FontReader.ReadInt16();
head.macStyle = FontReader.ReadUInt16();
head.lowestRecPPEM = FontReader.ReadUInt16();
head.fontDirectionHint = FontReader.ReadInt16();
head.indexToLocFormat = FontReader.ReadInt16();
head.glyphDataFormat = FontReader.ReadInt16();
}
}
// 解析表 maxp (获取 maxp.numGlyphs)
for (Directory Temp : Directorys) {
if (Temp.tag.equals("maxp")) {
FontReader.Index = Temp.offset;
maxp.majorVersion = FontReader.ReadUInt16();
maxp.minorVersion = FontReader.ReadUInt16();
maxp.numGlyphs = FontReader.ReadUInt16();
maxp.maxPoints = FontReader.ReadUInt16();
maxp.maxContours = FontReader.ReadUInt16();
maxp.maxCompositePoints = FontReader.ReadUInt16();
maxp.maxCompositeContours = FontReader.ReadUInt16();
maxp.maxZones = FontReader.ReadUInt16();
maxp.maxTwilightPoints = FontReader.ReadUInt16();
maxp.maxStorage = FontReader.ReadUInt16();
maxp.maxFunctionDefs = FontReader.ReadUInt16();
maxp.maxInstructionDefs = FontReader.ReadUInt16();
maxp.maxStackElements = FontReader.ReadUInt16();
maxp.maxSizeOfInstructions = FontReader.ReadUInt16();
maxp.maxComponentElements = FontReader.ReadUInt16();
maxp.maxComponentDepth = FontReader.ReadUInt16();
}
}
// 解析表 loca (轮廓数据偏移地址表)
for (Directory Temp : Directorys) {
if (Temp.tag.equals("loca")) {
FontReader.Index = Temp.offset;
int offset = head.indexToLocFormat == 0 ? 2 : 4;
for (long i = 0; i < Temp.length; i += offset) {
loca.add(offset == 2 ? FontReader.ReadUInt16() << 1 : FontReader.ReadUInt32());
}
}
}
// 解析表 cmap (Unicode编码轮廓索引对照表)
for (Directory Temp : Directorys) {
if (Temp.tag.equals("cmap")) {
FontReader.Index = Temp.offset;
Cmap.version = FontReader.ReadUInt16();
Cmap.numTables = FontReader.ReadUInt16();
for (int i = 0; i < Cmap.numTables; ++i) {
CmapRecord record = new CmapRecord();
record.platformID = FontReader.ReadUInt16();
record.encodingID = FontReader.ReadUInt16();
record.offset = FontReader.ReadUInt32();
Cmap.records.add(record);
}
for (int i = 0; i < Cmap.numTables; ++i) {
int fmtOffset = Cmap.records.get(i).offset;
FontReader.Index = Temp.offset + fmtOffset;
int EndIndex = FontReader.Index;
int format = FontReader.ReadUInt16();
if (Cmap.tables.containsKey(fmtOffset)) continue;
if (format == 0) {
CmapFormat f = new CmapFormat();
f.format = format;
f.length = FontReader.ReadUInt16();
f.language = FontReader.ReadUInt16();
f.glyphIdArray = FontReader.GetBytes(f.length - 6);
Cmap.tables.put(fmtOffset, f);
} else if (format == 4) {
CmapFormat4 f = new CmapFormat4();
f.format = format;
f.length = FontReader.ReadUInt16();
f.language = FontReader.ReadUInt16();
f.segCountX2 = FontReader.ReadUInt16();
int segCount = f.segCountX2 >> 1;
f.searchRange = FontReader.ReadUInt16();
f.entrySelector = FontReader.ReadUInt16();
f.rangeShift = FontReader.ReadUInt16();
f.endCode = FontReader.GetUInt16Array(segCount);
f.reservedPad = FontReader.ReadUInt16();
f.startCode = FontReader.GetUInt16Array(segCount);
f.idDelta = FontReader.GetInt16Array(segCount);
f.idRangeOffset = FontReader.GetUInt16Array(segCount);
f.glyphIdArray = FontReader.GetUInt16Array((EndIndex + f.length - FontReader.Index) >> 1);
Cmap.tables.put(fmtOffset, f);
} else if (format == 6) {
CmapFormat6 f = new CmapFormat6();
f.format = format;
f.length = FontReader.ReadUInt16();
f.language = FontReader.ReadUInt16();
f.firstCode = FontReader.ReadUInt16();
f.entryCount = FontReader.ReadUInt16();
f.glyphIdArray = FontReader.GetUInt16Array(f.entryCount);
Cmap.tables.put(fmtOffset, f);
} else if (format == 12) {
CmapFormat12 f = new CmapFormat12();
f.format = format;
f.reserved = FontReader.ReadUInt16();
f.length = FontReader.ReadUInt32();
f.language = FontReader.ReadUInt32();
f.numGroups = FontReader.ReadUInt32();
f.groups = new ArrayList<>(f.numGroups);
for (int n = 0; n < f.numGroups; ++n) {
f.groups.add(Triple.of(FontReader.ReadUInt32(), FontReader.ReadUInt32(), FontReader.ReadUInt32()));
}
Cmap.tables.put(fmtOffset, f);
}
}
}
}
// 解析表 glyf (字体轮廓数据表)
for (Directory Temp : Directorys) {
if (Temp.tag.equals("glyf")) {
FontReader.Index = Temp.offset;
for (int i = 0; i < maxp.numGlyphs; ++i) {
FontReader.Index = Temp.offset + loca.get(i);
short numberOfContours = FontReader.ReadInt16();
if (numberOfContours > 0) {
GlyfLayout g = new GlyfLayout();
g.numberOfContours = numberOfContours;
g.xMin = FontReader.ReadInt16();
g.yMin = FontReader.ReadInt16();
g.xMax = FontReader.ReadInt16();
g.yMax = FontReader.ReadInt16();
g.endPtsOfContours = FontReader.GetUInt16Array(numberOfContours);
g.instructionLength = FontReader.ReadUInt16();
g.instructions = FontReader.GetBytes(g.instructionLength);
int flagLength = g.endPtsOfContours[g.endPtsOfContours.length - 1] + 1;
// 获取轮廓点描述标志
g.flags = new byte[flagLength];
for (int n = 0; n < flagLength; ++n) {
g.flags[n] = FontReader.GetByte();
if ((g.flags[n] & 0x08) != 0x00) {
for (int m = FontReader.ReadUInt8(); m > 0; --m) {
g.flags[++n] = g.flags[n - 1];
}
}
}
// 获取轮廓点描述x轴相对值
g.xCoordinates = new short[flagLength];
for (int n = 0; n < flagLength; ++n) {
short same = (short) ((g.flags[n] & 0x10) != 0 ? 1 : -1);
if ((g.flags[n] & 0x02) != 0) {
g.xCoordinates[n] = (short) (same * FontReader.ReadUInt8());
} else {
g.xCoordinates[n] = same == 1 ? (short) 0 : FontReader.ReadInt16();
}
}
// 获取轮廓点描述y轴相对值
g.yCoordinates = new short[flagLength];
for (int n = 0; n < flagLength; ++n) {
short same = (short) ((g.flags[n] & 0x20) != 0 ? 1 : -1);
if ((g.flags[n] & 0x04) != 0) {
g.yCoordinates[n] = (short) (same * FontReader.ReadUInt8());
} else {
g.yCoordinates[n] = same == 1 ? (short) 0 : FontReader.ReadInt16();
}
}
// 相对坐标转绝对坐标
// for (int n = 1; n < flagLength; ++n) {
// xCoordinates[n] += xCoordinates[n - 1];
// yCoordinates[n] += yCoordinates[n - 1];
// }
glyf.add(g);
} else {
// 复合字体暂未使用
}
}
}
}
// 建立Unicode&Glyph双向表
for (int key = 0; key < 130000; ++key) {
if (key == 0xFF) key = 0x3400;
int gid = getGlyfIndex(key);
if (gid == 0) continue;
StringBuilder sb = new StringBuilder();
// 字型数据转String,方便存HashMap
for (short b : glyf.get(gid).xCoordinates) sb.append(b);
for (short b : glyf.get(gid).yCoordinates) sb.append(b);
String val = sb.toString();
if (LimitMix == 0) LimitMix = key;
LimitMax = key;
CodeToGlyph.put(key, val);
if (GlyphToCode.containsKey(val)) continue;
GlyphToCode.put(val, key);
}
}
/**
* 获取字体信息 (1=字体名称)
*
* @param nameId 传入十进制字体信息索引
* @return 返回查询结果字符串
*/
public String getNameById(int nameId) {
for (Directory Temp : Directorys) {
if (!Temp.tag.equals("name")) continue;
FontReader.Index = Temp.offset;
break;
}
for (NameRecord record : Name.records) {
if (record.nameID != nameId) continue;
FontReader.Index += Name.stringOffset + record.offset;
return FontReader.ReadStrings(record.length, record.platformID == 1 ? StandardCharsets.UTF_8 : StandardCharsets.UTF_16BE);
}
return "error";
}
/**
* 使用Unicode值查找轮廓索引
*
* @param code 传入Unicode十进制值
* @return 返回十进制轮廓索引
*/
private int getGlyfIndex(int code) {
if (code == 0) return 0;
int fmtKey = 0;
for (@SuppressWarnings("unchecked") Pair<Integer, Integer> item : pps) {
for (CmapRecord record : Cmap.records) {
if ((item.getLeft() == record.platformID) && (item.getRight() == record.encodingID)) {
fmtKey = record.offset;
break;
}
}
if (fmtKey > 0) break;
}
if (fmtKey == 0) return 0;
int glyfID = 0;
CmapFormat table = Cmap.tables.get(fmtKey);
int fmt = table.format;
if (fmt == 0) {
if (code < table.glyphIdArray.length) glyfID = table.glyphIdArray[code] & 0xFF;
} else if (fmt == 4) {
CmapFormat4 tab = (CmapFormat4) table;
if (code > tab.endCode[tab.endCode.length - 1]) return 0;
// 二分法查找数值索引
int start = 0, middle, end = tab.endCode.length - 1;
while (start + 1 < end) {
middle = (start + end) / 2;
if (tab.endCode[middle] <= code) start = middle;
else end = middle;
}
if (tab.endCode[start] < code) ++start;
if (code < tab.startCode[start]) return 0;
if (tab.idRangeOffset[start] != 0) {
glyfID = tab.glyphIdArray[code - tab.startCode[start] + (tab.idRangeOffset[start] >> 1) - (tab.idRangeOffset.length - start)];
} else glyfID = code + tab.idDelta[start];
glyfID &= 0xFFFF;
} else if (fmt == 6) {
CmapFormat6 tab = (CmapFormat6) table;
int index = code - tab.firstCode;
if (index < 0 || index >= tab.glyphIdArray.length) glyfID = 0;
else glyfID = tab.glyphIdArray[index];
} else if (fmt == 12) {
CmapFormat12 tab = (CmapFormat12) table;
if (code > tab.groups.get(tab.numGroups - 1).getMiddle()) return 0;
// 二分法查找数值索引
int start = 0, middle, end = tab.numGroups - 1;
while (start + 1 < end) {
middle = (start + end) / 2;
if (tab.groups.get(middle).getLeft() <= code) start = middle;
else end = middle;
}
if (tab.groups.get(start).getLeft() <= code && code <= tab.groups.get(start).getMiddle()) {
glyfID = tab.groups.get(start).getRight() + code - tab.groups.get(start).getLeft();
}
}
return glyfID;
}
/**
* 判断Unicode值是否在字体范围内
*
* @param code 传入Unicode十进制值
* @return 返回bool查询结果
*/
public boolean inLimit(char code) {
return (LimitMix <= code) && (code < LimitMax);
}
/**
* 使用Unicode值获取轮廓数据
*
* @param key 传入Unicode十进制值
* @return 返回轮廓数组的String值
*/
public String getGlyfByCode(int key) {
return CodeToGlyph.getOrDefault(key, "");
}
/**
* 使用轮廓数据获取Unicode值
*
* @param val 传入轮廓数组的String值
* @return 返回Unicode十进制值
*/
public int getCodeByGlyf(String val) {
return GlyphToCode.getOrDefault(val, 0);
}
}

@ -1,633 +0,0 @@
package io.legado.app.model.analyzeRule
import org.apache.commons.lang3.tuple.Pair
import org.apache.commons.lang3.tuple.Triple
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.util.*
import kotlin.experimental.and
class QueryTTF(buffer: ByteArray) {
private class Header(
var majorVersion: Int = 0,
var minorVersion: Int = 0,
var numOfTables: Int = 0,
var searchRange: Int = 0,
var entrySelector: Int = 0,
var rangeShift: Int = 0,
)
private class Directory {
lateinit var tag: String // table name
var checkSum: Int = 0 // Check sum
var offset: Int = 0 // Offset from beginning of file
var length: Int = 0 // length of the table in bytes
}
private class NameLayout(
var format: Int = 0,
var count: Int = 0,
var stringOffset: Int = 0,
var records: MutableList<NameRecord> = LinkedList(),
)
private class NameRecord(
var platformID: Int = 0, // 平台标识符<0:Unicode, 1:Mac, 2:ISO, 3:Windows, 4:Custom>
var encodingID: Int = 0, // 编码标识符
var languageID: Int = 0, // 语言标识符
var nameID: Int = 0, // 名称标识符
var length: Int = 0,// 名称字符串的长度
var offset: Int = 0, // 名称字符串相对于stringOffset的字节偏移量
)
private class HeadLayout(
var majorVersion: Int = 0,
var minorVersion: Int = 0,
var fontRevision: Int = 0,
var checkSumAdjustment: Int = 0,
var magicNumber: Int = 0,
var flags: Int = 0,
var unitsPerEm: Int = 0,
var created: Long = 0,
var modified: Long = 0,
var xMin: Short = 0,
var yMin: Short = 0,
var xMax: Short = 0,
var yMax: Short = 0,
var macStyle: Int = 0,
var lowestRecPPEM: Int = 0,
var fontDirectionHint: Short = 0,
var indexToLocFormat: Short = 0, // <0:loca是2字节数组, 1:loca是4字节数组>
var glyphDataFormat: Short = 0,
)
private class MaxpLayout(
var majorVersion: Int = 0,
var minorVersion: Int = 0,
var numGlyphs: Int = 0, // 字体中的字形数量
var maxPoints: Int = 0,
var maxContours: Int = 0,
var maxCompositePoints: Int = 0,
var maxCompositeContours: Int = 0,
var maxZones: Int = 0,
var maxTwilightPoints: Int = 0,
var maxStorage: Int = 0,
var maxFunctionDefs: Int = 0,
var maxInstructionDefs: Int = 0,
var maxStackElements: Int = 0,
var maxSizeOfInstructions: Int = 0,
var maxComponentElements: Int = 0,
var maxComponentDepth: Int = 0,
)
private class CmapLayout(
var version: Int = 0,
var numTables: Int = 0,
var records: MutableList<CmapRecord> = LinkedList(),
var tables: MutableMap<Int, CmapFormat> = HashMap(),
)
private class CmapRecord(
var platformID: Int = 0,
var encodingID: Int = 0,
var offset: Int = 0,
)
private open class CmapFormat {
var format: Int = 0
open var length: Int = 0
open var language: Int = 0
lateinit var glyphIdArray: IntArray
}
private class CmapFormat4 : CmapFormat() {
var segCountX2: Int = 0
var searchRange: Int = 0
var entrySelector: Int = 0
var rangeShift: Int = 0
lateinit var endCode: IntArray
var reservedPad: Int = 0
lateinit var startCode: IntArray
lateinit var idDelta: ShortArray
lateinit var idRangeOffset: IntArray
// override lateinit var glyphIdArray: IntArray
}
private class CmapFormat6(
var firstCode: Int = 0,
var entryCount: Int = 0,
) : CmapFormat()
private class CmapFormat12(
var reserved: Int = 0,
override var length: Int = 0,
override var language: Int = 0,
var numGroups: Int = 0,
var groups: MutableList<Triple<Int, Int, Int>>
) : CmapFormat()
private class GlyfLayout(
var numberOfContours: Short = 0, // 非负值为简单字型,负值为符合字型
var xMin: Short = 0,
var yMin: Short = 0,
var xMax: Short = 0,
var yMax: Short = 0,
var instructionLength: Int = 0
) {
lateinit var endPtsOfContours: IntArray // length=numberOfContours
lateinit var instructions: IntArray // length=instructionLength
lateinit var flags: ByteArray
lateinit var xCoordinates: ShortArray // length = flags.length
lateinit var yCoordinates: ShortArray // length = flags.length
}
private class ByteArrayReader(var buffer: ByteArray, var index: Int) {
fun readUIntX(len: Long): Long {
var result: Long = 0
for (i in 0 until len) {
result = result shl 8
result = result or ((buffer[index++] and 0xFF.toByte()).toLong())
}
return result
}
fun readUInt64(): Long = readUIntX(8)
fun readUInt32(): Int = readUIntX(4).toInt()
fun readUInt16(): Int = readUIntX(2).toInt()
fun readInt16(): Short = readUIntX(2).toShort()
fun readUInt8(): Short = readUIntX(1).toShort()
fun readStrings(len: Int, charset: Charset): String {
val result = ByteArray(len)
for (i in 0 until len) {
result[i] = buffer[index++]
}
return String(result, charset)
}
fun getByte() = buffer[index++]
fun getBytes(len: Int): IntArray {
val result = IntArray(len)
for (i in 0 until len) {
result[i] = buffer[index++].toInt()
}
return result
}
fun getUInt16Array(len: Int): IntArray {
val result = IntArray(len)
for (i in 0 until len) {
result[i] = readUInt16()
}
return result
}
fun getInt16Array(len: Int): ShortArray {
val result = ShortArray(len)
for (i in 0 until len) {
result[i] = readInt16()
}
return result
}
}
private val fontReader: ByteArrayReader = ByteArrayReader(buffer, 0)
private val directories: MutableList<Directory> = LinkedList()
private val name = NameLayout()
private val cmap = CmapLayout()
private val pps: Array<Pair<Int, Int>> = arrayOf(
Pair.of(3, 10),
Pair.of(0, 4),
Pair.of(3, 1),
Pair.of(1, 0),
Pair.of(0, 3),
Pair.of(0, 1)
)
private val codeToGlyph: MutableMap<Int, String> = HashMap()
private val glyphToCode: MutableMap<String, Int> = HashMap()
private var limitMix = 0
private var limitMax = 0
/**
* 获取字体信息 (1=字体名称)
*
* @param nameId 传入十进制字体信息索引
* @return 返回查询结果字符串
*/
fun getNameById(nameId: Int): String {
for (temp in directories) {
if ("name" != temp.tag) {
continue
}
fontReader.index = temp.offset
break
}
for (record in name.records) {
if (record.nameID != nameId) {
continue
}
fontReader.index += name.stringOffset + record.offset
return fontReader.readStrings(record.length, if (record.platformID == 1) StandardCharsets.UTF_8 else StandardCharsets.UTF_16BE)
}
return "error"
}
/**
* 使用Unicode值查找轮廓索引
*
* @param code 传入Unicode十进制值
* @return 返回十进制轮廓索引
*/
private fun getGlyfIndex(code: Int): Int {
if (code == 0) {
return 0
}
var fmtKey = 0
for (item in pps) {
for (record in cmap.records) {
if (item.left == record.platformID && item.right == record.encodingID) {
fmtKey = record.offset
break
}
}
if (fmtKey > 0) {
break
}
}
if (fmtKey == 0) {
return 0
}
var glyfID = 0
val table = cmap.tables[fmtKey]
when (table?.format) {
0 -> {
if (code < table.glyphIdArray.size) {
glyfID = table.glyphIdArray[code] and 0xFF
}
}
4 -> {
val tab = table as CmapFormat4
if (code > tab.endCode[tab.endCode.size - 1]) {
return 0
}
// 二分法查找数值索引
var start = 0
var middle: Int
var end = tab.endCode.size - 1
while (start + 1 < end) {
middle = (start + end) / 2
if (tab.endCode[middle] <= code) {
start = middle
} else {
end = middle
}
}
if (tab.endCode[start] < code) {
++start
}
if (code < tab.startCode[start]) {
return 0
}
glyfID = if (tab.idRangeOffset[start] != 0) {
tab.glyphIdArray[code - tab.startCode[start] + (tab.idRangeOffset[start] shr 1) - (tab.idRangeOffset.size - start)]
} else {
code + tab.idDelta[start]
}
glyfID = glyfID and 0xFFFF
}
6 -> {
val tab = table as CmapFormat6
val index = code - tab.firstCode
glyfID = if (index < 0 || index >= tab.glyphIdArray.size) {
0
} else {
tab.glyphIdArray[index]
}
}
12 -> {
val tab = table as CmapFormat12
if (code > tab.groups[tab.numGroups - 1].middle) {
return 0
}
// 二分法查找数值索引
var start = 0
var middle: Int
var end = tab.numGroups - 1
while (start + 1 < end) {
middle = (start + end) / 2
if (tab.groups[middle].left <= code) {
start = middle
} else {
end = middle
}
}
if (tab.groups[start].left <= code && code <= tab.groups[start].middle) {
glyfID = tab.groups[start].right + code - tab.groups[start].left
}
}
}
return glyfID
}
/**
* 判断Unicode值是否在字体范围内
* @param code 传入Unicode十进制值
* @return 返回bool查询结果
*/
fun inLimit(code: Char): Boolean = code.toInt() in limitMix until limitMax
/**
* 使用Unicode值获取轮廓数据
*
* @param key 传入Unicode十进制值
* @return 返回轮廓数组的String值
*/
fun getGlyfByCode(key: Int): String = codeToGlyph.getOrDefault(key, "")
/**
* 使用轮廓数据获取Unicode值
*
* @param val 传入轮廓数组的String值
* @return 返回Unicode十进制值
*/
fun getCodeByGlyf(`val`: String): Int = glyphToCode.getOrDefault(`val`, 0)
/**
* 构造函数 buffer 传入TTF字体二进制数组
*/
init {
// 获取文件头
val fileHeader = Header()
fileHeader.majorVersion = fontReader.readUInt16()
fileHeader.minorVersion = fontReader.readUInt16()
fileHeader.numOfTables = fontReader.readUInt16()
fileHeader.searchRange = fontReader.readUInt16()
fileHeader.entrySelector = fontReader.readUInt16()
fileHeader.rangeShift = fontReader.readUInt16()
// 获取目录
for (i in 0 until fileHeader.numOfTables) {
val d = Directory()
d.tag = fontReader.readStrings(4, StandardCharsets.US_ASCII)
d.checkSum = fontReader.readUInt32()
d.offset = fontReader.readUInt32()
d.length = fontReader.readUInt32()
directories.add(d)
}
// 解析表 name (字体信息,包含版权、名称、作者等...)
for (temp in directories) {
if ("name" == temp.tag) {
fontReader.index = temp.offset
name.format = fontReader.readUInt16()
name.count = fontReader.readUInt16()
name.stringOffset = fontReader.readUInt16()
for (i in 0 until name.count) {
val record = NameRecord()
record.platformID = fontReader.readUInt16()
record.encodingID = fontReader.readUInt16()
record.languageID = fontReader.readUInt16()
record.nameID = fontReader.readUInt16()
record.length = fontReader.readUInt16()
record.offset = fontReader.readUInt16()
name.records.add(record)
}
}
}
// 解析表 head (获取 head.indexToLocFormat)
val head = HeadLayout()
for (temp in directories) {
if ("head" == temp.tag) {
fontReader.index = temp.offset
head.majorVersion = fontReader.readUInt16()
head.minorVersion = fontReader.readUInt16()
head.fontRevision = fontReader.readUInt32()
head.checkSumAdjustment = fontReader.readUInt32()
head.magicNumber = fontReader.readUInt32()
head.flags = fontReader.readUInt16()
head.unitsPerEm = fontReader.readUInt16()
head.created = fontReader.readUInt64()
head.modified = fontReader.readUInt64()
head.xMin = fontReader.readInt16()
head.yMin = fontReader.readInt16()
head.xMax = fontReader.readInt16()
head.yMax = fontReader.readInt16()
head.macStyle = fontReader.readUInt16()
head.lowestRecPPEM = fontReader.readUInt16()
head.fontDirectionHint = fontReader.readInt16()
head.indexToLocFormat = fontReader.readInt16()
head.glyphDataFormat = fontReader.readInt16()
}
}
// 解析表 maxp (获取 maxp.numGlyphs)
val maxp = MaxpLayout()
for (temp in directories) {
if ("maxp" == temp.tag) {
fontReader.index = temp.offset
maxp.majorVersion = fontReader.readUInt16()
maxp.minorVersion = fontReader.readUInt16()
maxp.numGlyphs = fontReader.readUInt16()
maxp.maxPoints = fontReader.readUInt16()
maxp.maxContours = fontReader.readUInt16()
maxp.maxCompositePoints = fontReader.readUInt16()
maxp.maxCompositeContours = fontReader.readUInt16()
maxp.maxZones = fontReader.readUInt16()
maxp.maxTwilightPoints = fontReader.readUInt16()
maxp.maxStorage = fontReader.readUInt16()
maxp.maxFunctionDefs = fontReader.readUInt16()
maxp.maxInstructionDefs = fontReader.readUInt16()
maxp.maxStackElements = fontReader.readUInt16()
maxp.maxSizeOfInstructions = fontReader.readUInt16()
maxp.maxComponentElements = fontReader.readUInt16()
maxp.maxComponentDepth = fontReader.readUInt16()
}
}
// 解析表 loca (轮廓数据偏移地址表)
val loca: MutableList<Int> = LinkedList()
for (temp in directories) {
if ("loca" == temp.tag) {
fontReader.index = temp.offset
val offset = if (head.indexToLocFormat == 0.toShort()) 2 else 4
var i: Long = 0
while (i < temp.length) {
loca.add(if (offset == 2) fontReader.readUInt16() shl 1 else fontReader.readUInt32())
i += offset
}
}
}
// 解析表 cmap (Unicode编码轮廓索引对照表)
for (temp in directories) {
if ("cmap" == temp.tag) {
fontReader.index = temp.offset
cmap.version = fontReader.readUInt16()
cmap.numTables = fontReader.readUInt16()
for (i in 0 until cmap.numTables) {
val record = CmapRecord()
record.platformID = fontReader.readUInt16()
record.encodingID = fontReader.readUInt16()
record.offset = fontReader.readUInt32()
cmap.records.add(record)
}
for (i in 0 until cmap.numTables) {
val fmtOffset = cmap.records[i].offset
fontReader.index = temp.offset + fmtOffset
val endIndex = fontReader.index
val format = fontReader.readUInt16()
if (cmap.tables.containsKey(fmtOffset)) {
continue
}
when (format) {
0 -> {
val f = CmapFormat()
f.format = format
f.length = fontReader.readUInt16()
f.language = fontReader.readUInt16()
f.glyphIdArray = fontReader.getBytes(f.length - 6)
cmap.tables[fmtOffset] = f
}
4 -> {
val f = CmapFormat4()
f.format = format
f.length = fontReader.readUInt16()
f.language = fontReader.readUInt16()
f.segCountX2 = fontReader.readUInt16()
val segCount = f.segCountX2 shr 1
f.searchRange = fontReader.readUInt16()
f.entrySelector = fontReader.readUInt16()
f.rangeShift = fontReader.readUInt16()
f.endCode = fontReader.getUInt16Array(segCount)
f.reservedPad = fontReader.readUInt16()
f.startCode = fontReader.getUInt16Array(segCount)
f.idDelta = fontReader.getInt16Array(segCount)
f.idRangeOffset = fontReader.getUInt16Array(segCount)
f.glyphIdArray = fontReader.getUInt16Array(endIndex + f.length - fontReader.index shr 1)
cmap.tables[fmtOffset] = f
}
6 -> {
val f = CmapFormat6()
f.format = format
f.length = fontReader.readUInt16()
f.language = fontReader.readUInt16()
f.firstCode = fontReader.readUInt16()
f.entryCount = fontReader.readUInt16()
f.glyphIdArray = fontReader.getUInt16Array(f.entryCount)
cmap.tables[fmtOffset] = f
}
12 -> {
val f = CmapFormat12(
reserved = fontReader.readUInt16(),
length = fontReader.readUInt32(),
language = fontReader.readUInt32(),
numGroups = fontReader.readUInt32(),
groups = ArrayList(fontReader.readUInt32())
)
f.format = format
for (n in 0 until f.numGroups) {
(f.groups as ArrayList<Triple<Int, Int, Int>>).add(Triple.of(fontReader.readUInt32(), fontReader.readUInt32(), fontReader.readUInt32()))
}
cmap.tables[fmtOffset] = f
}
}
}
}
}
// 解析表 glyf (字体轮廓数据表)
val glyf: MutableList<GlyfLayout> = LinkedList()
for (temp in directories) {
if ("glyf" == temp.tag) {
fontReader.index = temp.offset
for (i in 0 until maxp.numGlyphs) {
fontReader.index = temp.offset + loca[i]
val numberOfContours = fontReader.readInt16()
if (numberOfContours > 0) {
val g = GlyfLayout()
g.numberOfContours = numberOfContours
g.xMin = fontReader.readInt16()
g.yMin = fontReader.readInt16()
g.xMax = fontReader.readInt16()
g.yMax = fontReader.readInt16()
g.endPtsOfContours = fontReader.getUInt16Array(numberOfContours.toInt())
g.instructionLength = fontReader.readUInt16()
g.instructions = fontReader.getBytes(g.instructionLength)
val flagLength = g.endPtsOfContours[g.endPtsOfContours.size - 1] + 1
// 获取轮廓点描述标志
g.flags = ByteArray(flagLength).apply {
var n = 0
while (n < flagLength) {
this[n] = fontReader.getByte()
if ((this[n] and 0x08) != 0x00.toByte()) {
for (m in fontReader.readUInt8() downTo 1) {
this[++n] = this[n - 1]
}
}
++n
}
}
// 获取轮廓点描述x轴相对值
g.xCoordinates = ShortArray(flagLength)
for (n in 0 until flagLength) {
val same = (if (g.flags[n] and 0x10 != 0.toByte()) 1 else -1).toShort()
if (g.flags[n] and 0x02 != 0.toByte()) {
g.xCoordinates[n] = (same * fontReader.readUInt8()).toShort()
} else {
g.xCoordinates[n] = if (same.toInt() == 1) 0.toShort() else fontReader.readInt16()
}
}
// 获取轮廓点描述y轴相对值
g.yCoordinates = ShortArray(flagLength)
for (n in 0 until flagLength) {
val same = (if (g.flags[n] and 0x20 != 0.toByte()) 1 else -1).toShort()
if (g.flags[n] and 0x04 != 0.toByte()) {
g.yCoordinates[n] = (same * fontReader.readUInt8()).toShort()
} else {
g.yCoordinates[n] = if (same.toInt() == 1) 0.toShort() else fontReader.readInt16()
}
}
// 相对坐标转绝对坐标
// for (int n = 1; n < flagLength; ++n) {
// xCoordinates[n] += xCoordinates[n - 1];
// yCoordinates[n] += yCoordinates[n - 1];
// }
glyf.add(g)
} //else情况:复合字体暂未使用
}
}
}
// 建立Unicode&Glyph双向表
var key = 0
while (key < 130000) {
if (key == 0xFF) {
key = 0x3400
}
val gid = getGlyfIndex(key)
if (gid == 0) {
++key
continue
}
val sb = StringBuilder()
// 字型数据转String,方便存HashMap
for (b in glyf[gid].xCoordinates) {
sb.append(b.toInt())
}
for (b in glyf[gid].yCoordinates) {
sb.append(b.toInt())
}
val `val` = sb.toString()
if (limitMix == 0) {
limitMix = key
}
limitMax = key
codeToGlyph[key] = `val`
if (glyphToCode.containsKey(`val`)) {
++key
continue
}
glyphToCode[`val`] = key
++key
}
}
}

@ -1,12 +1,13 @@
package io.legado.app.model.localBook package io.legado.app.model.localBook
import android.net.Uri import android.net.Uri
import io.legado.app.App import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.TxtTocRule import io.legado.app.data.entities.TxtTocRule
import io.legado.app.help.DefaultData import io.legado.app.help.DefaultData
import io.legado.app.utils.* import io.legado.app.utils.*
import splitties.init.appCtx
import java.io.File import java.io.File
import java.io.RandomAccessFile import java.io.RandomAccessFile
import java.nio.charset.Charset import java.nio.charset.Charset
@ -266,7 +267,7 @@ class AnalyzeTxtFile {
val bookFile = FileUtils.getFile(LocalBook.cacheFolder, book.originName) val bookFile = FileUtils.getFile(LocalBook.cacheFolder, book.originName)
if (!bookFile.exists()) { if (!bookFile.exists()) {
bookFile.createNewFile() bookFile.createNewFile()
DocumentUtils.readBytes(App.INSTANCE, uri)?.let { DocumentUtils.readBytes(appCtx, uri)?.let {
bookFile.writeBytes(it) bookFile.writeBytes(it)
} }
} }
@ -276,10 +277,10 @@ class AnalyzeTxtFile {
} }
private fun getTocRules(): List<TxtTocRule> { private fun getTocRules(): List<TxtTocRule> {
var rules = App.db.txtTocRule.enabled var rules = appDb.txtTocRule.enabled
if (rules.isEmpty()) { if (rules.isEmpty()) {
rules = DefaultData.txtTocRules.apply { rules = DefaultData.txtTocRules.apply {
App.db.txtTocRule.insert(*this.toTypedArray()) appDb.txtTocRule.insert(*this.toTypedArray())
}.filter { }.filter {
it.enable it.enable
} }

@ -4,13 +4,13 @@ import android.graphics.Bitmap
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import android.text.TextUtils import android.text.TextUtils
import io.legado.app.App
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.utils.* import io.legado.app.utils.*
import nl.siegmann.epublib.domain.Book import nl.siegmann.epublib.domain.Book
import nl.siegmann.epublib.domain.TOCReference import nl.siegmann.epublib.domain.TOCReference
import nl.siegmann.epublib.epub.EpubReader import nl.siegmann.epublib.epub.EpubReader
import org.jsoup.Jsoup import org.jsoup.Jsoup
import splitties.init.appCtx
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
@ -59,14 +59,14 @@ class EPUBFile(val book: io.legado.app.data.entities.Book) {
val epubReader = EpubReader() val epubReader = EpubReader()
val inputStream = if (book.bookUrl.isContentScheme()) { val inputStream = if (book.bookUrl.isContentScheme()) {
val uri = Uri.parse(book.bookUrl) val uri = Uri.parse(book.bookUrl)
App.INSTANCE.contentResolver.openInputStream(uri) appCtx.contentResolver.openInputStream(uri)
} else { } else {
File(book.bookUrl).inputStream() File(book.bookUrl).inputStream()
} }
epubBook = epubReader.readEpub(inputStream) epubBook = epubReader.readEpub(inputStream)
if (book.coverUrl.isNullOrEmpty()) { if (book.coverUrl.isNullOrEmpty()) {
book.coverUrl = FileUtils.getPath( book.coverUrl = FileUtils.getPath(
App.INSTANCE.externalFilesDir, appCtx.externalFilesDir,
"covers", "covers",
"${MD5Utils.md5Encode16(book.bookUrl)}.jpg" "${MD5Utils.md5Encode16(book.bookUrl)}.jpg"
) )

@ -2,20 +2,21 @@ package io.legado.app.model.localBook
import android.net.Uri import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import io.legado.app.App import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.utils.* import io.legado.app.utils.*
import splitties.init.appCtx
import java.io.File import java.io.File
object LocalBook { object LocalBook {
private const val folderName = "bookTxt" private const val folderName = "bookTxt"
val cacheFolder: File by lazy { val cacheFolder: File by lazy {
val rootFile = App.INSTANCE.getExternalFilesDir(null) val rootFile = appCtx.getExternalFilesDir(null)
?: App.INSTANCE.externalCacheDir ?: appCtx.externalCacheDir
?: App.INSTANCE.cacheDir ?: appCtx.cacheDir
FileUtils.createFolderIfNotExist(rootFile, folderName) FileUtils.createFolderIfNotExist(rootFile, folderName)
} }
@ -39,12 +40,12 @@ object LocalBook {
val path: String val path: String
val fileName = if (uri.isContentScheme()) { val fileName = if (uri.isContentScheme()) {
path = uri.toString() path = uri.toString()
val doc = DocumentFile.fromSingleUri(App.INSTANCE, uri) val doc = DocumentFile.fromSingleUri(appCtx, uri)
doc?.let { doc?.let {
val bookFile = FileUtils.getFile(cacheFolder, it.name!!) val bookFile = FileUtils.getFile(cacheFolder, it.name!!)
if (!bookFile.exists()) { if (!bookFile.exists()) {
bookFile.createNewFile() bookFile.createNewFile()
doc.readBytes(App.INSTANCE)?.let { bytes -> doc.readBytes(appCtx)?.let { bytes ->
bookFile.writeBytes(bytes) bookFile.writeBytes(bytes)
} }
} }
@ -77,12 +78,12 @@ object LocalBook {
author = author, author = author,
originName = fileName, originName = fileName,
coverUrl = FileUtils.getPath( coverUrl = FileUtils.getPath(
App.INSTANCE.externalFilesDir, appCtx.externalFilesDir,
"covers", "covers",
"${MD5Utils.md5Encode16(path)}.jpg" "${MD5Utils.md5Encode16(path)}.jpg"
) )
) )
App.db.bookDao.insert(book) appDb.bookDao.insert(book)
return book return book
} }
@ -96,7 +97,7 @@ object LocalBook {
if (deleteOriginal) { if (deleteOriginal) {
if (book.bookUrl.isContentScheme()) { if (book.bookUrl.isContentScheme()) {
val uri = Uri.parse(book.bookUrl) val uri = Uri.parse(book.bookUrl)
DocumentFile.fromSingleUri(App.INSTANCE, uri)?.delete() DocumentFile.fromSingleUri(appCtx, uri)?.delete()
} else { } else {
FileUtils.deleteFile(book.bookUrl) FileUtils.deleteFile(book.bookUrl)
} }

@ -1,7 +1,6 @@
package io.legado.app.model.rss package io.legado.app.model.rss
import androidx.annotation.Keep import androidx.annotation.Keep
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.data.entities.RssArticle import io.legado.app.data.entities.RssArticle
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
@ -10,6 +9,7 @@ import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.model.analyzeRule.RuleDataInterface import io.legado.app.model.analyzeRule.RuleDataInterface
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.NetworkUtils import io.legado.app.utils.NetworkUtils
import splitties.init.appCtx
import java.util.* import java.util.*
@Keep @Keep
@ -27,7 +27,7 @@ object RssParserByRule {
var nextUrl: String? = null var nextUrl: String? = null
if (body.isNullOrBlank()) { if (body.isNullOrBlank()) {
throw Exception( throw Exception(
App.INSTANCE.getString(R.string.error_get_web_content, rssSource.sourceUrl) appCtx.getString(R.string.error_get_web_content, rssSource.sourceUrl)
) )
} }
Debug.log(sourceUrl, "≡获取成功:$sourceUrl") Debug.log(sourceUrl, "≡获取成功:$sourceUrl")

@ -1,7 +1,6 @@
package io.legado.app.model.webBook package io.legado.app.model.webBook
import android.text.TextUtils import android.text.TextUtils
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
@ -14,6 +13,7 @@ import io.legado.app.model.analyzeRule.AnalyzeUrl
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import splitties.init.appCtx
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException import kotlin.coroutines.resumeWithException
@ -29,7 +29,7 @@ object BookChapterList {
kotlin.runCatching { kotlin.runCatching {
val chapterList = ArrayList<BookChapter>() val chapterList = ArrayList<BookChapter>()
body ?: throw Exception( body ?: throw Exception(
App.INSTANCE.getString(R.string.error_get_web_content, baseUrl) appCtx.getString(R.string.error_get_web_content, baseUrl)
) )
Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}")

@ -1,7 +1,7 @@
package io.legado.app.model.webBook package io.legado.app.model.webBook
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
@ -14,6 +14,7 @@ import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.htmlFormat import io.legado.app.utils.htmlFormat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import splitties.init.appCtx
object BookContent { object BookContent {
@ -28,7 +29,7 @@ object BookContent {
nextChapterUrlF: String? = null nextChapterUrlF: String? = null
): String { ): String {
body ?: throw Exception( body ?: throw Exception(
App.INSTANCE.getString(R.string.error_get_web_content, baseUrl) appCtx.getString(R.string.error_get_web_content, baseUrl)
) )
Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}")
val content = StringBuilder() val content = StringBuilder()
@ -45,7 +46,7 @@ object BookContent {
val nextChapterUrl = if (!nextChapterUrlF.isNullOrEmpty()) val nextChapterUrl = if (!nextChapterUrlF.isNullOrEmpty())
nextChapterUrlF nextChapterUrlF
else else
App.db.bookChapterDao.getChapter(book.bookUrl, bookChapter.index + 1)?.url appDb.bookChapterDao.getChapter(book.bookUrl, bookChapter.index + 1)?.url
while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) { while (nextUrl.isNotEmpty() && !nextUrlList.contains(nextUrl)) {
if (!nextChapterUrl.isNullOrEmpty() if (!nextChapterUrl.isNullOrEmpty()
&& NetworkUtils.getAbsoluteURL(baseUrl, nextUrl) && NetworkUtils.getAbsoluteURL(baseUrl, nextUrl)

@ -1,6 +1,5 @@
package io.legado.app.model.webBook package io.legado.app.model.webBook
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
@ -12,6 +11,7 @@ import io.legado.app.utils.StringUtils.wordCountFormat
import io.legado.app.utils.htmlFormat import io.legado.app.utils.htmlFormat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import splitties.init.appCtx
object BookInfo { object BookInfo {
@ -25,7 +25,7 @@ object BookInfo {
canReName: Boolean, canReName: Boolean,
) { ) {
body ?: throw Exception( body ?: throw Exception(
App.INSTANCE.getString(R.string.error_get_web_content, baseUrl) appCtx.getString(R.string.error_get_web_content, baseUrl)
) )
Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}") Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}")
val infoRule = bookSource.getBookInfoRule() val infoRule = bookSource.getBookInfoRule()

@ -1,6 +1,5 @@
package io.legado.app.model.webBook package io.legado.app.model.webBook
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
@ -14,6 +13,7 @@ import io.legado.app.utils.StringUtils.wordCountFormat
import io.legado.app.utils.htmlFormat import io.legado.app.utils.htmlFormat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import splitties.init.appCtx
object BookList { object BookList {
@ -29,7 +29,7 @@ object BookList {
): ArrayList<SearchBook> { ): ArrayList<SearchBook> {
val bookList = ArrayList<SearchBook>() val bookList = ArrayList<SearchBook>()
body ?: throw Exception( body ?: throw Exception(
App.INSTANCE.getString( appCtx.getString(
R.string.error_get_web_content, R.string.error_get_web_content,
analyzeUrl.ruleUrl analyzeUrl.ruleUrl
) )

@ -1,6 +1,6 @@
package io.legado.app.model.webBook package io.legado.app.model.webBook
import io.legado.app.App import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
@ -10,6 +10,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.ExecutorCoroutineDispatcher import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import splitties.init.appCtx
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.min import kotlin.math.min
@ -44,12 +45,12 @@ class SearchBookModel(private val scope: CoroutineScope, private val callBack: C
initSearchPool() initSearchPool()
mSearchId = searchId mSearchId = searchId
searchPage = 1 searchPage = 1
val searchGroup = App.INSTANCE.getPrefString("searchGroup") ?: "" val searchGroup = appCtx.getPrefString("searchGroup") ?: ""
bookSourceList.clear() bookSourceList.clear()
if (searchGroup.isBlank()) { if (searchGroup.isBlank()) {
bookSourceList.addAll(App.db.bookSourceDao.allEnabled) bookSourceList.addAll(appDb.bookSourceDao.allEnabled)
} else { } else {
bookSourceList.addAll(App.db.bookSourceDao.getEnabledByGroup(searchGroup)) bookSourceList.addAll(appDb.bookSourceDao.getEnabledByGroup(searchGroup))
} }
} else { } else {
searchPage++ searchPage++

@ -4,8 +4,8 @@ import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.view.KeyEvent import android.view.KeyEvent
import io.legado.app.App
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.help.ActivityHelp import io.legado.app.help.ActivityHelp
import io.legado.app.service.AudioPlayService import io.legado.app.service.AudioPlayService
@ -76,7 +76,7 @@ class MediaButtonReceiver : BroadcastReceiver() {
else -> if (context.getPrefBoolean("mediaButtonOnExit", true)) { else -> if (context.getPrefBoolean("mediaButtonOnExit", true)) {
GlobalScope.launch(Main) { GlobalScope.launch(Main) {
val lastBook: Book? = withContext(IO) { val lastBook: Book? = withContext(IO) {
App.db.bookDao.lastReadBook appDb.bookDao.lastReadBook
} }
lastBook?.let { lastBook?.let {
if (!ActivityHelp.isExist(MainActivity::class.java)) { if (!ActivityHelp.isExist(MainActivity::class.java)) {

@ -6,7 +6,7 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import io.legado.app.ui.book.search.SearchActivity import io.legado.app.ui.book.search.SearchActivity
import io.legado.app.ui.main.MainActivity import io.legado.app.ui.main.MainActivity
import org.jetbrains.anko.startActivity import io.legado.app.utils.startActivity
class SharedReceiverActivity : AppCompatActivity() { class SharedReceiverActivity : AppCompatActivity() {
@ -22,7 +22,9 @@ class SharedReceiverActivity : AppCompatActivity() {
if (Intent.ACTION_SEND == intent.action && intent.type == receivingType) { if (Intent.ACTION_SEND == intent.action && intent.type == receivingType) {
intent.getStringExtra(Intent.EXTRA_TEXT)?.let { intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
if (openUrl(it)) { if (openUrl(it)) {
startActivity<SearchActivity>(Pair("key", it)) startActivity<SearchActivity> {
putExtra("key", it)
}
} }
} }
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
@ -31,7 +33,9 @@ class SharedReceiverActivity : AppCompatActivity() {
) { ) {
intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT)?.let { intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT)?.let {
if (openUrl(it)) { if (openUrl(it)) {
startActivity<SearchActivity>(Pair("key", it)) startActivity<SearchActivity> {
putExtra("key", it)
}
} }
} }
} }

@ -16,13 +16,13 @@ import android.os.Looper
import android.support.v4.media.session.MediaSessionCompat import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat import android.support.v4.media.session.PlaybackStateCompat
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseService import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.constant.Status import io.legado.app.constant.Status
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.IntentHelp import io.legado.app.help.IntentHelp
import io.legado.app.help.MediaHelp import io.legado.app.help.MediaHelp
@ -31,10 +31,11 @@ import io.legado.app.receiver.MediaButtonReceiver
import io.legado.app.service.help.AudioPlay import io.legado.app.service.help.AudioPlay
import io.legado.app.ui.audio.AudioPlayActivity import io.legado.app.ui.audio.AudioPlayActivity
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jetbrains.anko.toast import splitties.init.appCtx
class AudioPlayService : BaseService(), class AudioPlayService : BaseService(),
@ -133,7 +134,7 @@ class AudioPlayService : BaseService(),
}.onFailure { }.onFailure {
it.printStackTrace() it.printStackTrace()
launch { launch {
toast("$url ${it.localizedMessage}") toastOnUi("$url ${it.localizedMessage}")
stopSelf() stopSelf()
} }
} }
@ -219,7 +220,7 @@ class AudioPlayService : BaseService(),
if (!mediaPlayer.isPlaying) { if (!mediaPlayer.isPlaying) {
AudioPlay.status = Status.STOP AudioPlay.status = Status.STOP
postEvent(EventBus.AUDIO_STATE, Status.STOP) postEvent(EventBus.AUDIO_STATE, Status.STOP)
launch { toast("error: $what $extra $url") } launch { toastOnUi("error: $what $extra $url") }
} }
return true return true
} }
@ -274,7 +275,7 @@ class AudioPlayService : BaseService(),
.onSuccess { content -> .onSuccess { content ->
if (content.isEmpty()) { if (content.isEmpty()) {
withContext(Main) { withContext(Main) {
toast("未获取到资源链接") toastOnUi("未获取到资源链接")
} }
} else { } else {
contentLoadFinish(chapter, content) contentLoadFinish(chapter, content)
@ -286,7 +287,7 @@ class AudioPlayService : BaseService(),
} }
} else { } else {
removeLoading(chapter.index) removeLoading(chapter.index)
toast("book or source is null") toastOnUi("book or source is null")
} }
} }
} }
@ -321,7 +322,7 @@ class AudioPlayService : BaseService(),
execute { execute {
AudioPlay.book?.let { AudioPlay.book?.let {
AudioPlay.durChapterPos = mediaPlayer.currentPosition AudioPlay.durChapterPos = mediaPlayer.currentPosition
App.db.bookDao.upProgress(it.bookUrl, AudioPlay.durChapterPos) appDb.bookDao.upProgress(it.bookUrl, AudioPlay.durChapterPos)
} }
} }
} }
@ -370,7 +371,7 @@ class AudioPlayService : BaseService(),
Intent( Intent(
Intent.ACTION_MEDIA_BUTTON, Intent.ACTION_MEDIA_BUTTON,
null, null,
App.INSTANCE, appCtx,
MediaButtonReceiver::class.java MediaButtonReceiver::class.java
), ),
PendingIntent.FLAG_CANCEL_CURRENT PendingIntent.FLAG_CANCEL_CURRENT

@ -14,7 +14,6 @@ import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat import android.support.v4.media.session.PlaybackStateCompat
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseService import io.legado.app.base.BaseService
import io.legado.app.constant.* import io.legado.app.constant.*
@ -27,6 +26,7 @@ import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.book.read.page.entities.TextChapter import io.legado.app.ui.book.read.page.entities.TextChapter
import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
import splitties.init.appCtx
abstract class BaseReadAloudService : BaseService(), abstract class BaseReadAloudService : BaseService(),
AudioManager.OnAudioFocusChangeListener { AudioManager.OnAudioFocusChangeListener {
@ -233,7 +233,7 @@ abstract class BaseReadAloudService : BaseService(),
Intent( Intent(
Intent.ACTION_MEDIA_BUTTON, Intent.ACTION_MEDIA_BUTTON,
null, null,
App.INSTANCE, appCtx,
MediaButtonReceiver::class.java MediaButtonReceiver::class.java
), ),
PendingIntent.FLAG_CANCEL_CURRENT PendingIntent.FLAG_CANCEL_CURRENT

@ -4,12 +4,12 @@ import android.content.Intent
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseService import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
@ -20,9 +20,10 @@ import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.service.help.CacheBook import io.legado.app.service.help.CacheBook
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import org.jetbrains.anko.toast import splitties.init.appCtx
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.CopyOnWriteArraySet
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -43,7 +44,7 @@ class CacheBookService : BaseService() {
@Volatile @Volatile
private var downloadingCount = 0 private var downloadingCount = 0
private var notificationContent = App.INSTANCE.getString(R.string.starting_download) private var notificationContent = appCtx.getString(R.string.starting_download)
private val notificationBuilder by lazy { private val notificationBuilder by lazy {
val builder = NotificationCompat.Builder(this, AppConst.channelIdDownload) val builder = NotificationCompat.Builder(this, AppConst.channelIdDownload)
@ -95,7 +96,7 @@ class CacheBookService : BaseService() {
synchronized(this) { synchronized(this) {
book = bookMap[bookUrl] book = bookMap[bookUrl]
if (book == null) { if (book == null) {
book = App.db.bookDao.getBook(bookUrl) book = appDb.bookDao.getBook(bookUrl)
if (book == null) { if (book == null) {
removeDownload(bookUrl) removeDownload(bookUrl)
} }
@ -111,7 +112,7 @@ class CacheBookService : BaseService() {
synchronized(this) { synchronized(this) {
webBook = webBookMap[origin] webBook = webBookMap[origin]
if (webBook == null) { if (webBook == null) {
App.db.bookSourceDao.getBookSource(origin)?.let { appDb.bookSourceDao.getBookSource(origin)?.let {
webBook = WebBook(it) webBook = WebBook(it)
} }
if (webBook == null) { if (webBook == null) {
@ -128,12 +129,12 @@ class CacheBookService : BaseService() {
if (downloadMap.containsKey(bookUrl)) { if (downloadMap.containsKey(bookUrl)) {
notificationContent = getString(R.string.already_in_download) notificationContent = getString(R.string.already_in_download)
upNotification() upNotification()
toast(notificationContent) toastOnUi(notificationContent)
return return
} }
downloadCount[bookUrl] = DownloadCount() downloadCount[bookUrl] = DownloadCount()
execute { execute {
App.db.bookChapterDao.getChapterList(bookUrl, start, end).let { appDb.bookChapterDao.getChapterList(bookUrl, start, end).let {
if (it.isNotEmpty()) { if (it.isNotEmpty()) {
val chapters = CopyOnWriteArraySet<BookChapter>() val chapters = CopyOnWriteArraySet<BookChapter>()
chapters.addAll(it) chapters.addAll(it)

@ -2,12 +2,12 @@ package io.legado.app.service
import android.content.Intent import android.content.Intent
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseService import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.IntentHelp import io.legado.app.help.IntentHelp
@ -16,8 +16,8 @@ import io.legado.app.model.webBook.WebBook
import io.legado.app.service.help.CheckSource import io.legado.app.service.help.CheckSource
import io.legado.app.ui.book.source.manage.BookSourceActivity import io.legado.app.ui.book.source.manage.BookSourceActivity
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import org.jetbrains.anko.toast
import java.util.concurrent.Executors import java.util.concurrent.Executors
import kotlin.math.min import kotlin.math.min
@ -70,7 +70,7 @@ class CheckSourceService : BaseService() {
private fun check(ids: List<String>) { private fun check(ids: List<String>) {
if (allIds.isNotEmpty()) { if (allIds.isNotEmpty()) {
toast("已有书源在校验,等完成后再试") toastOnUi("已有书源在校验,等完成后再试")
return return
} }
tasks.clear() tasks.clear()
@ -97,7 +97,7 @@ class CheckSourceService : BaseService() {
execute { execute {
if (index < allIds.size) { if (index < allIds.size) {
val sourceUrl = allIds[index] val sourceUrl = allIds[index]
App.db.bookSourceDao.getBookSource(sourceUrl)?.let { source -> appDb.bookSourceDao.getBookSource(sourceUrl)?.let { source ->
check(source) check(source)
} ?: onNext(sourceUrl, "") } ?: onNext(sourceUrl, "")
} }
@ -133,10 +133,10 @@ class CheckSourceService : BaseService() {
source.addGroup("失效") source.addGroup("失效")
source.bookSourceComment = source.bookSourceComment =
"error:${it.localizedMessage}\n${source.bookSourceComment}" "error:${it.localizedMessage}\n${source.bookSourceComment}"
App.db.bookSourceDao.update(source) appDb.bookSourceDao.update(source)
}.onSuccess { }.onSuccess {
source.removeGroup("失效") source.removeGroup("失效")
App.db.bookSourceDao.update(source) appDb.bookSourceDao.update(source)
}.onFinally { }.onFinally {
onNext(source.bookSourceUrl, source.bookSourceName) onNext(source.bookSourceUrl, source.bookSourceName)
} }

@ -20,8 +20,8 @@ import io.legado.app.constant.IntentAction
import io.legado.app.help.IntentHelp import io.legado.app.help.IntentHelp
import io.legado.app.utils.RealPathUtil import io.legado.app.utils.RealPathUtil
import io.legado.app.utils.msg import io.legado.app.utils.msg
import org.jetbrains.anko.downloadManager import io.legado.app.utils.toastOnUi
import org.jetbrains.anko.toast import splitties.systemservices.downloadManager
import java.io.File import java.io.File
@ -149,7 +149,7 @@ class DownloadService : BaseService() {
try { try {
startActivity(intent) startActivity(intent)
} catch (e: Exception) { } catch (e: Exception) {
toast(e.msg) toastOnUi(e.msg)
} }
} }
} }

@ -10,15 +10,9 @@ import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.analyzeRule.AnalyzeUrl import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.service.help.ReadAloud import io.legado.app.service.help.ReadAloud
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.utils.FileUtils import io.legado.app.utils.*
import io.legado.app.utils.LogUtils
import io.legado.app.utils.MD5Utils
import io.legado.app.utils.postEvent
import kotlinx.coroutines.ensureActive import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.isActive import kotlinx.coroutines.isActive
import org.jetbrains.anko.collections.forEachWithIndex
import org.jetbrains.anko.runOnUiThread
import org.jetbrains.anko.toast
import java.io.File import java.io.File
import java.io.FileDescriptor import java.io.FileDescriptor
import java.io.FileInputStream import java.io.FileInputStream
@ -84,10 +78,10 @@ class HttpReadAloudService : BaseReadAloudService(),
task = execute { task = execute {
removeCacheFile() removeCacheFile()
ReadAloud.httpTTS?.let { ReadAloud.httpTTS?.let {
contentList.forEachWithIndex { index, item -> contentList.forEachIndexed { index, item ->
if (isActive) { if (isActive) {
val fileName = val fileName =
md5SpeakFileName(it.url, AppConfig.ttsSpeechRate.toString(), item) md5SpeakFileName(it.url, AppConfig.ttsSpeechRate.toString(), item)
if (hasSpeakFile(fileName)) { //已经下载好的语音缓存 if (hasSpeakFile(fileName)) { //已经下载好的语音缓存
if (index == nowSpeak) { if (index == nowSpeak) {
@ -116,12 +110,6 @@ class HttpReadAloudService : BaseReadAloudService(),
val fis = FileInputStream(file) val fis = FileInputStream(file)
// 用来检测下载的文件是否为可正常播放的音频 (如果不是的话抛出异常,没找到更秒的办法,先这么着吧)
MediaPlayer().apply {
setDataSource(fis.fd)
prepare()
release()
}
if (index == nowSpeak) { if (index == nowSpeak) {
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
playAudio(fis.fd) playAudio(fis.fd)
@ -129,17 +117,17 @@ class HttpReadAloudService : BaseReadAloudService(),
} }
} catch (e: SocketTimeoutException) { } catch (e: SocketTimeoutException) {
removeSpeakCacheFile(fileName) removeSpeakCacheFile(fileName)
runOnUiThread { toast("tts接口超时,尝试重新获取") } toastOnUi("tts接口超时,尝试重新获取")
downloadAudio() downloadAudio()
} catch (e: ConnectException) { } catch (e: ConnectException) {
removeSpeakCacheFile(fileName) removeSpeakCacheFile(fileName)
runOnUiThread { toast("网络错误") } toastOnUi("网络错误")
} catch (e: IOException) { } catch (e: IOException) {
val file = getSpeakFileAsMd5(fileName) val file = getSpeakFileAsMd5(fileName)
if (file.exists()) { if (file.exists()) {
FileUtils.deleteFile(file.absolutePath) FileUtils.deleteFile(file.absolutePath)
} }
runOnUiThread { toast("tts文件解析错误") } toastOnUi("tts文件解析错误")
} catch (e: Exception) { } catch (e: Exception) {
removeSpeakCacheFile(fileName) removeSpeakCacheFile(fileName)
} }

@ -13,8 +13,8 @@ import io.legado.app.help.MediaHelp
import io.legado.app.service.help.ReadBook import io.legado.app.service.help.ReadBook
import io.legado.app.utils.getPrefBoolean import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.anko.toast
import java.util.* import java.util.*
class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener { class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener {
@ -64,7 +64,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
} }
} else { } else {
launch { launch {
toast(R.string.tts_init_failed) toastOnUi(R.string.tts_init_failed)
} }
} }
} }

@ -10,14 +10,9 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.help.IntentHelp import io.legado.app.help.IntentHelp
import io.legado.app.utils.NetworkUtils import io.legado.app.utils.*
import io.legado.app.utils.getPrefInt
import io.legado.app.utils.postEvent
import io.legado.app.web.HttpServer import io.legado.app.web.HttpServer
import io.legado.app.web.WebSocketServer import io.legado.app.web.WebSocketServer
import kotlinx.coroutines.launch
import org.jetbrains.anko.startService
import org.jetbrains.anko.toast
import java.io.IOException import java.io.IOException
class WebService : BaseService() { class WebService : BaseService() {
@ -92,10 +87,8 @@ class WebService : BaseService() {
notificationContent = hostAddress notificationContent = hostAddress
upNotification() upNotification()
} catch (e: IOException) { } catch (e: IOException) {
launch { toastOnUi(e.localizedMessage ?: "")
toast(e.localizedMessage ?: "") stopSelf()
stopSelf()
}
} }
} else { } else {
stopSelf() stopSelf()

@ -3,10 +3,10 @@ package io.legado.app.service.help
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import io.legado.app.App
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.constant.Status import io.legado.app.constant.Status
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
@ -44,7 +44,7 @@ object AudioPlay {
} }
fun upDurChapter(book: Book) { fun upDurChapter(book: Book) {
durChapter = App.db.bookChapterDao.getChapter(book.bookUrl, durChapterIndex) durChapter = appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)
postEvent(EventBus.AUDIO_SUB_TITLE, durChapter?.title ?: "") postEvent(EventBus.AUDIO_SUB_TITLE, durChapter?.title ?: "")
postEvent(EventBus.AUDIO_SIZE, durChapter?.end?.toInt() ?: 0) postEvent(EventBus.AUDIO_SIZE, durChapter?.end?.toInt() ?: 0)
postEvent(EventBus.AUDIO_PROGRESS, durChapterPos) postEvent(EventBus.AUDIO_PROGRESS, durChapterPos)
@ -153,10 +153,10 @@ object AudioPlay {
book.durChapterTime = System.currentTimeMillis() book.durChapterTime = System.currentTimeMillis()
book.durChapterIndex = durChapterIndex book.durChapterIndex = durChapterIndex
book.durChapterPos = durChapterPos book.durChapterPos = durChapterPos
App.db.bookChapterDao.getChapter(book.bookUrl, book.durChapterIndex)?.let { appDb.bookChapterDao.getChapter(book.bookUrl, book.durChapterIndex)?.let {
book.durChapterTitle = it.title book.durChapterTitle = it.title
} }
App.db.bookDao.update(book) appDb.bookDao.update(book)
} }
} }
} }
@ -165,7 +165,7 @@ object AudioPlay {
Coroutine.async { Coroutine.async {
durChapter?.let { durChapter?.let {
it.end = audioSize it.end = audioSize
App.db.bookChapterDao.insert(it) appDb.bookChapterDao.insert(it)
} }
} }
} }

@ -2,7 +2,6 @@ package io.legado.app.service.help
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
@ -11,6 +10,7 @@ import io.legado.app.model.webBook.WebBook
import io.legado.app.service.CacheBookService import io.legado.app.service.CacheBookService
import io.legado.app.utils.msg import io.legado.app.utils.msg
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import splitties.init.appCtx
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.CopyOnWriteArraySet
@ -81,7 +81,7 @@ object CacheBook {
ReadBook.contentLoadFinish( ReadBook.contentLoadFinish(
book, book,
chapter, chapter,
content.ifBlank { App.INSTANCE.getString(R.string.content_empty) }, content.ifBlank { appCtx.getString(R.string.content_empty) },
resetPageOffset = resetPageOffset resetPageOffset = resetPageOffset
) )
} }

@ -6,14 +6,14 @@ import io.legado.app.R
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.service.CheckSourceService import io.legado.app.service.CheckSourceService
import org.jetbrains.anko.toast import io.legado.app.utils.toastOnUi
object CheckSource { object CheckSource {
var keyword = "我的" var keyword = "我的"
fun start(context: Context, sources: List<BookSource>) { fun start(context: Context, sources: List<BookSource>) {
if (sources.isEmpty()) { if (sources.isEmpty()) {
context.toast(R.string.non_select) context.toastOnUi(R.string.non_select)
return return
} }
val selectedIds: ArrayList<String> = arrayListOf() val selectedIds: ArrayList<String> = arrayListOf()

@ -2,22 +2,23 @@ package io.legado.app.service.help
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import io.legado.app.App
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.data.entities.HttpTTS import io.legado.app.data.entities.HttpTTS
import io.legado.app.service.BaseReadAloudService import io.legado.app.service.BaseReadAloudService
import io.legado.app.service.HttpReadAloudService import io.legado.app.service.HttpReadAloudService
import io.legado.app.service.TTSReadAloudService import io.legado.app.service.TTSReadAloudService
import io.legado.app.utils.getPrefLong import io.legado.app.utils.getPrefLong
import splitties.init.appCtx
object ReadAloud { object ReadAloud {
private var aloudClass: Class<*> = getReadAloudClass() private var aloudClass: Class<*> = getReadAloudClass()
var httpTTS: HttpTTS? = null var httpTTS: HttpTTS? = null
private fun getReadAloudClass(): Class<*> { private fun getReadAloudClass(): Class<*> {
val spId = App.INSTANCE.getPrefLong(PreferKey.speakEngine) val spId = appCtx.getPrefLong(PreferKey.speakEngine)
httpTTS = App.db.httpTTSDao.get(spId) httpTTS = appDb.httpTTSDao.get(spId)
return if (httpTTS != null) { return if (httpTTS != null) {
HttpReadAloudService::class.java HttpReadAloudService::class.java
} else { } else {
@ -26,7 +27,7 @@ object ReadAloud {
} }
fun upReadAloudClass() { fun upReadAloudClass() {
stop(App.INSTANCE) stop(appCtx)
aloudClass = getReadAloudClass() aloudClass = getReadAloudClass()
} }

@ -2,8 +2,8 @@ package io.legado.app.service.help
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.hankcs.hanlp.HanLP import com.hankcs.hanlp.HanLP
import io.legado.app.App
import io.legado.app.constant.BookType import io.legado.app.constant.BookType
import io.legado.app.data.appDb
import io.legado.app.data.entities.* import io.legado.app.data.entities.*
import io.legado.app.help.* import io.legado.app.help.*
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
@ -14,12 +14,13 @@ import io.legado.app.ui.book.read.page.entities.TextChapter
import io.legado.app.ui.book.read.page.entities.TextPage import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.ui.book.read.page.provider.ImageProvider import io.legado.app.ui.book.read.page.provider.ImageProvider
import io.legado.app.utils.msg
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.anko.getStackTraceString import splitties.init.appCtx
import org.jetbrains.anko.toast
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
@ -47,7 +48,7 @@ object ReadBook {
this.book = book this.book = book
contentProcessor = ContentProcessor(book.name, book.origin) contentProcessor = ContentProcessor(book.name, book.origin)
readRecord.bookName = book.name readRecord.bookName = book.name
readRecord.readTime = App.db.readRecordDao.getReadTime(book.name) ?: 0 readRecord.readTime = appDb.readRecordDao.getReadTime(book.name) ?: 0
durChapterIndex = book.durChapterIndex durChapterIndex = book.durChapterIndex
durChapterPos = book.durChapterPos durChapterPos = book.durChapterPos
isLocalBook = book.origin == BookType.local isLocalBook = book.origin == BookType.local
@ -67,7 +68,7 @@ object ReadBook {
bookSource = null bookSource = null
webBook = null webBook = null
} else { } else {
App.db.bookSourceDao.getBookSource(book.origin)?.let { appDb.bookSourceDao.getBookSource(book.origin)?.let {
bookSource = it bookSource = it
webBook = WebBook(it) webBook = WebBook(it)
} ?: let { } ?: let {
@ -102,7 +103,7 @@ object ReadBook {
Coroutine.async { Coroutine.async {
readRecord.readTime = readRecord.readTime + System.currentTimeMillis() - readStartTime readRecord.readTime = readRecord.readTime + System.currentTimeMillis() - readStartTime
readStartTime = System.currentTimeMillis() readStartTime = System.currentTimeMillis()
App.db.readRecordDao.insert(readRecord) appDb.readRecordDao.insert(readRecord)
} }
} }
@ -215,7 +216,7 @@ object ReadBook {
if (book != null && textChapter != null) { if (book != null && textChapter != null) {
val key = IntentDataHelp.putData(textChapter) val key = IntentDataHelp.putData(textChapter)
ReadAloud.play( ReadAloud.play(
App.INSTANCE, book.name, textChapter.title, durPageIndex(), key, play appCtx, book.name, textChapter.title, durPageIndex(), key, play
) )
} }
} }
@ -259,7 +260,7 @@ object ReadBook {
book?.let { book -> book?.let { book ->
if (addLoading(index)) { if (addLoading(index)) {
Coroutine.async { Coroutine.async {
App.db.bookChapterDao.getChapter(book.bookUrl, index)?.let { chapter -> appDb.bookChapterDao.getChapter(book.bookUrl, index)?.let { chapter ->
BookHelp.getContent(book, chapter)?.let { BookHelp.getContent(book, chapter)?.let {
contentLoadFinish(book, chapter, it, upContent, resetPageOffset) { contentLoadFinish(book, chapter, it, upContent, resetPageOffset) {
success?.invoke() success?.invoke()
@ -279,7 +280,7 @@ object ReadBook {
if (book.isLocalBook()) return if (book.isLocalBook()) return
if (addLoading(index)) { if (addLoading(index)) {
Coroutine.async { Coroutine.async {
App.db.bookChapterDao.getChapter(book.bookUrl, index)?.let { chapter -> appDb.bookChapterDao.getChapter(book.bookUrl, index)?.let { chapter ->
if (BookHelp.hasContent(book, chapter)) { if (BookHelp.hasContent(book, chapter)) {
removeLoading(chapter.index) removeLoading(chapter.index)
} else { } else {
@ -437,7 +438,7 @@ object ReadBook {
} }
}.onError { }.onError {
it.printStackTrace() it.printStackTrace()
App.INSTANCE.toast("ChapterProvider ERROR:\n${it.getStackTraceString()}") appCtx.toastOnUi("ChapterProvider ERROR:\n${it.msg}")
}.onSuccess { }.onSuccess {
success?.invoke() success?.invoke()
} }
@ -470,10 +471,10 @@ object ReadBook {
book.durChapterTime = System.currentTimeMillis() book.durChapterTime = System.currentTimeMillis()
book.durChapterIndex = durChapterIndex book.durChapterIndex = durChapterIndex
book.durChapterPos = durChapterPos book.durChapterPos = durChapterPos
App.db.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let { appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let {
book.durChapterTitle = it.title book.durChapterTitle = it.title
} }
App.db.bookDao.update(book) appDb.bookDao.update(book)
} }
} }
} }

@ -12,7 +12,7 @@ import io.legado.app.databinding.ActivityAboutBinding
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.accentColor import io.legado.app.lib.theme.accentColor
import io.legado.app.utils.openUrl import io.legado.app.utils.openUrl
import org.jetbrains.anko.share import io.legado.app.utils.share
class AboutActivity : BaseActivity<ActivityAboutBinding>() { class AboutActivity : BaseActivity<ActivityAboutBinding>() {

@ -7,15 +7,15 @@ import android.view.View
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.appInfo
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.ui.widget.dialog.TextDialog import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.utils.openUrl import io.legado.app.utils.openUrl
import io.legado.app.utils.sendMail import io.legado.app.utils.sendMail
import io.legado.app.utils.sendToClip import io.legado.app.utils.sendToClip
import io.legado.app.utils.toast import io.legado.app.utils.toastOnUi
class AboutFragment : PreferenceFragmentCompat() { class AboutFragment : PreferenceFragmentCompat() {
@ -39,7 +39,7 @@ class AboutFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.about) addPreferencesFromResource(R.xml.about)
findPreference<Preference>("update_log")?.summary = findPreference<Preference>("update_log")?.summary =
"${getString(R.string.version)} ${App.versionName}" "${getString(R.string.version)} ${appInfo.versionName}"
if (AppConfig.isGooglePlay) { if (AppConfig.isGooglePlay) {
preferenceScreen.removePreferenceRecursively("check_update") preferenceScreen.removePreferenceRecursively("check_update")
} }
@ -105,7 +105,7 @@ class AboutFragment : PreferenceFragmentCompat() {
startActivity(intent) startActivity(intent)
return true return true
}.onFailure { }.onFailure {
toast("添加失败,请手动添加") toastOnUi("添加失败,请手动添加")
} }
return false return false
} }

@ -8,9 +8,9 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import io.legado.app.R import io.legado.app.R
import io.legado.app.utils.ACache import io.legado.app.utils.ACache
import io.legado.app.utils.longToastOnUi
import io.legado.app.utils.openUrl import io.legado.app.utils.openUrl
import io.legado.app.utils.sendToClip import io.legado.app.utils.sendToClip
import org.jetbrains.anko.longToast
class DonateFragment : PreferenceFragmentCompat() { class DonateFragment : PreferenceFragmentCompat() {
@ -42,7 +42,7 @@ class DonateFragment : PreferenceFragmentCompat() {
private fun getZfbHb(context: Context) { private fun getZfbHb(context: Context) {
requireContext().sendToClip("537954522") requireContext().sendToClip("537954522")
context.longToast("高级功能已开启\n红包码已复制\n支付宝首页搜索“537954522” 立即领红包") context.longToastOnUi("高级功能已开启\n红包码已复制\n支付宝首页搜索“537954522” 立即领红包")
try { try {
val packageManager = context.applicationContext.packageManager val packageManager = context.applicationContext.packageManager
val intent = packageManager.getLaunchIntentForPackage("com.eg.android.AlipayGphone")!! val intent = packageManager.getLaunchIntentForPackage("com.eg.android.AlipayGphone")!!

@ -5,11 +5,11 @@ import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.ViewGroup import android.view.ViewGroup
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseActivity import io.legado.app.base.BaseActivity
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.data.appDb
import io.legado.app.data.entities.ReadRecordShow import io.legado.app.data.entities.ReadRecordShow
import io.legado.app.databinding.ActivityReadRecordBinding import io.legado.app.databinding.ActivityReadRecordBinding
import io.legado.app.databinding.ItemReadRecordBinding import io.legado.app.databinding.ItemReadRecordBinding
@ -19,7 +19,6 @@ import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.jetbrains.anko.sdk27.listeners.onClick
import java.util.* import java.util.*
class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() { class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
@ -59,10 +58,10 @@ class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
readRecord.tvBookName.setText(R.string.all_read_time) readRecord.tvBookName.setText(R.string.all_read_time)
adapter = RecordAdapter(this@ReadRecordActivity) adapter = RecordAdapter(this@ReadRecordActivity)
recyclerView.adapter = adapter recyclerView.adapter = adapter
readRecord.ivRemove.onClick { readRecord.tvRemove.setOnClickListener {
alert(R.string.delete, R.string.sure_del) { alert(R.string.delete, R.string.sure_del) {
okButton { okButton {
App.db.readRecordDao.clear() appDb.readRecordDao.clear()
initData() initData()
} }
noButton() noButton()
@ -72,11 +71,11 @@ class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
private fun initData() { private fun initData() {
launch(IO) { launch(IO) {
val allTime = App.db.readRecordDao.allTime val allTime = appDb.readRecordDao.allTime
withContext(Main) { withContext(Main) {
binding.readRecord.tvReadTime.text = formatDuring(allTime) binding.readRecord.tvReadTime.text = formatDuring(allTime)
} }
var readRecords = App.db.readRecordDao.allShow var readRecords = appDb.readRecordDao.allShow
readRecords = when (sortMode) { readRecords = when (sortMode) {
1 -> readRecords.sortedBy { it.readTime } 1 -> readRecords.sortedBy { it.readTime }
else -> { else -> {
@ -112,7 +111,7 @@ class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
override fun registerListener(holder: ItemViewHolder, binding: ItemReadRecordBinding) { override fun registerListener(holder: ItemViewHolder, binding: ItemReadRecordBinding) {
binding.apply { binding.apply {
ivRemove.onClick { tvRemove.setOnClickListener {
getItem(holder.layoutPosition)?.let { item -> getItem(holder.layoutPosition)?.let { item ->
sureDelAlert(item) sureDelAlert(item)
} }
@ -122,9 +121,9 @@ class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
private fun sureDelAlert(item: ReadRecordShow) { private fun sureDelAlert(item: ReadRecordShow) {
alert(R.string.delete) { alert(R.string.delete) {
message = getString(R.string.sure_del_any, item.bookName) setMessage(getString(R.string.sure_del_any, item.bookName))
okButton { okButton {
App.db.readRecordDao.deleteByName(item.bookName) appDb.readRecordDao.deleteByName(item.bookName)
initData() initData()
} }
noButton() noButton()
@ -142,7 +141,11 @@ class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
val h = if (hours > 0) "${hours}小时" else "" val h = if (hours > 0) "${hours}小时" else ""
val m = if (minutes > 0) "${minutes}分钟" else "" val m = if (minutes > 0) "${minutes}分钟" else ""
val s = if (seconds > 0) "${seconds}" else "" val s = if (seconds > 0) "${seconds}" else ""
return "$d$h$m$s" var time = "$d$h$m$s"
if (time.isBlank()) {
time = "0秒"
}
return time
} }
} }

@ -2,14 +2,14 @@ package io.legado.app.ui.association
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import androidx.activity.viewModels
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.Theme import io.legado.app.constant.Theme
import io.legado.app.databinding.ActivityTranslucenceBinding import io.legado.app.databinding.ActivityTranslucenceBinding
import io.legado.app.ui.main.MainActivity import io.legado.app.ui.main.MainActivity
import io.legado.app.utils.getViewModel import io.legado.app.utils.startActivity
import org.jetbrains.anko.startActivity
import org.jetbrains.anko.toast
import io.legado.app.utils.toastOnUi
class FileAssociationActivity : class FileAssociationActivity :
VMBaseActivity<ActivityTranslucenceBinding, FileAssociationViewModel>( VMBaseActivity<ActivityTranslucenceBinding, FileAssociationViewModel>(
@ -20,14 +20,13 @@ class FileAssociationActivity :
return ActivityTranslucenceBinding.inflate(layoutInflater) return ActivityTranslucenceBinding.inflate(layoutInflater)
} }
override val viewModel: FileAssociationViewModel override val viewModel: FileAssociationViewModel by viewModels()
get() = getViewModel(FileAssociationViewModel::class.java)
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
binding.rotateLoading.show() binding.rotateLoading.show()
viewModel.errorLiveData.observe(this, { viewModel.errorLiveData.observe(this, {
binding.rotateLoading.hide() binding.rotateLoading.hide()
toast(it) toastOnUi(it)
finish() finish()
}) })
viewModel.successLiveData.observe(this, { viewModel.successLiveData.observe(this, {

@ -1,14 +1,15 @@
package io.legado.app.ui.association package io.legado.app.ui.association
import android.os.Bundle import android.os.Bundle
import androidx.activity.viewModels
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.Theme import io.legado.app.constant.Theme
import io.legado.app.databinding.ActivityTranslucenceBinding import io.legado.app.databinding.ActivityTranslucenceBinding
import io.legado.app.help.IntentDataHelp import io.legado.app.help.IntentDataHelp
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.utils.getViewModel
import org.jetbrains.anko.toast import io.legado.app.utils.toastOnUi
class ImportBookSourceActivity : class ImportBookSourceActivity :
@ -21,7 +22,7 @@ class ImportBookSourceActivity :
} }
override val viewModel: ImportBookSourceViewModel override val viewModel: ImportBookSourceViewModel
get() = getViewModel(ImportBookSourceViewModel::class.java) by viewModels()
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
binding.rotateLoading.show() binding.rotateLoading.show()
@ -66,7 +67,7 @@ class ImportBookSourceActivity :
} }
else -> { else -> {
binding.rotateLoading.hide() binding.rotateLoading.hide()
toast(R.string.wrong_format) toastOnUi(R.string.wrong_format)
finish() finish()
} }
} }

@ -9,23 +9,27 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseDialogFragment import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.databinding.DialogEditTextBinding import io.legado.app.databinding.DialogEditTextBinding
import io.legado.app.databinding.DialogRecyclerViewBinding import io.legado.app.databinding.DialogRecyclerViewBinding
import io.legado.app.databinding.ItemSourceImportBinding import io.legado.app.databinding.ItemSourceImportBinding
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.utils.* import io.legado.app.utils.dp
import io.legado.app.utils.putPrefBoolean
import io.legado.app.utils.splitNotBlank
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
import org.jetbrains.anko.sdk27.listeners.onClick import io.legado.app.utils.visible
/** /**
* 导入书源弹出窗口 * 导入书源弹出窗口
@ -34,8 +38,7 @@ class ImportBookSourceDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickList
private val binding by viewBinding(DialogRecyclerViewBinding::bind) private val binding by viewBinding(DialogRecyclerViewBinding::bind)
val viewModel: ImportBookSourceViewModel val viewModel: ImportBookSourceViewModel by viewModels()
get() = getViewModelOfActivity(ImportBookSourceViewModel::class.java)
lateinit var adapter: SourcesAdapter lateinit var adapter: SourcesAdapter
override fun onStart() { override fun onStart() {
@ -62,11 +65,11 @@ class ImportBookSourceDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickList
binding.recyclerView.adapter = adapter binding.recyclerView.adapter = adapter
adapter.setItems(viewModel.allSources) adapter.setItems(viewModel.allSources)
binding.tvCancel.visible() binding.tvCancel.visible()
binding.tvCancel.onClick { binding.tvCancel.setOnClickListener {
dismiss() dismiss()
} }
binding.tvOk.visible() binding.tvOk.visible()
binding.tvOk.onClick { binding.tvOk.setOnClickListener {
viewModel.importSelect { viewModel.importSelect {
dismiss() dismiss()
} }
@ -87,13 +90,15 @@ class ImportBookSourceDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickList
alert(R.string.diy_edit_source_group) { alert(R.string.diy_edit_source_group) {
val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply { val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
val groups = linkedSetOf<String>() val groups = linkedSetOf<String>()
App.db.bookSourceDao.allGroup.forEach { group -> appDb.bookSourceDao.allGroup.forEach { group ->
groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex))
} }
editView.setFilterValues(groups.toList()) editView.setFilterValues(groups.toList())
editView.dropDownHeight = 180.dp editView.dropDownHeight = 180.dp
} }
customView = alertBinding.root customView {
alertBinding.root
}
okButton { okButton {
alertBinding.editView.text?.toString()?.let { group -> alertBinding.editView.text?.toString()?.let { group ->
viewModel.groupName = group viewModel.groupName = group

@ -5,9 +5,9 @@ import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.jayway.jsonpath.JsonPath import com.jayway.jsonpath.JsonPath
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.SourceHelp import io.legado.app.help.SourceHelp
@ -134,7 +134,7 @@ class ImportBookSourceViewModel(app: Application) : BaseViewModel(app) {
private fun comparisonSource() { private fun comparisonSource() {
execute { execute {
allSources.forEach { allSources.forEach {
val has = App.db.bookSourceDao.getBookSource(it.bookSourceUrl) val has = appDb.bookSourceDao.getBookSource(it.bookSourceUrl)
checkSources.add(has) checkSources.add(has)
selectStatus.add(has == null) selectStatus.add(has == null)
} }

@ -1,15 +1,15 @@
package io.legado.app.ui.association package io.legado.app.ui.association
import android.os.Bundle import android.os.Bundle
import io.legado.app.App import androidx.activity.viewModels
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.Theme import io.legado.app.constant.Theme
import io.legado.app.data.appDb
import io.legado.app.data.entities.ReplaceRule import io.legado.app.data.entities.ReplaceRule
import io.legado.app.databinding.ActivityTranslucenceBinding import io.legado.app.databinding.ActivityTranslucenceBinding
import io.legado.app.help.IntentDataHelp import io.legado.app.help.IntentDataHelp
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.utils.getViewModel import io.legado.app.utils.toastOnUi
import org.jetbrains.anko.toast
class ImportReplaceRuleActivity : class ImportReplaceRuleActivity :
VMBaseActivity<ActivityTranslucenceBinding, ImportReplaceRuleViewModel>( VMBaseActivity<ActivityTranslucenceBinding, ImportReplaceRuleViewModel>(
@ -21,7 +21,7 @@ class ImportReplaceRuleActivity :
} }
override val viewModel: ImportReplaceRuleViewModel override val viewModel: ImportReplaceRuleViewModel
get() = getViewModel(ImportReplaceRuleViewModel::class.java) by viewModels()
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
binding.rotateLoading.show() binding.rotateLoading.show()
@ -66,7 +66,7 @@ class ImportReplaceRuleActivity :
} }
else -> { else -> {
binding.rotateLoading.hide() binding.rotateLoading.hide()
toast("格式不对") toastOnUi("格式不对")
finish() finish()
} }
} }
@ -85,7 +85,7 @@ class ImportReplaceRuleActivity :
private fun successDialog(allSource: ArrayList<ReplaceRule>) { private fun successDialog(allSource: ArrayList<ReplaceRule>) {
alert("解析结果", "${allSource.size}个替换规则,是否确认导入?") { alert("解析结果", "${allSource.size}个替换规则,是否确认导入?") {
okButton { okButton {
App.db.replaceRuleDao.insert(*allSource.toTypedArray()) appDb.replaceRuleDao.insert(*allSource.toTypedArray())
} }
noButton() noButton()
onDismiss { onDismiss {

@ -1,14 +1,15 @@
package io.legado.app.ui.association package io.legado.app.ui.association
import android.os.Bundle import android.os.Bundle
import androidx.activity.viewModels
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.Theme import io.legado.app.constant.Theme
import io.legado.app.databinding.ActivityTranslucenceBinding import io.legado.app.databinding.ActivityTranslucenceBinding
import io.legado.app.help.IntentDataHelp import io.legado.app.help.IntentDataHelp
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.utils.getViewModel
import org.jetbrains.anko.toast import io.legado.app.utils.toastOnUi
class ImportRssSourceActivity : class ImportRssSourceActivity :
VMBaseActivity<ActivityTranslucenceBinding, ImportRssSourceViewModel>( VMBaseActivity<ActivityTranslucenceBinding, ImportRssSourceViewModel>(
@ -20,7 +21,7 @@ class ImportRssSourceActivity :
} }
override val viewModel: ImportRssSourceViewModel override val viewModel: ImportRssSourceViewModel
get() = getViewModel(ImportRssSourceViewModel::class.java) by viewModels()
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
binding.rotateLoading.show() binding.rotateLoading.show()
@ -65,7 +66,7 @@ class ImportRssSourceActivity :
} }
else -> { else -> {
binding.rotateLoading.hide() binding.rotateLoading.hide()
toast(R.string.wrong_format) toastOnUi(R.string.wrong_format)
finish() finish()
} }
} }

@ -9,23 +9,26 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseDialogFragment import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.databinding.DialogEditTextBinding import io.legado.app.databinding.DialogEditTextBinding
import io.legado.app.databinding.DialogRecyclerViewBinding import io.legado.app.databinding.DialogRecyclerViewBinding
import io.legado.app.databinding.ItemSourceImportBinding import io.legado.app.databinding.ItemSourceImportBinding
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.lib.dialogs.alert import io.legado.app.lib.dialogs.alert
import io.legado.app.utils.* import io.legado.app.utils.dp
import io.legado.app.utils.putPrefBoolean
import io.legado.app.utils.splitNotBlank
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
import org.jetbrains.anko.sdk27.listeners.onClick import io.legado.app.utils.visible
/** /**
* 导入rss源弹出窗口 * 导入rss源弹出窗口
@ -35,7 +38,7 @@ class ImportRssSourceDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListe
private val binding by viewBinding(DialogRecyclerViewBinding::bind) private val binding by viewBinding(DialogRecyclerViewBinding::bind)
val viewModel: ImportRssSourceViewModel val viewModel: ImportRssSourceViewModel
get() = getViewModelOfActivity(ImportRssSourceViewModel::class.java) by activityViewModels()
lateinit var adapter: SourcesAdapter lateinit var adapter: SourcesAdapter
override fun onStart() { override fun onStart() {
@ -62,11 +65,11 @@ class ImportRssSourceDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListe
binding.recyclerView.adapter = adapter binding.recyclerView.adapter = adapter
adapter.setItems(viewModel.allSources) adapter.setItems(viewModel.allSources)
binding.tvCancel.visible() binding.tvCancel.visible()
binding.tvCancel.onClick { binding.tvCancel.setOnClickListener {
dismiss() dismiss()
} }
binding.tvOk.visible() binding.tvOk.visible()
binding.tvOk.onClick { binding.tvOk.setOnClickListener {
viewModel.importSelect { viewModel.importSelect {
dismiss() dismiss()
} }
@ -87,13 +90,15 @@ class ImportRssSourceDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListe
alert(R.string.diy_edit_source_group) { alert(R.string.diy_edit_source_group) {
val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply { val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
val groups = linkedSetOf<String>() val groups = linkedSetOf<String>()
App.db.rssSourceDao.allGroup.forEach { group -> appDb.rssSourceDao.allGroup.forEach { group ->
groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex))
} }
editView.setFilterValues(groups.toList()) editView.setFilterValues(groups.toList())
editView.dropDownHeight = 180.dp editView.dropDownHeight = 180.dp
} }
customView = alertBinding.root customView {
alertBinding.root
}
okButton { okButton {
alertBinding.editView.text?.toString()?.let { group -> alertBinding.editView.text?.toString()?.let { group ->
viewModel.groupName = group viewModel.groupName = group

@ -5,9 +5,9 @@ import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import com.jayway.jsonpath.JsonPath import com.jayway.jsonpath.JsonPath
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.data.appDb
import io.legado.app.data.entities.RssSource import io.legado.app.data.entities.RssSource
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.SourceHelp import io.legado.app.help.SourceHelp
@ -129,7 +129,7 @@ class ImportRssSourceViewModel(app: Application) : BaseViewModel(app) {
private fun comparisonSource() { private fun comparisonSource() {
execute { execute {
allSources.forEach { allSources.forEach {
val has = App.db.rssSourceDao.getByKey(it.sourceUrl) val has = appDb.rssSourceDao.getByKey(it.sourceUrl)
checkSources.add(has) checkSources.add(has)
selectStatus.add(has == null) selectStatus.add(has == null)
} }

@ -9,6 +9,7 @@ import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.widget.SeekBar import android.widget.SeekBar
import androidx.activity.viewModels
import com.bumptech.glide.RequestBuilder import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestOptions.bitmapTransform import com.bumptech.glide.request.RequestOptions.bitmapTransform
@ -28,9 +29,7 @@ import io.legado.app.ui.book.toc.ChapterListActivity
import io.legado.app.ui.widget.image.CoverImageView import io.legado.app.ui.widget.image.CoverImageView
import io.legado.app.ui.widget.seekbar.SeekBarChangeListener import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
import io.legado.app.utils.* import io.legado.app.utils.*
import org.jetbrains.anko.sdk27.listeners.onClick import splitties.views.onLongClick
import org.jetbrains.anko.sdk27.listeners.onLongClick
import org.jetbrains.anko.startActivityForResult
import java.util.* import java.util.*
/** /**
@ -41,7 +40,7 @@ class AudioPlayActivity :
ChangeSourceDialog.CallBack { ChangeSourceDialog.CallBack {
override val viewModel: AudioPlayViewModel override val viewModel: AudioPlayViewModel
get() = getViewModel(AudioPlayViewModel::class.java) by viewModels()
private var requestCodeChapter = 8461 private var requestCodeChapter = 8461
private var adjustProgress = false private var adjustProgress = false
@ -80,18 +79,17 @@ class AudioPlayActivity :
} }
private fun initView() { private fun initView() {
binding.fabPlayStop.onClick { binding.fabPlayStop.setOnClickListener {
playButton() playButton()
} }
binding.fabPlayStop.onLongClick { binding.fabPlayStop.onLongClick {
AudioPlay.stop(this) AudioPlay.stop(this@AudioPlayActivity)
true
} }
binding.ivSkipNext.onClick { binding.ivSkipNext.setOnClickListener {
AudioPlay.next(this) AudioPlay.next(this@AudioPlayActivity)
} }
binding.ivSkipPrevious.onClick { binding.ivSkipPrevious.setOnClickListener {
AudioPlay.prev(this) AudioPlay.prev(this@AudioPlayActivity)
} }
binding.playerProgress.setOnSeekBarChangeListener(object : SeekBarChangeListener { binding.playerProgress.setOnSeekBarChangeListener(object : SeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
@ -107,26 +105,25 @@ class AudioPlayActivity :
AudioPlay.adjustProgress(this@AudioPlayActivity, seekBar.progress) AudioPlay.adjustProgress(this@AudioPlayActivity, seekBar.progress)
} }
}) })
binding.ivChapter.onClick { binding.ivChapter.setOnClickListener {
AudioPlay.book?.let { AudioPlay.book?.let {
startActivityForResult<ChapterListActivity>( startActivityForResult<ChapterListActivity>(requestCodeChapter) {
requestCodeChapter, putExtra("bookUrl", it.bookUrl)
Pair("bookUrl", it.bookUrl) }
)
} }
} }
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
binding.ivFastRewind.invisible() binding.ivFastRewind.invisible()
binding.ivFastForward.invisible() binding.ivFastForward.invisible()
} }
binding.ivFastForward.onClick { binding.ivFastForward.setOnClickListener {
AudioPlay.adjustSpeed(this, 0.1f) AudioPlay.adjustSpeed(this@AudioPlayActivity, 0.1f)
} }
binding.ivFastRewind.onClick { binding.ivFastRewind.setOnClickListener {
AudioPlay.adjustSpeed(this, -0.1f) AudioPlay.adjustSpeed(this@AudioPlayActivity, -0.1f)
} }
binding.ivTimer.onClick { binding.ivTimer.setOnClickListener {
AudioPlay.addTimer(this) AudioPlay.addTimer(this@AudioPlayActivity)
} }
} }
@ -166,7 +163,7 @@ class AudioPlayActivity :
AudioPlay.book?.let { AudioPlay.book?.let {
if (!AudioPlay.inBookshelf) { if (!AudioPlay.inBookshelf) {
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) setMessage(getString(R.string.check_add_bookshelf, it.name))
okButton { okButton {
AudioPlay.inBookshelf = true AudioPlay.inBookshelf = true
setResult(Activity.RESULT_OK) setResult(Activity.RESULT_OK)

@ -2,9 +2,9 @@ package io.legado.app.ui.audio
import android.app.Application import android.app.Application
import android.content.Intent import android.content.Intent
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
@ -21,18 +21,18 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
stop(context) stop(context)
inBookshelf = intent.getBooleanExtra("inBookshelf", true) inBookshelf = intent.getBooleanExtra("inBookshelf", true)
book = if (!bookUrl.isNullOrEmpty()) { book = if (!bookUrl.isNullOrEmpty()) {
App.db.bookDao.getBook(bookUrl) appDb.bookDao.getBook(bookUrl)
} else { } else {
App.db.bookDao.lastReadBook appDb.bookDao.lastReadBook
} }
book?.let { book -> book?.let { book ->
titleData.postValue(book.name) titleData.postValue(book.name)
coverData.postValue(book.getDisplayCover()) coverData.postValue(book.getDisplayCover())
durChapterIndex = book.durChapterIndex durChapterIndex = book.durChapterIndex
durChapterPos = book.durChapterPos durChapterPos = book.durChapterPos
durChapter = App.db.bookChapterDao.getChapter(book.bookUrl, durChapterIndex) durChapter = appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)
upDurChapter(book) upDurChapter(book)
App.db.bookSourceDao.getBookSource(book.origin)?.let { appDb.bookSourceDao.getBookSource(book.origin)?.let {
webBook = WebBook(it) webBook = WebBook(it)
} }
if (durChapter == null) { if (durChapter == null) {
@ -69,16 +69,16 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
?.onSuccess(Dispatchers.IO) { cList -> ?.onSuccess(Dispatchers.IO) { cList ->
if (cList.isNotEmpty()) { if (cList.isNotEmpty()) {
if (changeDruChapterIndex == null) { if (changeDruChapterIndex == null) {
App.db.bookChapterDao.insert(*cList.toTypedArray()) appDb.bookChapterDao.insert(*cList.toTypedArray())
} else { } else {
changeDruChapterIndex(cList) changeDruChapterIndex(cList)
} }
AudioPlay.upDurChapter(book) AudioPlay.upDurChapter(book)
} else { } else {
toast(R.string.error_load_toc) toastOnUi(R.string.error_load_toc)
} }
}?.onError { }?.onError {
toast(R.string.error_load_toc) toastOnUi(R.string.error_load_toc)
} }
} }
} }
@ -89,11 +89,11 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
AudioPlay.book?.let { AudioPlay.book?.let {
oldTocSize = it.totalChapterNum oldTocSize = it.totalChapterNum
book1.order = it.order book1.order = it.order
App.db.bookDao.delete(it) appDb.bookDao.delete(it)
} }
App.db.bookDao.insert(book1) appDb.bookDao.insert(book1)
AudioPlay.book = book1 AudioPlay.book = book1
App.db.bookSourceDao.getBookSource(book1.origin)?.let { appDb.bookSourceDao.getBookSource(book1.origin)?.let {
AudioPlay.webBook = WebBook(it) AudioPlay.webBook = WebBook(it)
} }
if (book1.tocUrl.isEmpty()) { if (book1.tocUrl.isEmpty()) {
@ -118,15 +118,15 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
) )
book.durChapterIndex = AudioPlay.durChapterIndex book.durChapterIndex = AudioPlay.durChapterIndex
book.durChapterTitle = chapters[AudioPlay.durChapterIndex].title book.durChapterTitle = chapters[AudioPlay.durChapterIndex].title
App.db.bookDao.update(book) appDb.bookDao.update(book)
App.db.bookChapterDao.insert(*chapters.toTypedArray()) appDb.bookChapterDao.insert(*chapters.toTypedArray())
} }
} }
fun removeFromBookshelf(success: (() -> Unit)?) { fun removeFromBookshelf(success: (() -> Unit)?) {
execute { execute {
AudioPlay.book?.let { AudioPlay.book?.let {
App.db.bookDao.delete(it) appDb.bookDao.delete(it)
} }
}.onSuccess { }.onSuccess {
success?.invoke() success?.invoke()

@ -3,15 +3,16 @@ package io.legado.app.ui.book.arrange
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.activity.viewModels
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
import io.legado.app.databinding.ActivityArrangeBookBinding import io.legado.app.databinding.ActivityArrangeBookBinding
@ -25,7 +26,6 @@ import io.legado.app.ui.widget.recycler.ItemTouchCallback
import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.cnCompare import io.legado.app.utils.cnCompare
import io.legado.app.utils.getPrefInt import io.legado.app.utils.getPrefInt
import io.legado.app.utils.getViewModel
class ArrangeBookActivity : VMBaseActivity<ActivityArrangeBookBinding, ArrangeBookViewModel>(), class ArrangeBookActivity : VMBaseActivity<ActivityArrangeBookBinding, ArrangeBookViewModel>(),
@ -34,7 +34,7 @@ class ArrangeBookActivity : VMBaseActivity<ActivityArrangeBookBinding, ArrangeBo
ArrangeBookAdapter.CallBack, ArrangeBookAdapter.CallBack,
GroupSelectDialog.CallBack { GroupSelectDialog.CallBack {
override val viewModel: ArrangeBookViewModel override val viewModel: ArrangeBookViewModel
get() = getViewModel(ArrangeBookViewModel::class.java) by viewModels()
override val groupList: ArrayList<BookGroup> = arrayListOf() override val groupList: ArrayList<BookGroup> = arrayListOf()
private val groupRequestCode = 22 private val groupRequestCode = 22
private val addToGroupRequestCode = 34 private val addToGroupRequestCode = 34
@ -102,7 +102,7 @@ class ArrangeBookActivity : VMBaseActivity<ActivityArrangeBookBinding, ArrangeBo
private fun initGroupData() { private fun initGroupData() {
groupLiveData?.removeObservers(this) groupLiveData?.removeObservers(this)
groupLiveData = App.db.bookGroupDao.liveDataAll() groupLiveData = appDb.bookGroupDao.liveDataAll()
groupLiveData?.observe(this, { groupLiveData?.observe(this, {
groupList.clear() groupList.clear()
groupList.addAll(it) groupList.addAll(it)
@ -115,11 +115,11 @@ class ArrangeBookActivity : VMBaseActivity<ActivityArrangeBookBinding, ArrangeBo
booksLiveData?.removeObservers(this) booksLiveData?.removeObservers(this)
booksLiveData = booksLiveData =
when (groupId) { when (groupId) {
AppConst.bookGroupAllId -> App.db.bookDao.observeAll() AppConst.bookGroupAllId -> appDb.bookDao.observeAll()
AppConst.bookGroupLocalId -> App.db.bookDao.observeLocal() AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal()
AppConst.bookGroupAudioId -> App.db.bookDao.observeAudio() AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio()
AppConst.bookGroupNoneId -> App.db.bookDao.observeNoGroup() AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup()
else -> App.db.bookDao.observeByGroup(groupId) else -> appDb.bookDao.observeByGroup(groupId)
} }
booksLiveData?.observe(this, { list -> booksLiveData?.observe(this, { list ->
val books = when (getPrefInt(PreferKey.bookshelfSort)) { val books = when (getPrefInt(PreferKey.bookshelfSort)) {
@ -140,7 +140,7 @@ class ArrangeBookActivity : VMBaseActivity<ActivityArrangeBookBinding, ArrangeBo
.show(supportFragmentManager, "groupManage") .show(supportFragmentManager, "groupManage")
else -> if (item.groupId == R.id.menu_group) { else -> if (item.groupId == R.id.menu_group) {
binding.titleBar.subtitle = item.title binding.titleBar.subtitle = item.title
groupId = App.db.bookGroupDao.getByName(item.title.toString())?.groupId ?: 0 groupId = appDb.bookGroupDao.getByName(item.title.toString())?.groupId ?: 0
initBookData() initBookData()
} }
} }

@ -13,8 +13,6 @@ import io.legado.app.databinding.ItemArrangeBookBinding
import io.legado.app.lib.theme.backgroundColor import io.legado.app.lib.theme.backgroundColor
import io.legado.app.ui.widget.recycler.DragSelectTouchHelper import io.legado.app.ui.widget.recycler.DragSelectTouchHelper
import io.legado.app.ui.widget.recycler.ItemTouchCallback import io.legado.app.ui.widget.recycler.ItemTouchCallback
import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.sdk27.listeners.onClick
import java.util.* import java.util.*
class ArrangeBookAdapter(context: Context, val callBack: CallBack) : class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
@ -40,7 +38,7 @@ class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
payloads: MutableList<Any> payloads: MutableList<Any>
) { ) {
binding.apply { binding.apply {
root.backgroundColor = context.backgroundColor root.setBackgroundColor(context.backgroundColor)
tvName.text = item.name tvName.text = item.name
tvAuthor.text = item.author tvAuthor.text = item.author
tvAuthor.visibility = if (item.author.isEmpty()) View.GONE else View.VISIBLE tvAuthor.visibility = if (item.author.isEmpty()) View.GONE else View.VISIBLE
@ -65,7 +63,7 @@ class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
} }
} }
} }
root.onClick { root.setOnClickListener {
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
checkbox.isChecked = !checkbox.isChecked checkbox.isChecked = !checkbox.isChecked
if (checkbox.isChecked) { if (checkbox.isChecked) {
@ -76,12 +74,12 @@ class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
callBack.upSelectCount() callBack.upSelectCount()
} }
} }
tvDelete.onClick { tvDelete.setOnClickListener {
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
callBack.deleteBook(it) callBack.deleteBook(it)
} }
} }
tvGroup.onClick { tvGroup.setOnClickListener {
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
actionItem = it actionItem = it
callBack.selectGroup(groupRequestCode, it.group) callBack.selectGroup(groupRequestCode, it.group)

@ -1,8 +1,8 @@
package io.legado.app.ui.book.arrange package io.legado.app.ui.book.arrange
import android.app.Application import android.app.Application
import io.legado.app.App
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
@ -13,19 +13,19 @@ class ArrangeBookViewModel(application: Application) : BaseViewModel(application
books.forEach { books.forEach {
it.canUpdate = canUpdate it.canUpdate = canUpdate
} }
App.db.bookDao.update(*books) appDb.bookDao.update(*books)
} }
} }
fun updateBook(vararg book: Book) { fun updateBook(vararg book: Book) {
execute { execute {
App.db.bookDao.update(*book) appDb.bookDao.update(*book)
} }
} }
fun deleteBook(vararg book: Book) { fun deleteBook(vararg book: Book) {
execute { execute {
App.db.bookDao.delete(*book) appDb.bookDao.delete(*book)
} }
} }

@ -5,15 +5,16 @@ import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.activity.viewModels
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookGroup import io.legado.app.data.entities.BookGroup
@ -46,7 +47,7 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
private var groupId: Long = -1 private var groupId: Long = -1
override val viewModel: CacheViewModel override val viewModel: CacheViewModel
get() = getViewModel(CacheViewModel::class.java) by viewModels()
override fun getViewBinding(): ActivityCacheBookBinding { override fun getViewBinding(): ActivityCacheBookBinding {
return ActivityCacheBookBinding.inflate(layoutInflater) return ActivityCacheBookBinding.inflate(layoutInflater)
@ -101,7 +102,7 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
} }
else -> if (item.groupId == R.id.menu_group) { else -> if (item.groupId == R.id.menu_group) {
binding.titleBar.subtitle = item.title binding.titleBar.subtitle = item.title
groupId = App.db.bookGroupDao.getByName(item.title.toString())?.groupId ?: 0 groupId = appDb.bookGroupDao.getByName(item.title.toString())?.groupId ?: 0
initBookData() initBookData()
} }
} }
@ -117,11 +118,11 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
private fun initBookData() { private fun initBookData() {
booksLiveData?.removeObservers(this) booksLiveData?.removeObservers(this)
booksLiveData = when (groupId) { booksLiveData = when (groupId) {
AppConst.bookGroupAllId -> App.db.bookDao.observeAll() AppConst.bookGroupAllId -> appDb.bookDao.observeAll()
AppConst.bookGroupLocalId -> App.db.bookDao.observeLocal() AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal()
AppConst.bookGroupAudioId -> App.db.bookDao.observeAudio() AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio()
AppConst.bookGroupNoneId -> App.db.bookDao.observeNoGroup() AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup()
else -> App.db.bookDao.observeByGroup(groupId) else -> appDb.bookDao.observeByGroup(groupId)
} }
booksLiveData?.observe(this, { list -> booksLiveData?.observe(this, { list ->
val booksDownload = list.filter { val booksDownload = list.filter {
@ -142,7 +143,7 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
private fun initGroupData() { private fun initGroupData() {
groupLiveData?.removeObservers(this) groupLiveData?.removeObservers(this)
groupLiveData = App.db.bookGroupDao.liveDataAll() groupLiveData = appDb.bookGroupDao.liveDataAll()
groupLiveData?.observe(this, { groupLiveData?.observe(this, {
groupList.clear() groupList.clear()
groupList.addAll(it) groupList.addAll(it)
@ -156,7 +157,7 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
books.forEach { book -> books.forEach { book ->
val chapterCaches = hashSetOf<String>() val chapterCaches = hashSetOf<String>()
val cacheNames = BookHelp.getChapterFiles(book) val cacheNames = BookHelp.getChapterFiles(book)
App.db.bookChapterDao.getChapterList(book.bookUrl).forEach { chapter -> appDb.bookChapterDao.getChapterList(book.bookUrl).forEach { chapter ->
if (cacheNames.contains(chapter.getFileName())) { if (cacheNames.contains(chapter.getFileName())) {
chapterCaches.add(chapter.url) chapterCaches.add(chapter.url)
} }

@ -10,7 +10,7 @@ import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.databinding.ItemDownloadBinding import io.legado.app.databinding.ItemDownloadBinding
import io.legado.app.service.help.CacheBook import io.legado.app.service.help.CacheBook
import org.jetbrains.anko.sdk27.listeners.onClick
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.CopyOnWriteArraySet
@ -54,7 +54,7 @@ class CacheAdapter(context: Context, private val callBack: CallBack) :
override fun registerListener(holder: ItemViewHolder, binding: ItemDownloadBinding) { override fun registerListener(holder: ItemViewHolder, binding: ItemDownloadBinding) {
with(binding) { with(binding) {
ivDownload.onClick { ivDownload.setOnClickListener {
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
if (downloadMap?.containsKey(it.bookUrl) == true) { if (downloadMap?.containsKey(it.bookUrl) == true) {
CacheBook.remove(context, it.bookUrl) CacheBook.remove(context, it.bookUrl)
@ -63,7 +63,7 @@ class CacheAdapter(context: Context, private val callBack: CallBack) :
} }
} }
} }
tvExport.onClick { tvExport.setOnClickListener {
callBack.export(holder.layoutPosition) callBack.export(holder.layoutPosition)
} }
} }

@ -3,16 +3,17 @@ package io.legado.app.ui.book.cache
import android.app.Application import android.app.Application
import android.net.Uri import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.ContentProcessor import io.legado.app.help.ContentProcessor
import io.legado.app.help.storage.BookWebDav import io.legado.app.help.storage.BookWebDav
import io.legado.app.utils.* import io.legado.app.utils.*
import splitties.init.appCtx
import java.io.File import java.io.File
@ -41,7 +42,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
val content = getAllContents(book) val content = getAllContents(book)
DocumentUtils.createFileIfNotExist(doc, filename) DocumentUtils.createFileIfNotExist(doc, filename)
?.writeText(context, content) ?.writeText(context, content)
if (App.INSTANCE.getPrefBoolean(PreferKey.webDavCacheBackup, false)) { if (appCtx.getPrefBoolean(PreferKey.webDavCacheBackup, false)) {
FileUtils.createFileIfNotExist( FileUtils.createFileIfNotExist(
File(FileUtils.getCachePath()), File(FileUtils.getCachePath()),
filename filename
@ -67,7 +68,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
val filename = "${book.name} by ${book.author}.txt" val filename = "${book.name} by ${book.author}.txt"
FileUtils.createFileIfNotExist(file, filename) FileUtils.createFileIfNotExist(file, filename)
.writeText(getAllContents(book)) .writeText(getAllContents(book))
if (App.INSTANCE.getPrefBoolean(PreferKey.webDavCacheBackup, false)) { if (appCtx.getPrefBoolean(PreferKey.webDavCacheBackup, false)) {
BookWebDav.exportWebDav(file.absolutePath, filename) // 导出到webdav BookWebDav.exportWebDav(file.absolutePath, filename) // 导出到webdav
} }
getSrcList(book).forEach { getSrcList(book).forEach {
@ -90,7 +91,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
stringBuilder.append(book.name) stringBuilder.append(book.name)
.append("\n") .append("\n")
.append(context.getString(R.string.author_show, book.author)) .append(context.getString(R.string.author_show, book.author))
App.db.bookChapterDao.getChapterList(book.bookUrl).forEach { chapter -> appDb.bookChapterDao.getChapterList(book.bookUrl).forEach { chapter ->
BookHelp.getContent(book, chapter).let { content -> BookHelp.getContent(book, chapter).let { content ->
val content1 = contentProcessor val content1 = contentProcessor
.getContent(book, chapter.title, content ?: "null", false) .getContent(book, chapter.title, content ?: "null", false)
@ -104,7 +105,7 @@ class CacheViewModel(application: Application) : BaseViewModel(application) {
private fun getSrcList(book: Book): ArrayList<Triple<String, Int, String>> { private fun getSrcList(book: Book): ArrayList<Triple<String, Int, String>> {
val srcList = arrayListOf<Triple<String, Int, String>>() val srcList = arrayListOf<Triple<String, Int, String>>()
App.db.bookChapterDao.getChapterList(book.bookUrl).forEach { chapter -> appDb.bookChapterDao.getChapterList(book.bookUrl).forEach { chapter ->
BookHelp.getContent(book, chapter)?.let { content -> BookHelp.getContent(book, chapter)?.let { content ->
content.split("\n").forEachIndexed { index, text -> content.split("\n").forEachIndexed { index, text ->
val matcher = AppPattern.imgPattern.matcher(text) val matcher = AppPattern.imgPattern.matcher(text)

@ -7,6 +7,7 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseDialogFragment import io.legado.app.base.BaseDialogFragment
@ -14,7 +15,7 @@ import io.legado.app.databinding.DialogChangeCoverBinding
import io.legado.app.lib.theme.primaryColor import io.legado.app.lib.theme.primaryColor
import io.legado.app.utils.applyTint import io.legado.app.utils.applyTint
import io.legado.app.utils.getSize import io.legado.app.utils.getSize
import io.legado.app.utils.getViewModel
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
@ -39,7 +40,7 @@ class ChangeCoverDialog : BaseDialogFragment(),
private val binding by viewBinding(DialogChangeCoverBinding::bind) private val binding by viewBinding(DialogChangeCoverBinding::bind)
private var callBack: CallBack? = null private var callBack: CallBack? = null
private lateinit var viewModel: ChangeCoverViewModel private val viewModel: ChangeCoverViewModel by viewModels()
lateinit var adapter: CoverAdapter lateinit var adapter: CoverAdapter
override fun onStart() { override fun onStart() {
@ -54,7 +55,6 @@ class ChangeCoverDialog : BaseDialogFragment(),
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
callBack = activity as? CallBack callBack = activity as? CallBack
viewModel = getViewModel(ChangeCoverViewModel::class.java)
return inflater.inflate(R.layout.dialog_change_cover, container) return inflater.inflate(R.layout.dialog_change_cover, container)
} }

@ -5,9 +5,9 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import io.legado.app.App
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
@ -55,7 +55,7 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application
fun loadDbSearchBook() { fun loadDbSearchBook() {
execute { execute {
App.db.searchBookDao.getEnableHasCover(name, author).let { appDb.searchBookDao.getEnableHasCover(name, author).let {
searchBooks.addAll(it) searchBooks.addAll(it)
searchBooksLiveData.postValue(searchBooks.toList()) searchBooksLiveData.postValue(searchBooks.toList())
if (it.size <= 1) { if (it.size <= 1) {
@ -81,7 +81,7 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application
private fun startSearch() { private fun startSearch() {
execute { execute {
bookSourceList.clear() bookSourceList.clear()
bookSourceList.addAll(App.db.bookSourceDao.allEnabled) bookSourceList.addAll(appDb.bookSourceDao.allEnabled)
searchStateData.postValue(true) searchStateData.postValue(true)
initSearchPool() initSearchPool()
for (i in 0 until threadCount) { for (i in 0 until threadCount) {
@ -106,7 +106,7 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application
if (searchBook.name == name && searchBook.author == author if (searchBook.name == name && searchBook.author == author
&& !searchBook.coverUrl.isNullOrEmpty() && !searchBook.coverUrl.isNullOrEmpty()
) { ) {
App.db.searchBookDao.insert(searchBook) appDb.searchBookDao.insert(searchBook)
if (!searchBooks.contains(searchBook)) { if (!searchBooks.contains(searchBook)) {
searchBooks.add(searchBook) searchBooks.add(searchBook)
upAdapter() upAdapter()

@ -7,7 +7,7 @@ import io.legado.app.base.adapter.DiffRecyclerAdapter
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.databinding.ItemCoverBinding import io.legado.app.databinding.ItemCoverBinding
import org.jetbrains.anko.sdk27.listeners.onClick
class CoverAdapter(context: Context, val callBack: CallBack) : class CoverAdapter(context: Context, val callBack: CallBack) :
DiffRecyclerAdapter<SearchBook, ItemCoverBinding>(context) { DiffRecyclerAdapter<SearchBook, ItemCoverBinding>(context) {
@ -41,7 +41,7 @@ class CoverAdapter(context: Context, val callBack: CallBack) :
override fun registerListener(holder: ItemViewHolder, binding: ItemCoverBinding) { override fun registerListener(holder: ItemViewHolder, binding: ItemCoverBinding) {
holder.itemView.apply { holder.itemView.apply {
onClick { setOnClickListener {
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
callBack.changeTo(it.coverUrl ?: "") callBack.changeTo(it.coverUrl ?: "")
} }

@ -13,8 +13,7 @@ import io.legado.app.data.entities.SearchBook
import io.legado.app.databinding.ItemChangeSourceBinding import io.legado.app.databinding.ItemChangeSourceBinding
import io.legado.app.utils.invisible import io.legado.app.utils.invisible
import io.legado.app.utils.visible import io.legado.app.utils.visible
import org.jetbrains.anko.sdk27.listeners.onClick import splitties.views.onLongClick
import org.jetbrains.anko.sdk27.listeners.onLongClick
class ChangeSourceAdapter(context: Context, val callBack: CallBack) : class ChangeSourceAdapter(context: Context, val callBack: CallBack) :
@ -65,14 +64,13 @@ class ChangeSourceAdapter(context: Context, val callBack: CallBack) :
} }
override fun registerListener(holder: ItemViewHolder, binding: ItemChangeSourceBinding) { override fun registerListener(holder: ItemViewHolder, binding: ItemChangeSourceBinding) {
holder.itemView.onClick { holder.itemView.setOnClickListener {
getItem(holder.layoutPosition)?.let { getItem(holder.layoutPosition)?.let {
callBack.changeTo(it) callBack.changeTo(it)
} }
} }
holder.itemView.onLongClick { holder.itemView.onLongClick {
showMenu(holder.itemView, getItem(holder.layoutPosition)) showMenu(holder.itemView, getItem(holder.layoutPosition))
true
} }
} }

@ -5,13 +5,14 @@ import android.view.*
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentManager
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.legado.app.App
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseDialogFragment import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
import io.legado.app.databinding.DialogChangeSourceBinding import io.legado.app.databinding.DialogChangeSourceBinding
@ -45,7 +46,7 @@ class ChangeSourceDialog : BaseDialogFragment(),
private val binding by viewBinding(DialogChangeSourceBinding::bind) private val binding by viewBinding(DialogChangeSourceBinding::bind)
private val groups = linkedSetOf<String>() private val groups = linkedSetOf<String>()
private var callBack: CallBack? = null private var callBack: CallBack? = null
private lateinit var viewModel: ChangeSourceViewModel private val viewModel: ChangeSourceViewModel by viewModels()
lateinit var adapter: ChangeSourceAdapter lateinit var adapter: ChangeSourceAdapter
override fun onStart() { override fun onStart() {
@ -60,7 +61,6 @@ class ChangeSourceDialog : BaseDialogFragment(),
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
callBack = activity as? CallBack callBack = activity as? CallBack
viewModel = getViewModel(ChangeSourceViewModel::class.java)
return inflater.inflate(R.layout.dialog_change_source, container) return inflater.inflate(R.layout.dialog_change_source, container)
} }
@ -146,7 +146,7 @@ class ChangeSourceDialog : BaseDialogFragment(),
viewModel.searchBooksLiveData.observe(viewLifecycleOwner, { viewModel.searchBooksLiveData.observe(viewLifecycleOwner, {
adapter.setItems(it) adapter.setItems(it)
}) })
App.db.bookSourceDao.liveGroupEnabled().observe(this, { appDb.bookSourceDao.liveGroupEnabled().observe(this, {
groups.clear() groups.clear()
it.map { group -> it.map { group ->
groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex)) groups.addAll(group.splitNotBlank(AppPattern.splitGroupRegex))

@ -5,11 +5,10 @@ import android.os.Bundle
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppPattern import io.legado.app.constant.AppPattern
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.SearchBook import io.legado.app.data.entities.SearchBook
@ -21,7 +20,7 @@ import io.legado.app.utils.getPrefString
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.ExecutorCoroutineDispatcher import kotlinx.coroutines.ExecutorCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.asCoroutineDispatcher
import org.jetbrains.anko.debug import splitties.init.appCtx
import java.util.concurrent.CopyOnWriteArraySet import java.util.concurrent.CopyOnWriteArraySet
import java.util.concurrent.Executors import java.util.concurrent.Executors
@ -39,7 +38,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
private val searchBooks = CopyOnWriteArraySet<SearchBook>() private val searchBooks = CopyOnWriteArraySet<SearchBook>()
private var postTime = 0L private var postTime = 0L
private val sendRunnable = Runnable { upAdapter() } private val sendRunnable = Runnable { upAdapter() }
private val searchGroup get() = App.INSTANCE.getPrefString("searchGroup") ?: "" private val searchGroup get() = appCtx.getPrefString("searchGroup") ?: ""
@Volatile @Volatile
private var searchIndex = -1 private var searchIndex = -1
@ -64,7 +63,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
execute { execute {
searchBooks.clear() searchBooks.clear()
upAdapter() upAdapter()
App.db.searchBookDao.getChangeSourceSearch(name, author, searchGroup).let { appDb.searchBookDao.getChangeSourceSearch(name, author, searchGroup).let {
searchBooks.addAll(it) searchBooks.addAll(it)
searchBooksLiveData.postValue(searchBooks.toList()) searchBooksLiveData.postValue(searchBooks.toList())
if (it.size <= 1) { if (it.size <= 1) {
@ -89,7 +88,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
private fun searchFinish(searchBook: SearchBook) { private fun searchFinish(searchBook: SearchBook) {
if (searchBooks.contains(searchBook)) return if (searchBooks.contains(searchBook)) return
App.db.searchBookDao.insert(searchBook) appDb.searchBookDao.insert(searchBook)
if (screenKey.isEmpty()) { if (screenKey.isEmpty()) {
searchBooks.add(searchBook) searchBooks.add(searchBook)
} else if (searchBook.name.contains(screenKey)) { } else if (searchBook.name.contains(screenKey)) {
@ -100,14 +99,14 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
private fun startSearch() { private fun startSearch() {
execute { execute {
App.db.searchBookDao.clear(name, author) appDb.searchBookDao.clear(name, author)
searchBooks.clear() searchBooks.clear()
upAdapter() upAdapter()
bookSourceList.clear() bookSourceList.clear()
if (searchGroup.isBlank()) { if (searchGroup.isBlank()) {
bookSourceList.addAll(App.db.bookSourceDao.allEnabled) bookSourceList.addAll(appDb.bookSourceDao.allEnabled)
} else { } else {
bookSourceList.addAll(App.db.bookSourceDao.getEnabledByGroup(searchGroup)) bookSourceList.addAll(appDb.bookSourceDao.getEnabledByGroup(searchGroup))
} }
searchStateData.postValue(true) searchStateData.postValue(true)
initSearchPool() initSearchPool()
@ -173,7 +172,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
searchFinish(searchBook) searchFinish(searchBook)
} }
}.onError { }.onError {
debug { context.getString(R.string.error_get_book_info) } it.printStackTrace()
} }
} }
@ -186,7 +185,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
searchFinish(searchBook) searchFinish(searchBook)
} }
}.onError { }.onError {
debug { context.getString(R.string.error_get_chapter_list) } it.printStackTrace()
} }
} }
@ -200,7 +199,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
loadDbSearchBook() loadDbSearchBook()
} else { } else {
val items = val items =
App.db.searchBookDao.getChangeSourceSearch(name, author, screenKey, searchGroup) appDb.searchBookDao.getChangeSourceSearch(name, author, screenKey, searchGroup)
searchBooks.clear() searchBooks.clear()
searchBooks.addAll(items) searchBooks.addAll(items)
upAdapter() upAdapter()
@ -225,9 +224,9 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
fun disableSource(searchBook: SearchBook) { fun disableSource(searchBook: SearchBook) {
execute { execute {
App.db.bookSourceDao.getBookSource(searchBook.origin)?.let { source -> appDb.bookSourceDao.getBookSource(searchBook.origin)?.let { source ->
source.enabled = false source.enabled = false
App.db.bookSourceDao.update(source) appDb.bookSourceDao.update(source)
} }
searchBooks.remove(searchBook) searchBooks.remove(searchBook)
upAdapter() upAdapter()

@ -1,6 +1,7 @@
package io.legado.app.ui.book.explore package io.legado.app.ui.book.explore
import android.os.Bundle import android.os.Bundle
import androidx.activity.viewModels
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
@ -11,13 +12,12 @@ import io.legado.app.databinding.ViewLoadMoreBinding
import io.legado.app.ui.book.info.BookInfoActivity import io.legado.app.ui.book.info.BookInfoActivity
import io.legado.app.ui.widget.recycler.LoadMoreView import io.legado.app.ui.widget.recycler.LoadMoreView
import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.getViewModel import io.legado.app.utils.startActivity
import org.jetbrains.anko.startActivity
class ExploreShowActivity : VMBaseActivity<ActivityExploreShowBinding, ExploreShowViewModel>(), class ExploreShowActivity : VMBaseActivity<ActivityExploreShowBinding, ExploreShowViewModel>(),
ExploreShowAdapter.CallBack { ExploreShowAdapter.CallBack {
override val viewModel: ExploreShowViewModel override val viewModel: ExploreShowViewModel
get() = getViewModel(ExploreShowViewModel::class.java) by viewModels()
private lateinit var adapter: ExploreShowAdapter private lateinit var adapter: ExploreShowAdapter
private lateinit var loadMoreView: LoadMoreView private lateinit var loadMoreView: LoadMoreView
@ -87,9 +87,9 @@ class ExploreShowActivity : VMBaseActivity<ActivityExploreShowBinding, ExploreSh
} }
override fun showBookInfo(book: Book) { override fun showBookInfo(book: Book) {
startActivity<BookInfoActivity>( startActivity<BookInfoActivity> {
Pair("name", book.name), putExtra("name", book.name)
Pair("author", book.author) putExtra("author", book.author)
) }
} }
} }

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

Loading…
Cancel
Save