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'
//androidX
implementation 'androidx.core:core-ktx:1.3.2'
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.collection:collection-ktx:1.1.0"
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.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:flexbox:1.1.0'
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
def lifecycle_version = '2.2.0'
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
@ -150,11 +159,6 @@ dependencies {
//paging
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
implementation 'com.jeremyliao:live-event-bus-x:1.5.7'
@ -185,7 +189,7 @@ dependencies {
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'

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

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

@ -5,58 +5,33 @@ import android.app.NotificationManager
import android.content.Context
import android.content.res.Configuration
import android.os.Build
import android.provider.Settings
import androidx.appcompat.app.AppCompatDelegate
import androidx.multidex.MultiDexApplication
import com.jeremyliao.liveeventbus.LiveEventBus
import io.legado.app.constant.AppConst
import io.legado.app.constant.AppConst.channelIdDownload
import io.legado.app.constant.AppConst.channelIdReadAloud
import io.legado.app.constant.AppConst.channelIdWeb
import io.legado.app.constant.EventBus
import io.legado.app.data.AppDatabase
import io.legado.app.help.*
import io.legado.app.help.ActivityHelp
import io.legado.app.help.AppConfig
import io.legado.app.help.CrashHandler
import io.legado.app.help.ThemeConfig.applyDayNight
import io.legado.app.help.http.HttpHelper
import io.legado.app.utils.LanguageUtils
import io.legado.app.utils.postEvent
import org.jetbrains.anko.defaultSharedPreferences
import io.legado.app.utils.defaultSharedPreferences
import rxhttp.wrapper.param.RxHttp
@Suppress("DEPRECATION")
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() {
super.onCreate()
INSTANCE = this
androidId = Settings.System.getString(contentResolver, Settings.Secure.ANDROID_ID)
CrashHandler(this)
LanguageUtils.setConfiguration(this)
db = AppDatabase.createDatabase(INSTANCE)
RxHttp.init(HttpHelper.client, BuildConfig.DEBUG)
RxHttp.setOnParamAssembly {
it.addHeader(AppConst.UA_NAME, AppConfig.userAgent)
}
packageManager.getPackageInfo(packageName, 0)?.let {
versionCode = it.versionCode
versionName = it.versionName
}
createNotificationChannels()
applyDayNight()
applyDayNight(this)
LiveEventBus.config()
.supportBroadcast(this)
.lifecycleObserverAlwaysActive(true)
@ -69,27 +44,10 @@ class App : MultiDexApplication() {
super.onConfigurationChanged(newConfig)
when (newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
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
*/
@ -99,7 +57,7 @@ class App : MultiDexApplication() {
val downloadChannel = NotificationChannel(
channelIdDownload,
getString(R.string.action_download),
NotificationManager.IMPORTANCE_LOW
NotificationManager.IMPORTANCE_DEFAULT
).apply {
enableLights(false)
enableVibration(false)
@ -109,7 +67,7 @@ class App : MultiDexApplication() {
val readAloudChannel = NotificationChannel(
channelIdReadAloud,
getString(R.string.read_aloud),
NotificationManager.IMPORTANCE_LOW
NotificationManager.IMPORTANCE_DEFAULT
).apply {
enableLights(false)
enableVibration(false)
@ -119,7 +77,7 @@ class App : MultiDexApplication() {
val webChannel = NotificationChannel(
channelIdWeb,
getString(R.string.web_service),
NotificationManager.IMPORTANCE_LOW
NotificationManager.IMPORTANCE_DEFAULT
).apply {
enableLights(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
import io.legado.app.App
import io.legado.app.api.ReturnData
import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book
import io.legado.app.help.BookHelp
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.getPrefInt
import kotlinx.coroutines.runBlocking
import splitties.init.appCtx
object BookshelfController {
val bookshelf: ReturnData
get() {
val books = App.db.bookDao.all
val books = appDb.bookDao.all
val returnData = ReturnData()
return if (books.isEmpty()) {
returnData.setErrorMsg("还没有添加小说")
} else {
val data = when (App.INSTANCE.getPrefInt(PreferKey.bookshelfSort)) {
val data = when (appCtx.getPrefInt(PreferKey.bookshelfSort)) {
1 -> books.sortedByDescending { it.latestChapterTime }
2 -> books.sortedWith { o1, o2 ->
o1.name.cnCompare(o2.name)
@ -40,7 +41,7 @@ object BookshelfController {
if (bookUrl.isNullOrEmpty()) {
return returnData.setErrorMsg("参数url不能为空,请指定书籍地址")
}
val chapterList = App.db.bookChapterDao.getChapterList(bookUrl)
val chapterList = appDb.bookChapterDao.getChapterList(bookUrl)
return returnData.setData(chapterList)
}
@ -54,8 +55,8 @@ object BookshelfController {
if (index == null) {
return returnData.setErrorMsg("参数index不能为空, 请指定目录序号")
}
val book = App.db.bookDao.getBook(bookUrl)
val chapter = App.db.bookChapterDao.getChapter(bookUrl, index)
val book = appDb.bookDao.getBook(bookUrl)
val chapter = appDb.bookChapterDao.getChapter(bookUrl, index)
if (book == null || chapter == null) {
returnData.setErrorMsg("未找到")
} else {
@ -64,7 +65,7 @@ object BookshelfController {
saveBookReadIndex(book, index)
returnData.setData(content)
} else {
App.db.bookSourceDao.getBookSource(book.origin)?.let { source ->
appDb.bookSourceDao.getBookSource(book.origin)?.let { source ->
runBlocking {
WebBook(source).getContentAwait(this, book, chapter)
}.let {
@ -81,7 +82,7 @@ object BookshelfController {
val book = GSON.fromJsonObject<Book>(postData)
val returnData = ReturnData()
if (book != null) {
App.db.bookDao.insert(book)
appDb.bookDao.insert(book)
if (ReadBook.book?.bookUrl == book.bookUrl) {
ReadBook.book = book
ReadBook.durChapterIndex = book.durChapterIndex
@ -95,10 +96,10 @@ object BookshelfController {
if (index > book.durChapterIndex) {
book.durChapterIndex = index
book.durChapterTime = System.currentTimeMillis()
App.db.bookChapterDao.getChapter(book.bookUrl, index)?.let {
appDb.bookChapterDao.getChapter(book.bookUrl, index)?.let {
book.durChapterTitle = it.title
}
App.db.bookDao.update(book)
appDb.bookDao.update(book)
if (ReadBook.book?.bookUrl == book.bookUrl) {
ReadBook.book = book
ReadBook.durChapterIndex = index

@ -2,8 +2,8 @@ package io.legado.app.api.controller
import android.text.TextUtils
import io.legado.app.App
import io.legado.app.api.ReturnData
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray
@ -14,7 +14,7 @@ object SourceController {
val sources: ReturnData
get() {
val bookSources = App.db.bookSourceDao.all
val bookSources = appDb.bookSourceDao.all
val returnData = ReturnData()
return if (bookSources.isEmpty()) {
returnData.setErrorMsg("设备书源列表为空")
@ -29,7 +29,7 @@ object SourceController {
if (TextUtils.isEmpty(bookSource.bookSourceName) || TextUtils.isEmpty(bookSource.bookSourceUrl)) {
returnData.setErrorMsg("书源名称和URL不能为空")
} else {
App.db.bookSourceDao.insert(bookSource)
appDb.bookSourceDao.insert(bookSource)
returnData.setData("")
}
} else {
@ -50,7 +50,7 @@ object SourceController {
if (bookSource.bookSourceName.isBlank() || bookSource.bookSourceUrl.isBlank()) {
continue
}
App.db.bookSourceDao.insert(bookSource)
appDb.bookSourceDao.insert(bookSource)
okSources.add(bookSource)
}
}
@ -64,7 +64,7 @@ object SourceController {
if (url.isNullOrEmpty()) {
return returnData.setErrorMsg("参数url不能为空,请指定书源地址")
}
val bookSource = App.db.bookSourceDao.getBookSource(url)
val bookSource = appDb.bookSourceDao.getBookSource(url)
?: return returnData.setErrorMsg("未找到书源,请检查书源地址")
return returnData.setData(bookSource)
}
@ -73,7 +73,7 @@ object SourceController {
kotlin.runCatching {
GSON.fromJsonArray<BookSource>(postData)?.let {
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 io.legado.app.App
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.*
import org.jetbrains.anko.AnkoLogger
import org.jetbrains.anko.toast
import kotlin.coroutines.CoroutineContext
@Suppress("unused")
open class BaseViewModel(application: Application) : AndroidViewModel(application),
CoroutineScope by MainScope(),
AnkoLogger {
CoroutineScope by MainScope() {
val context: Context by lazy { this.getApplication<App>() }
@ -40,27 +38,12 @@ open class BaseViewModel(application: Application) : AndroidViewModel(applicatio
cancel()
}
open fun toast(message: Int) {
launch {
context.toast(message)
}
open fun toastOnUi(message: Int) {
context.toastOnUi(message)
}
open fun toast(message: CharSequence?) {
launch {
context.toast(message ?: toString())
}
open fun toastOnUi(message: CharSequence?) {
context.toastOnUi(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.RecyclerView
import androidx.viewbinding.ViewBinding
import splitties.views.onLongClick
/**
* Created by Invincible on 2017/12/15.
@ -130,10 +131,10 @@ abstract class DiffRecyclerAdapter<ITEM, VB : ViewBinding>(protected val context
}
if (itemLongClickListener != null) {
holder.itemView.setOnLongClickListener {
holder.itemView.onLongClick {
getItem(holder.layoutPosition)?.let {
itemLongClickListener?.invoke(holder, it) ?: true
} ?: true
itemLongClickListener?.invoke(holder, it)
}
}
}

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

@ -1,8 +1,8 @@
package io.legado.app.constant
import android.annotation.SuppressLint
import io.legado.app.App
import io.legado.app.R
import splitties.init.appCtx
import java.text.SimpleDateFormat
import javax.script.ScriptEngine
import javax.script.ScriptEngineManager
@ -68,5 +68,12 @@ object AppConst {
"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.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import io.legado.app.App
import io.legado.app.constant.AppConst
import io.legado.app.constant.androidId
import io.legado.app.data.dao.*
import io.legado.app.data.entities.*
import splitties.init.appCtx
import java.util.*
val appDb by lazy {
AppDatabase.createDatabase(appCtx)
}
@Database(
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,
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("ALTER TABLE readRecordNew RENAME TO readRecord")
}

@ -19,7 +19,7 @@ interface BookGroupDao {
@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 = -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)

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

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

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

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

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

@ -1,12 +1,13 @@
package io.legado.app.help
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.ReplaceRule
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.jetbrains.anko.toast
import splitties.init.appCtx
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
fun upReplaceRules() {
replaceRules.clear()
replaceRules.addAll(App.db.replaceRuleDao.findEnabledByScope(bookName, bookOrigin))
replaceRules.addAll(appDb.replaceRuleDao.findEnabledByScope(bookName, bookOrigin))
}
suspend fun getContent(
@ -40,7 +41,7 @@ class ContentProcessor(private val bookName: String, private val bookOrigin: Str
}
} catch (e: Exception) {
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) {
withContext(Dispatchers.Main) {
App.INSTANCE.toast("简繁转换出错")
appCtx.toastOnUi("简繁转换出错")
}
}
}

@ -1,11 +1,12 @@
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.RssSource
import io.legado.app.data.entities.TxtTocRule
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray
import splitties.init.appCtx
import java.io.File
object DefaultData {
@ -16,7 +17,7 @@ object DefaultData {
val httpTTS by lazy {
val json =
String(
App.INSTANCE.assets.open("defaultData${File.separator}$httpTtsFileName")
appCtx.assets.open("defaultData${File.separator}$httpTtsFileName")
.readBytes()
)
GSON.fromJsonArray<HttpTTS>(json)!!
@ -24,7 +25,7 @@ object DefaultData {
val readConfigs by lazy {
val json = String(
App.INSTANCE.assets.open("defaultData${File.separator}${ReadBookConfig.configFileName}")
appCtx.assets.open("defaultData${File.separator}${ReadBookConfig.configFileName}")
.readBytes()
)
GSON.fromJsonArray<ReadBookConfig.Config>(json)!!
@ -32,7 +33,7 @@ object DefaultData {
val txtTocRules by lazy {
val json = String(
App.INSTANCE.assets.open("defaultData${File.separator}$txtTocRuleFileName")
appCtx.assets.open("defaultData${File.separator}$txtTocRuleFileName")
.readBytes()
)
GSON.fromJsonArray<TxtTocRule>(json)!!
@ -40,7 +41,7 @@ object DefaultData {
val themeConfigs by lazy {
val json = String(
App.INSTANCE.assets.open("defaultData${File.separator}${ThemeConfig.configFileName}")
appCtx.assets.open("defaultData${File.separator}${ThemeConfig.configFileName}")
.readBytes()
)
GSON.fromJsonArray<ThemeConfig.Config>(json)!!
@ -48,23 +49,23 @@ object DefaultData {
val rssSources by lazy {
val json = String(
App.INSTANCE.assets.open("defaultData${File.separator}rssSources.json")
appCtx.assets.open("defaultData${File.separator}rssSources.json")
.readBytes()
)
GSON.fromJsonArray<RssSource>(json)!!
}
fun importDefaultHttpTTS() {
App.db.httpTTSDao.deleteDefault()
App.db.httpTTSDao.insert(*httpTTS.toTypedArray())
appDb.httpTTSDao.deleteDefault()
appDb.httpTTSDao.insert(*httpTTS.toTypedArray())
}
fun importDefaultTocRules() {
App.db.txtTocRule.deleteDefault()
App.db.txtTocRule.insert(*txtTocRules.toTypedArray())
appDb.txtTocRule.deleteDefault()
appDb.txtTocRule.insert(*txtTocRules.toTypedArray())
}
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.os.Bundle
import io.legado.app.R
import org.jetbrains.anko.toast
import io.legado.app.utils.toastOnUi
@Suppress("unused")
object IntentHelp {
@ -19,7 +19,7 @@ object IntentHelp {
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
context.startActivity(intent)
}.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
context.startActivity(intent)
}.onFailure {
context.toast("无法打开设置")
context.toastOnUi("无法打开设置")
}
}

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

@ -3,30 +3,30 @@ package io.legado.app.help
import android.content.ComponentName
import android.content.pm.PackageManager
import android.os.Build
import io.legado.app.App
import io.legado.app.R
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.
* 更换图标
*/
object LauncherIconHelp {
private val packageManager: PackageManager = App.INSTANCE.packageManager
private val packageManager: PackageManager = appCtx.packageManager
private val componentNames = arrayListOf(
ComponentName(App.INSTANCE, Launcher1::class.java.name),
ComponentName(App.INSTANCE, Launcher2::class.java.name),
ComponentName(App.INSTANCE, Launcher3::class.java.name),
ComponentName(App.INSTANCE, Launcher4::class.java.name),
ComponentName(App.INSTANCE, Launcher5::class.java.name),
ComponentName(App.INSTANCE, Launcher6::class.java.name)
ComponentName(appCtx, Launcher1::class.java.name),
ComponentName(appCtx, Launcher2::class.java.name),
ComponentName(appCtx, Launcher3::class.java.name),
ComponentName(appCtx, Launcher4::class.java.name),
ComponentName(appCtx, Launcher5::class.java.name),
ComponentName(appCtx, Launcher6::class.java.name)
)
fun changeIcon(icon: String?) {
if (icon.isNullOrEmpty()) return
if (Build.VERSION.SDK_INT < 26) {
App.INSTANCE.toast(R.string.change_icon_error)
appCtx.toastOnUi(R.string.change_icon_error)
return
}
var hasEnabled = false
@ -50,13 +50,13 @@ object LauncherIconHelp {
}
if (hasEnabled) {
packageManager.setComponentEnabledSetting(
ComponentName(App.INSTANCE, WelcomeActivity::class.java.name),
ComponentName(appCtx, WelcomeActivity::class.java.name),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
)
} else {
packageManager.setComponentEnabledSetting(
ComponentName(App.INSTANCE, WelcomeActivity::class.java.name),
ComponentName(appCtx, WelcomeActivity::class.java.name),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP
)

@ -2,19 +2,19 @@ package io.legado.app.help
import android.content.Context
import androidx.core.content.edit
import io.legado.app.App
import splitties.init.appCtx
object LocalConfig {
private const val versionCodeKey = "versionCode"
private const val versionCodeKey = "appVersionCode"
private val localConfig =
App.INSTANCE.getSharedPreferences("local", Context.MODE_PRIVATE)
appCtx.getSharedPreferences("local", Context.MODE_PRIVATE)
var versionCode
get() = localConfig.getInt(versionCodeKey, 0)
get() = localConfig.getLong(versionCodeKey, 0)
set(value) {
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.Drawable
import androidx.annotation.Keep
import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.PreferKey
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.utils.*
import splitties.init.appCtx
import java.io.File
/**
@ -20,7 +20,7 @@ import java.io.File
object ReadBookConfig {
const val configFileName = "readConfig.json"
const val shareConfigFileName = "shareReadConfig.json"
val context get() = App.INSTANCE
val context get() = appCtx
val configFilePath = FileUtils.getPath(context.filesDir, configFileName)
val shareConfigFilePath = FileUtils.getPath(context.filesDir, shareConfigFileName)
val configList: ArrayList<Config> = arrayListOf()

@ -1,11 +1,11 @@
package io.legado.app.help
import io.legado.app.App
import io.legado.app.R
import splitties.init.appCtx
object ReadTipConfig {
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 {
linkedMapOf(0 to "状态栏显示时隐藏", 1 to "显示", 2 to "隐藏")

@ -2,20 +2,21 @@ package io.legado.app.help
import android.os.Handler
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.RssSource
import io.legado.app.utils.EncoderUtils
import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.splitNotBlank
import org.jetbrains.anko.toast
import io.legado.app.utils.toastOnUi
import splitties.init.appCtx
object SourceHelp {
private val handler = Handler(Looper.getMainLooper())
private val list18Plus by lazy {
try {
return@lazy String(App.INSTANCE.assets.open("18PlusList.txt").readBytes())
return@lazy String(appCtx.assets.open("18PlusList.txt").readBytes())
.splitNotBlank("\n")
} catch (e: Exception) {
return@lazy arrayOf<String>()
@ -26,10 +27,10 @@ object SourceHelp {
rssSources.forEach { rssSource ->
if (is18Plus(rssSource.sourceUrl)) {
handler.post {
App.INSTANCE.toast("${rssSource.sourceName}是18+网址,禁止导入.")
appCtx.toastOnUi("${rssSource.sourceName}是18+网址,禁止导入.")
}
} else {
App.db.rssSourceDao.insert(rssSource)
appDb.rssSourceDao.insert(rssSource)
}
}
}
@ -38,10 +39,10 @@ object SourceHelp {
bookSources.forEach { bookSource ->
if (is18Plus(bookSource.bookSourceUrl)) {
handler.post {
App.INSTANCE.toast("${bookSource.bookSourceName}是18+网址,禁止导入.")
appCtx.toastOnUi("${bookSource.bookSourceName}是18+网址,禁止导入.")
}
} 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.Drawable
import androidx.annotation.Keep
import io.legado.app.App
import androidx.appcompat.app.AppCompatDelegate
import io.legado.app.R
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.constant.Theme
import io.legado.app.lib.theme.ThemeStore
import io.legado.app.utils.*
import splitties.init.appCtx
import java.io.File
object ThemeConfig {
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 cList = getConfigs() ?: DefaultData.themeConfigs
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? {
val bgPath = when (Theme.getTheme()) {
Theme.Light -> context.getPrefString(PreferKey.bgImage)
@ -101,8 +119,7 @@ object ThemeConfig {
context.putPrefInt(PreferKey.cBBackground, bBackground)
}
AppConfig.isNightTheme = config.isNightTheme
App.INSTANCE.applyDayNight()
postEvent(EventBus.RECREATE, "")
applyDayNight(context)
}
fun saveDayTheme(context: Context, name: String) {

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

@ -3,7 +3,7 @@
package io.legado.app.help.http
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.help.http.api.CookieManager
import io.legado.app.utils.NetworkUtils
@ -12,7 +12,7 @@ object CookieStore : CookieManager {
override fun setCookie(url: String, cookie: String?) {
val cookieBean = Cookie(NetworkUtils.getSubDomain(url), cookie ?: "")
App.db.cookieDao.insert(cookieBean)
appDb.cookieDao.insert(cookieBean)
}
override fun replaceCookie(url: String, cookie: String) {
@ -31,12 +31,12 @@ object CookieStore : CookieManager {
}
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 ?: ""
}
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> {
@ -77,7 +77,7 @@ object CookieStore : CookieManager {
}
fun clear() {
App.db.cookieDao.deleteOkHttp()
appDb.cookieDao.deleteOkHttp()
}
}

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

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

@ -3,8 +3,8 @@ package io.legado.app.help.storage
import android.content.Context
import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import io.legado.app.App
import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb
import io.legado.app.help.DefaultData
import io.legado.app.help.ReadBookConfig
import io.legado.app.help.ThemeConfig
@ -12,7 +12,7 @@ import io.legado.app.help.coroutine.Coroutine
import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext
import org.jetbrains.anko.defaultSharedPreferences
import splitties.init.appCtx
import java.io.File
import java.util.concurrent.TimeUnit
@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit
object Backup {
val backupPath: String by lazy {
FileUtils.getFile(App.INSTANCE.filesDir, "backup").absolutePath
FileUtils.getFile(appCtx.filesDir, "backup").absolutePath
}
val backupFileNames by lazy {
@ -57,32 +57,33 @@ object Backup {
context.putPrefLong(PreferKey.lastBackup, System.currentTimeMillis())
withContext(IO) {
FileUtils.deleteFile(backupPath)
writeListToJson(App.db.bookDao.all, "bookshelf.json", backupPath)
writeListToJson(App.db.bookmarkDao.all, "bookmark.json", backupPath)
writeListToJson(App.db.bookGroupDao.all, "bookGroup.json", backupPath)
writeListToJson(App.db.bookSourceDao.all, "bookSource.json", backupPath)
writeListToJson(App.db.rssSourceDao.all, "rssSources.json", backupPath)
writeListToJson(App.db.rssStarDao.all, "rssStar.json", backupPath)
writeListToJson(App.db.replaceRuleDao.all, "replaceRule.json", backupPath)
writeListToJson(App.db.readRecordDao.all, "readRecord.json", backupPath)
writeListToJson(App.db.searchKeywordDao.all, "searchHistory.json", backupPath)
writeListToJson(App.db.ruleSubDao.all, "sourceSub.json", backupPath)
writeListToJson(App.db.txtTocRule.all, DefaultData.txtTocRuleFileName, backupPath)
writeListToJson(App.db.httpTTSDao.all, DefaultData.httpTtsFileName, backupPath)
writeListToJson(appDb.bookDao.all, "bookshelf.json", backupPath)
writeListToJson(appDb.bookmarkDao.all, "bookmark.json", backupPath)
writeListToJson(appDb.bookGroupDao.all, "bookGroup.json", backupPath)
writeListToJson(appDb.bookSourceDao.all, "bookSource.json", backupPath)
writeListToJson(appDb.rssSourceDao.all, "rssSources.json", backupPath)
writeListToJson(appDb.rssStarDao.all, "rssStar.json", backupPath)
writeListToJson(appDb.replaceRuleDao.all, "replaceRule.json", backupPath)
writeListToJson(appDb.readRecordDao.all, "readRecord.json", backupPath)
writeListToJson(appDb.searchKeywordDao.all, "searchHistory.json", backupPath)
writeListToJson(appDb.ruleSubDao.all, "sourceSub.json", backupPath)
writeListToJson(appDb.txtTocRule.all, DefaultData.txtTocRuleFileName, backupPath)
writeListToJson(appDb.httpTTSDao.all, DefaultData.httpTtsFileName, backupPath)
GSON.toJson(ReadBookConfig.configList).let {
FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.configFileName)
.writeText(it)
}
GSON.toJson(ReadBookConfig.shareConfig).let {
FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.shareConfigFileName)
.writeText(it)
}
GSON.toJson(ThemeConfig.configList).let {
FileUtils.createFileIfNotExist(backupPath + File.separator + ThemeConfig.configFileName)
.writeText(it)
}
Preferences.getSharedPreferences(App.INSTANCE, backupPath, "config")?.let { sp ->
Preferences.getSharedPreferences(appCtx, backupPath, "config")?.let { sp ->
val edit = sp.edit()
App.INSTANCE.defaultSharedPreferences.all.map {
appCtx.defaultSharedPreferences.all.map {
when (val value = it.value) {
is Int -> edit.putInt(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.os.Handler
import android.os.Looper
import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.PreferKey
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.Main
import kotlinx.coroutines.withContext
import org.jetbrains.anko.toast
import splitties.init.appCtx
import java.io.File
import java.text.SimpleDateFormat
import java.util.*
@ -28,20 +27,20 @@ object BookWebDav {
private val rootWebDavUrl: String
get() {
var url = App.INSTANCE.getPrefString(PreferKey.webDavUrl)
var url = appCtx.getPrefString(PreferKey.webDavUrl)
if (url.isNullOrEmpty()) {
url = defaultWebDavUrl
}
if (!url.endsWith("/")) url = "${url}/"
if (App.INSTANCE.getPrefBoolean(PreferKey.webDavCreateDir, true)) {
if (appCtx.getPrefBoolean(PreferKey.webDavCreateDir, true)) {
url = "${url}legado/"
}
return url
}
suspend fun initWebDav(): Boolean {
val account = App.INSTANCE.getPrefString(PreferKey.webDavAccount)
val password = App.INSTANCE.getPrefString(PreferKey.webDavPassword)
val account = appCtx.getPrefString(PreferKey.webDavAccount)
val password = appCtx.getPrefString(PreferKey.webDavPassword)
if (!account.isNullOrBlank() && !password.isNullOrBlank()) {
HttpAuth.auth = HttpAuth.Auth(account, password)
WebDav(rootWebDavUrl).makeAsDir()
@ -80,7 +79,7 @@ object BookWebDav {
Coroutine.async {
restoreWebDav(names[index])
}.onError {
App.INSTANCE.toast("WebDavError:${it.localizedMessage}")
appCtx.toastOnUi("WebDavError:${it.localizedMessage}")
}
}
}
@ -118,7 +117,7 @@ object BookWebDav {
}
} catch (e: Exception) {
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) {
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.net.Uri
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.utils.DocumentUtils
import io.legado.app.utils.FileUtils
import io.legado.app.utils.isContentScheme
import org.jetbrains.anko.toast
import io.legado.app.utils.toastOnUi
import java.io.File
object ImportOldData {
@ -21,28 +21,28 @@ object ImportOldData {
kotlin.runCatching {
DocumentUtils.readText(context, doc.uri)?.let { json ->
val importCount = importOldBookshelf(json)
context.toast("成功导入书籍${importCount}")
context.toastOnUi("成功导入书籍${importCount}")
}
}.onFailure {
context.toast("导入书籍失败\n${it.localizedMessage}")
context.toastOnUi("导入书籍失败\n${it.localizedMessage}")
}
"myBookSource.json" ->
kotlin.runCatching {
DocumentUtils.readText(context, doc.uri)?.let { json ->
val importCount = importOldSource(json)
context.toast("成功导入书源${importCount}")
context.toastOnUi("成功导入书源${importCount}")
}
}.onFailure {
context.toast("导入源失败\n${it.localizedMessage}")
context.toastOnUi("导入源失败\n${it.localizedMessage}")
}
"myBookReplaceRule.json" ->
kotlin.runCatching {
DocumentUtils.readText(context, doc.uri)?.let { json ->
val importCount = importOldReplaceRule(json)
context.toast("成功导入替换规则${importCount}")
context.toastOnUi("成功导入替换规则${importCount}")
}
}.onFailure {
context.toast("导入替换规则失败\n${it.localizedMessage}")
context.toastOnUi("导入替换规则失败\n${it.localizedMessage}")
}
}
}
@ -54,9 +54,9 @@ object ImportOldData {
FileUtils.createFileIfNotExist(file, "myBookShelf.json")
val json = shelfFile.readText()
val importCount = importOldBookshelf(json)
context.toast("成功导入书籍${importCount}")
context.toastOnUi("成功导入书籍${importCount}")
}.onFailure {
context.toast("导入书籍失败\n${it.localizedMessage}")
context.toastOnUi("导入书籍失败\n${it.localizedMessage}")
}
kotlin.runCatching {// Book source
@ -64,9 +64,9 @@ object ImportOldData {
FileUtils.getFile(file, "myBookSource.json")
val json = sourceFile.readText()
val importCount = importOldSource(json)
context.toast("成功导入书源${importCount}")
context.toastOnUi("成功导入书源${importCount}")
}.onFailure {
context.toast("导入源失败\n${it.localizedMessage}")
context.toastOnUi("导入源失败\n${it.localizedMessage}")
}
kotlin.runCatching {// Replace rules
@ -74,12 +74,12 @@ object ImportOldData {
if (ruleFile.exists()) {
val json = ruleFile.readText()
val importCount = importOldReplaceRule(json)
context.toast("成功导入替换规则${importCount}")
context.toastOnUi("成功导入替换规则${importCount}")
} else {
context.toast("未找到替换规则")
context.toastOnUi("未找到替换规则")
}
}.onFailure {
context.toast("导入替换规则失败\n${it.localizedMessage}")
context.toastOnUi("导入替换规则失败\n${it.localizedMessage}")
}
}
}
@ -87,7 +87,7 @@ object ImportOldData {
private fun importOldBookshelf(json: String): Int {
val books = OldBook.toNewBook(json)
App.db.bookDao.insert(*books.toTypedArray())
appDb.bookDao.insert(*books.toTypedArray())
return books.size
}
@ -100,13 +100,13 @@ object ImportOldData {
bookSources.add(it)
}
}
App.db.bookSourceDao.insert(*bookSources.toTypedArray())
appDb.bookSourceDao.insert(*bookSources.toTypedArray())
return bookSources.size
}
private fun importOldReplaceRule(json: String): Int {
val rules = OldReplace.jsonToReplaceRules(json)
App.db.replaceRuleDao.insert(*rules.toTypedArray())
appDb.replaceRuleDao.insert(*rules.toTypedArray())
return rules.size
}
}

@ -1,8 +1,8 @@
package io.legado.app.help.storage
import android.util.Log
import io.legado.app.App
import io.legado.app.constant.AppConst
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book
import io.legado.app.utils.readBool
import io.legado.app.utils.readInt
@ -14,7 +14,7 @@ object OldBook {
fun toNewBook(json: String): List<Book> {
val books = mutableListOf<Book>()
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) {
val jsonItem = Restore.jsonPath.parse(item)
val book = Book()

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

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

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

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

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

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

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

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

@ -9,9 +9,9 @@ import androidx.annotation.CheckResult
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import io.legado.app.App
import io.legado.app.R
import io.legado.app.utils.ColorUtils
import splitties.init.appCtx
/**
* @author Aidan Follestad (afollestad), Karim Abou Zeid (kabouzeid)
@ -200,7 +200,7 @@ private constructor(private val mContext: Context) : ThemeStoreInterface {
@CheckResult
@ColorInt
fun primaryColor(context: Context = App.INSTANCE): Int {
fun primaryColor(context: Context = appCtx): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_PRIMARY_COLOR,
ATHUtils.resolveColor(context, R.attr.colorPrimary, Color.parseColor("#455A64"))
@ -218,7 +218,7 @@ private constructor(private val mContext: Context) : ThemeStoreInterface {
@CheckResult
@ColorInt
fun accentColor(context: Context = App.INSTANCE): Int {
fun accentColor(context: Context = appCtx): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_ACCENT_COLOR,
ATHUtils.resolveColor(context, R.attr.colorAccent, Color.parseColor("#263238"))
@ -292,7 +292,7 @@ private constructor(private val mContext: Context) : ThemeStoreInterface {
@CheckResult
@ColorInt
fun backgroundColor(context: Context = App.INSTANCE): Int {
fun backgroundColor(context: Context = appCtx): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_BACKGROUND_COLOR,
ATHUtils.resolveColor(context, android.R.attr.colorBackground)
@ -314,7 +314,7 @@ private constructor(private val mContext: Context) : ThemeStoreInterface {
@CheckResult
@ColorInt
fun bottomBackground(context: Context = App.INSTANCE): Int {
fun bottomBackground(context: Context = appCtx): Int {
return prefs(context).getInt(
ThemeStorePrefKeys.KEY_BOTTOM_BACKGROUND,
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
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.BookChapter
import io.legado.app.data.entities.TxtTocRule
import io.legado.app.help.DefaultData
import io.legado.app.utils.*
import splitties.init.appCtx
import java.io.File
import java.io.RandomAccessFile
import java.nio.charset.Charset
@ -266,7 +267,7 @@ class AnalyzeTxtFile {
val bookFile = FileUtils.getFile(LocalBook.cacheFolder, book.originName)
if (!bookFile.exists()) {
bookFile.createNewFile()
DocumentUtils.readBytes(App.INSTANCE, uri)?.let {
DocumentUtils.readBytes(appCtx, uri)?.let {
bookFile.writeBytes(it)
}
}
@ -276,10 +277,10 @@ class AnalyzeTxtFile {
}
private fun getTocRules(): List<TxtTocRule> {
var rules = App.db.txtTocRule.enabled
var rules = appDb.txtTocRule.enabled
if (rules.isEmpty()) {
rules = DefaultData.txtTocRules.apply {
App.db.txtTocRule.insert(*this.toTypedArray())
appDb.txtTocRule.insert(*this.toTypedArray())
}.filter {
it.enable
}

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

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

@ -1,7 +1,6 @@
package io.legado.app.model.rss
import androidx.annotation.Keep
import io.legado.app.App
import io.legado.app.R
import io.legado.app.data.entities.RssArticle
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.utils.GSON
import io.legado.app.utils.NetworkUtils
import splitties.init.appCtx
import java.util.*
@Keep
@ -27,7 +27,7 @@ object RssParserByRule {
var nextUrl: String? = null
if (body.isNullOrBlank()) {
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")

@ -1,7 +1,6 @@
package io.legado.app.model.webBook
import android.text.TextUtils
import io.legado.app.App
import io.legado.app.R
import io.legado.app.data.entities.Book
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.ensureActive
import kotlinx.coroutines.suspendCancellableCoroutine
import splitties.init.appCtx
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
@ -29,7 +29,7 @@ object BookChapterList {
kotlin.runCatching {
val chapterList = ArrayList<BookChapter>()
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}")

@ -1,7 +1,7 @@
package io.legado.app.model.webBook
import io.legado.app.App
import io.legado.app.R
import io.legado.app.data.appDb
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource
@ -14,6 +14,7 @@ import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.htmlFormat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.withContext
import splitties.init.appCtx
object BookContent {
@ -28,7 +29,7 @@ object BookContent {
nextChapterUrlF: String? = null
): String {
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}")
val content = StringBuilder()
@ -45,7 +46,7 @@ object BookContent {
val nextChapterUrl = if (!nextChapterUrlF.isNullOrEmpty())
nextChapterUrlF
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)) {
if (!nextChapterUrl.isNullOrEmpty()
&& NetworkUtils.getAbsoluteURL(baseUrl, nextUrl)

@ -1,6 +1,5 @@
package io.legado.app.model.webBook
import io.legado.app.App
import io.legado.app.R
import io.legado.app.data.entities.Book
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 kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ensureActive
import splitties.init.appCtx
object BookInfo {
@ -25,7 +25,7 @@ object BookInfo {
canReName: Boolean,
) {
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}")
val infoRule = bookSource.getBookInfoRule()

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

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

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

@ -6,7 +6,7 @@ import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import io.legado.app.ui.book.search.SearchActivity
import io.legado.app.ui.main.MainActivity
import org.jetbrains.anko.startActivity
import io.legado.app.utils.startActivity
class SharedReceiverActivity : AppCompatActivity() {
@ -22,7 +22,9 @@ class SharedReceiverActivity : AppCompatActivity() {
if (Intent.ACTION_SEND == intent.action && intent.type == receivingType) {
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
if (openUrl(it)) {
startActivity<SearchActivity>(Pair("key", it))
startActivity<SearchActivity> {
putExtra("key", it)
}
}
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
@ -31,7 +33,9 @@ class SharedReceiverActivity : AppCompatActivity() {
) {
intent.getStringExtra(Intent.EXTRA_PROCESS_TEXT)?.let {
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.PlaybackStateCompat
import androidx.core.app.NotificationCompat
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction
import io.legado.app.constant.Status
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookChapter
import io.legado.app.help.IntentHelp
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.ui.audio.AudioPlayActivity
import io.legado.app.utils.postEvent
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.anko.toast
import splitties.init.appCtx
class AudioPlayService : BaseService(),
@ -133,7 +134,7 @@ class AudioPlayService : BaseService(),
}.onFailure {
it.printStackTrace()
launch {
toast("$url ${it.localizedMessage}")
toastOnUi("$url ${it.localizedMessage}")
stopSelf()
}
}
@ -219,7 +220,7 @@ class AudioPlayService : BaseService(),
if (!mediaPlayer.isPlaying) {
AudioPlay.status = Status.STOP
postEvent(EventBus.AUDIO_STATE, Status.STOP)
launch { toast("error: $what $extra $url") }
launch { toastOnUi("error: $what $extra $url") }
}
return true
}
@ -274,7 +275,7 @@ class AudioPlayService : BaseService(),
.onSuccess { content ->
if (content.isEmpty()) {
withContext(Main) {
toast("未获取到资源链接")
toastOnUi("未获取到资源链接")
}
} else {
contentLoadFinish(chapter, content)
@ -286,7 +287,7 @@ class AudioPlayService : BaseService(),
}
} else {
removeLoading(chapter.index)
toast("book or source is null")
toastOnUi("book or source is null")
}
}
}
@ -321,7 +322,7 @@ class AudioPlayService : BaseService(),
execute {
AudioPlay.book?.let {
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.ACTION_MEDIA_BUTTON,
null,
App.INSTANCE,
appCtx,
MediaButtonReceiver::class.java
),
PendingIntent.FLAG_CANCEL_CURRENT

@ -14,7 +14,6 @@ import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import androidx.annotation.CallSuper
import androidx.core.app.NotificationCompat
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseService
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.utils.getPrefBoolean
import io.legado.app.utils.postEvent
import splitties.init.appCtx
abstract class BaseReadAloudService : BaseService(),
AudioManager.OnAudioFocusChangeListener {
@ -233,7 +233,7 @@ abstract class BaseReadAloudService : BaseService(),
Intent(
Intent.ACTION_MEDIA_BUTTON,
null,
App.INSTANCE,
appCtx,
MediaButtonReceiver::class.java
),
PendingIntent.FLAG_CANCEL_CURRENT

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

@ -2,12 +2,12 @@ package io.legado.app.service
import android.content.Intent
import androidx.core.app.NotificationCompat
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction
import io.legado.app.data.appDb
import io.legado.app.data.entities.BookSource
import io.legado.app.help.AppConfig
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.ui.book.source.manage.BookSourceActivity
import io.legado.app.utils.postEvent
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.asCoroutineDispatcher
import org.jetbrains.anko.toast
import java.util.concurrent.Executors
import kotlin.math.min
@ -70,7 +70,7 @@ class CheckSourceService : BaseService() {
private fun check(ids: List<String>) {
if (allIds.isNotEmpty()) {
toast("已有书源在校验,等完成后再试")
toastOnUi("已有书源在校验,等完成后再试")
return
}
tasks.clear()
@ -97,7 +97,7 @@ class CheckSourceService : BaseService() {
execute {
if (index < allIds.size) {
val sourceUrl = allIds[index]
App.db.bookSourceDao.getBookSource(sourceUrl)?.let { source ->
appDb.bookSourceDao.getBookSource(sourceUrl)?.let { source ->
check(source)
} ?: onNext(sourceUrl, "")
}
@ -133,10 +133,10 @@ class CheckSourceService : BaseService() {
source.addGroup("失效")
source.bookSourceComment =
"error:${it.localizedMessage}\n${source.bookSourceComment}"
App.db.bookSourceDao.update(source)
appDb.bookSourceDao.update(source)
}.onSuccess {
source.removeGroup("失效")
App.db.bookSourceDao.update(source)
appDb.bookSourceDao.update(source)
}.onFinally {
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.utils.RealPathUtil
import io.legado.app.utils.msg
import org.jetbrains.anko.downloadManager
import org.jetbrains.anko.toast
import io.legado.app.utils.toastOnUi
import splitties.systemservices.downloadManager
import java.io.File
@ -149,7 +149,7 @@ class DownloadService : BaseService() {
try {
startActivity(intent)
} 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.service.help.ReadAloud
import io.legado.app.service.help.ReadBook
import io.legado.app.utils.FileUtils
import io.legado.app.utils.LogUtils
import io.legado.app.utils.MD5Utils
import io.legado.app.utils.postEvent
import io.legado.app.utils.*
import kotlinx.coroutines.ensureActive
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.FileDescriptor
import java.io.FileInputStream
@ -84,10 +78,10 @@ class HttpReadAloudService : BaseReadAloudService(),
task = execute {
removeCacheFile()
ReadAloud.httpTTS?.let {
contentList.forEachWithIndex { index, item ->
contentList.forEachIndexed { index, item ->
if (isActive) {
val fileName =
md5SpeakFileName(it.url, AppConfig.ttsSpeechRate.toString(), item)
md5SpeakFileName(it.url, AppConfig.ttsSpeechRate.toString(), item)
if (hasSpeakFile(fileName)) { //已经下载好的语音缓存
if (index == nowSpeak) {
@ -116,12 +110,6 @@ class HttpReadAloudService : BaseReadAloudService(),
val fis = FileInputStream(file)
// 用来检测下载的文件是否为可正常播放的音频 (如果不是的话抛出异常,没找到更秒的办法,先这么着吧)
MediaPlayer().apply {
setDataSource(fis.fd)
prepare()
release()
}
if (index == nowSpeak) {
@Suppress("BlockingMethodInNonBlockingContext")
playAudio(fis.fd)
@ -129,17 +117,17 @@ class HttpReadAloudService : BaseReadAloudService(),
}
} catch (e: SocketTimeoutException) {
removeSpeakCacheFile(fileName)
runOnUiThread { toast("tts接口超时,尝试重新获取") }
toastOnUi("tts接口超时,尝试重新获取")
downloadAudio()
} catch (e: ConnectException) {
removeSpeakCacheFile(fileName)
runOnUiThread { toast("网络错误") }
toastOnUi("网络错误")
} catch (e: IOException) {
val file = getSpeakFileAsMd5(fileName)
if (file.exists()) {
FileUtils.deleteFile(file.absolutePath)
}
runOnUiThread { toast("tts文件解析错误") }
toastOnUi("tts文件解析错误")
} catch (e: Exception) {
removeSpeakCacheFile(fileName)
}

@ -13,8 +13,8 @@ import io.legado.app.help.MediaHelp
import io.legado.app.service.help.ReadBook
import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.postEvent
import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.launch
import org.jetbrains.anko.toast
import java.util.*
class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener {
@ -64,7 +64,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
}
} else {
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.PreferKey
import io.legado.app.help.IntentHelp
import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.getPrefInt
import io.legado.app.utils.postEvent
import io.legado.app.utils.*
import io.legado.app.web.HttpServer
import io.legado.app.web.WebSocketServer
import kotlinx.coroutines.launch
import org.jetbrains.anko.startService
import org.jetbrains.anko.toast
import java.io.IOException
class WebService : BaseService() {
@ -92,10 +87,8 @@ class WebService : BaseService() {
notificationContent = hostAddress
upNotification()
} catch (e: IOException) {
launch {
toast(e.localizedMessage ?: "")
stopSelf()
}
toastOnUi(e.localizedMessage ?: "")
stopSelf()
}
} else {
stopSelf()

@ -3,10 +3,10 @@ package io.legado.app.service.help
import android.content.Context
import android.content.Intent
import androidx.lifecycle.MutableLiveData
import io.legado.app.App
import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction
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.BookChapter
import io.legado.app.help.coroutine.Coroutine
@ -44,7 +44,7 @@ object AudioPlay {
}
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_SIZE, durChapter?.end?.toInt() ?: 0)
postEvent(EventBus.AUDIO_PROGRESS, durChapterPos)
@ -153,10 +153,10 @@ object AudioPlay {
book.durChapterTime = System.currentTimeMillis()
book.durChapterIndex = durChapterIndex
book.durChapterPos = durChapterPos
App.db.bookChapterDao.getChapter(book.bookUrl, book.durChapterIndex)?.let {
appDb.bookChapterDao.getChapter(book.bookUrl, book.durChapterIndex)?.let {
book.durChapterTitle = it.title
}
App.db.bookDao.update(book)
appDb.bookDao.update(book)
}
}
}
@ -165,7 +165,7 @@ object AudioPlay {
Coroutine.async {
durChapter?.let {
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.Intent
import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.IntentAction
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.utils.msg
import kotlinx.coroutines.CoroutineScope
import splitties.init.appCtx
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet
@ -81,7 +81,7 @@ object CacheBook {
ReadBook.contentLoadFinish(
book,
chapter,
content.ifBlank { App.INSTANCE.getString(R.string.content_empty) },
content.ifBlank { appCtx.getString(R.string.content_empty) },
resetPageOffset = resetPageOffset
)
}

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

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

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

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

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

@ -5,11 +5,11 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import android.view.ViewGroup
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.RecyclerAdapter
import io.legado.app.data.appDb
import io.legado.app.data.entities.ReadRecordShow
import io.legado.app.databinding.ActivityReadRecordBinding
import io.legado.app.databinding.ItemReadRecordBinding
@ -19,7 +19,6 @@ import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.jetbrains.anko.sdk27.listeners.onClick
import java.util.*
class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
@ -59,10 +58,10 @@ class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
readRecord.tvBookName.setText(R.string.all_read_time)
adapter = RecordAdapter(this@ReadRecordActivity)
recyclerView.adapter = adapter
readRecord.ivRemove.onClick {
readRecord.tvRemove.setOnClickListener {
alert(R.string.delete, R.string.sure_del) {
okButton {
App.db.readRecordDao.clear()
appDb.readRecordDao.clear()
initData()
}
noButton()
@ -72,11 +71,11 @@ class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
private fun initData() {
launch(IO) {
val allTime = App.db.readRecordDao.allTime
val allTime = appDb.readRecordDao.allTime
withContext(Main) {
binding.readRecord.tvReadTime.text = formatDuring(allTime)
}
var readRecords = App.db.readRecordDao.allShow
var readRecords = appDb.readRecordDao.allShow
readRecords = when (sortMode) {
1 -> readRecords.sortedBy { it.readTime }
else -> {
@ -112,7 +111,7 @@ class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
override fun registerListener(holder: ItemViewHolder, binding: ItemReadRecordBinding) {
binding.apply {
ivRemove.onClick {
tvRemove.setOnClickListener {
getItem(holder.layoutPosition)?.let { item ->
sureDelAlert(item)
}
@ -122,9 +121,9 @@ class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
private fun sureDelAlert(item: ReadRecordShow) {
alert(R.string.delete) {
message = getString(R.string.sure_del_any, item.bookName)
setMessage(getString(R.string.sure_del_any, item.bookName))
okButton {
App.db.readRecordDao.deleteByName(item.bookName)
appDb.readRecordDao.deleteByName(item.bookName)
initData()
}
noButton()
@ -142,7 +141,11 @@ class ReadRecordActivity : BaseActivity<ActivityReadRecordBinding>() {
val h = if (hours > 0) "${hours}小时" else ""
val m = if (minutes > 0) "${minutes}分钟" 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.os.Bundle
import androidx.activity.viewModels
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.Theme
import io.legado.app.databinding.ActivityTranslucenceBinding
import io.legado.app.ui.main.MainActivity
import io.legado.app.utils.getViewModel
import org.jetbrains.anko.startActivity
import org.jetbrains.anko.toast
import io.legado.app.utils.startActivity
import io.legado.app.utils.toastOnUi
class FileAssociationActivity :
VMBaseActivity<ActivityTranslucenceBinding, FileAssociationViewModel>(
@ -20,14 +20,13 @@ class FileAssociationActivity :
return ActivityTranslucenceBinding.inflate(layoutInflater)
}
override val viewModel: FileAssociationViewModel
get() = getViewModel(FileAssociationViewModel::class.java)
override val viewModel: FileAssociationViewModel by viewModels()
override fun onActivityCreated(savedInstanceState: Bundle?) {
binding.rotateLoading.show()
viewModel.errorLiveData.observe(this, {
binding.rotateLoading.hide()
toast(it)
toastOnUi(it)
finish()
})
viewModel.successLiveData.observe(this, {

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

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

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

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

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

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

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

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

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

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

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

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

@ -5,15 +5,16 @@ import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.activity.viewModels
import androidx.lifecycle.LiveData
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus
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.BookChapter
import io.legado.app.data.entities.BookGroup
@ -46,7 +47,7 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
private var groupId: Long = -1
override val viewModel: CacheViewModel
get() = getViewModel(CacheViewModel::class.java)
by viewModels()
override fun getViewBinding(): ActivityCacheBookBinding {
return ActivityCacheBookBinding.inflate(layoutInflater)
@ -101,7 +102,7 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
}
else -> if (item.groupId == R.id.menu_group) {
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()
}
}
@ -117,11 +118,11 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
private fun initBookData() {
booksLiveData?.removeObservers(this)
booksLiveData = when (groupId) {
AppConst.bookGroupAllId -> App.db.bookDao.observeAll()
AppConst.bookGroupLocalId -> App.db.bookDao.observeLocal()
AppConst.bookGroupAudioId -> App.db.bookDao.observeAudio()
AppConst.bookGroupNoneId -> App.db.bookDao.observeNoGroup()
else -> App.db.bookDao.observeByGroup(groupId)
AppConst.bookGroupAllId -> appDb.bookDao.observeAll()
AppConst.bookGroupLocalId -> appDb.bookDao.observeLocal()
AppConst.bookGroupAudioId -> appDb.bookDao.observeAudio()
AppConst.bookGroupNoneId -> appDb.bookDao.observeNoGroup()
else -> appDb.bookDao.observeByGroup(groupId)
}
booksLiveData?.observe(this, { list ->
val booksDownload = list.filter {
@ -142,7 +143,7 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
private fun initGroupData() {
groupLiveData?.removeObservers(this)
groupLiveData = App.db.bookGroupDao.liveDataAll()
groupLiveData = appDb.bookGroupDao.liveDataAll()
groupLiveData?.observe(this, {
groupList.clear()
groupList.addAll(it)
@ -156,7 +157,7 @@ class CacheActivity : VMBaseActivity<ActivityCacheBookBinding, CacheViewModel>()
books.forEach { book ->
val chapterCaches = hashSetOf<String>()
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())) {
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.databinding.ItemDownloadBinding
import io.legado.app.service.help.CacheBook
import org.jetbrains.anko.sdk27.listeners.onClick
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArraySet
@ -54,7 +54,7 @@ class CacheAdapter(context: Context, private val callBack: CallBack) :
override fun registerListener(holder: ItemViewHolder, binding: ItemDownloadBinding) {
with(binding) {
ivDownload.onClick {
ivDownload.setOnClickListener {
getItem(holder.layoutPosition)?.let {
if (downloadMap?.containsKey(it.bookUrl) == true) {
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)
}
}

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

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

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

@ -7,7 +7,7 @@ import io.legado.app.base.adapter.DiffRecyclerAdapter
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.data.entities.SearchBook
import io.legado.app.databinding.ItemCoverBinding
import org.jetbrains.anko.sdk27.listeners.onClick
class CoverAdapter(context: Context, val callBack: CallBack) :
DiffRecyclerAdapter<SearchBook, ItemCoverBinding>(context) {
@ -41,7 +41,7 @@ class CoverAdapter(context: Context, val callBack: CallBack) :
override fun registerListener(holder: ItemViewHolder, binding: ItemCoverBinding) {
holder.itemView.apply {
onClick {
setOnClickListener {
getItem(holder.layoutPosition)?.let {
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.utils.invisible
import io.legado.app.utils.visible
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.sdk27.listeners.onLongClick
import splitties.views.onLongClick
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) {
holder.itemView.onClick {
holder.itemView.setOnClickListener {
getItem(holder.layoutPosition)?.let {
callBack.changeTo(it)
}
}
holder.itemView.onLongClick {
showMenu(holder.itemView, getItem(holder.layoutPosition))
true
}
}

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

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

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

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

Loading…
Cancel
Save