Merge branch 'gedoor:master' into master

pull/1673/head
Xwite 3 years ago committed by GitHub
commit 9b02ea182e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      README.md
  2. 6
      app/src/main/assets/updateLog.md
  3. 2
      app/src/main/assets/web/bookshelf/css/about.71e409b6.css
  4. 2
      app/src/main/assets/web/bookshelf/css/chunk-vendors.5f0f4fba.css
  5. 2
      app/src/main/assets/web/bookshelf/css/detail.cfefeb27.css
  6. BIN
      app/src/main/assets/web/bookshelf/favicon.ico
  7. BIN
      app/src/main/assets/web/bookshelf/img/icons/android-chrome-192x192.png
  8. BIN
      app/src/main/assets/web/bookshelf/img/icons/android-chrome-512x512.png
  9. BIN
      app/src/main/assets/web/bookshelf/img/icons/apple-touch-icon-120x120.png
  10. BIN
      app/src/main/assets/web/bookshelf/img/icons/apple-touch-icon-152x152.png
  11. BIN
      app/src/main/assets/web/bookshelf/img/icons/apple-touch-icon-180x180.png
  12. BIN
      app/src/main/assets/web/bookshelf/img/icons/apple-touch-icon-60x60.png
  13. BIN
      app/src/main/assets/web/bookshelf/img/icons/apple-touch-icon-76x76.png
  14. BIN
      app/src/main/assets/web/bookshelf/img/icons/apple-touch-icon.png
  15. BIN
      app/src/main/assets/web/bookshelf/img/icons/favicon-16x16.png
  16. BIN
      app/src/main/assets/web/bookshelf/img/icons/favicon-32x32.png
  17. BIN
      app/src/main/assets/web/bookshelf/img/icons/msapplication-icon-144x144.png
  18. BIN
      app/src/main/assets/web/bookshelf/img/icons/mstile-150x150.png
  19. 149
      app/src/main/assets/web/bookshelf/img/icons/safari-pinned-tab.svg
  20. 40
      app/src/main/assets/web/bookshelf/index.html
  21. 1
      app/src/main/assets/web/bookshelf/js/about.29795c94.js
  22. 1
      app/src/main/assets/web/bookshelf/js/about.9f8f9ac0.js
  23. 1
      app/src/main/assets/web/bookshelf/js/about~detail.67be2f55.js
  24. 1
      app/src/main/assets/web/bookshelf/js/about~detail.8270a871.js
  25. 2
      app/src/main/assets/web/bookshelf/js/app.79f613ac.js
  26. 33
      app/src/main/assets/web/bookshelf/js/chunk-vendors.3ef7796f.js
  27. 33
      app/src/main/assets/web/bookshelf/js/chunk-vendors.6d9073dd.js
  28. 1
      app/src/main/assets/web/bookshelf/js/detail.4e6a53a9.js
  29. 1
      app/src/main/assets/web/bookshelf/js/detail.989b5dbd.js
  30. 1
      app/src/main/assets/web/bookshelf/manifest.json
  31. 70
      app/src/main/assets/web/bookshelf/precache-manifest.5ae9ceec57e7f0f3cc808807b7fe5f32.js
  32. 34
      app/src/main/assets/web/bookshelf/service-worker.js
  33. 21
      app/src/main/java/io/legado/app/api/controller/BookController.kt
  34. 4
      app/src/main/java/io/legado/app/api/controller/BookSourceController.kt
  35. 3
      app/src/main/java/io/legado/app/constant/AppLog.kt
  36. 19
      app/src/main/java/io/legado/app/data/dao/BookDao.kt
  37. 16
      app/src/main/java/io/legado/app/data/entities/BaseSource.kt
  38. 6
      app/src/main/java/io/legado/app/data/entities/Book.kt
  39. 4
      app/src/main/java/io/legado/app/data/entities/BookChapter.kt
  40. 32
      app/src/main/java/io/legado/app/data/entities/BookSource.kt
  41. 4
      app/src/main/java/io/legado/app/data/entities/RssArticle.kt
  42. 2
      app/src/main/java/io/legado/app/data/entities/RssStar.kt
  43. 4
      app/src/main/java/io/legado/app/data/entities/SearchBook.kt
  44. 2
      app/src/main/java/io/legado/app/help/ContentProcessor.kt
  45. 19
      app/src/main/java/io/legado/app/help/DefaultData.kt
  46. 2
      app/src/main/java/io/legado/app/help/JsExtensions.kt
  47. 16
      app/src/main/java/io/legado/app/help/ReadBookConfig.kt
  48. 40
      app/src/main/java/io/legado/app/help/SourceAnalyzer.kt
  49. 11
      app/src/main/java/io/legado/app/help/ThemeConfig.kt
  50. 4
      app/src/main/java/io/legado/app/help/storage/AppWebDav.kt
  51. 2
      app/src/main/java/io/legado/app/help/storage/BackupRestore.kt
  52. 7
      app/src/main/java/io/legado/app/help/storage/OldReplace.kt
  53. 7
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  54. 5
      app/src/main/java/io/legado/app/model/AudioPlay.kt
  55. 5
      app/src/main/java/io/legado/app/model/ReadBook.kt
  56. 15
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeRule.kt
  57. 51
      app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt
  58. 4
      app/src/main/java/io/legado/app/model/localBook/LocalBook.kt
  59. 2
      app/src/main/java/io/legado/app/service/TTSReadAloudService.kt
  60. 8
      app/src/main/java/io/legado/app/ui/association/BaseAssociationViewModel.kt
  61. 2
      app/src/main/java/io/legado/app/ui/association/ImportReplaceRuleDialog.kt
  62. 2
      app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt
  63. 2
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeBookSourceDialog.kt
  64. 2
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeChapterSourceDialog.kt
  65. 7
      app/src/main/java/io/legado/app/ui/book/local/rule/TxtTocRuleActivity.kt
  66. 4
      app/src/main/java/io/legado/app/ui/book/local/rule/TxtTocRuleViewModel.kt
  67. 5
      app/src/main/java/io/legado/app/ui/book/read/ContentEditDialog.kt
  68. 2
      app/src/main/java/io/legado/app/ui/book/read/PhotoDialog.kt
  69. 6
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  70. 2
      app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudConfigDialog.kt
  71. 7
      app/src/main/java/io/legado/app/ui/book/read/config/SpeakEngineDialog.kt
  72. 5
      app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt
  73. 4
      app/src/main/java/io/legado/app/ui/book/read/config/TocRegexViewModel.kt
  74. 4
      app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
  75. 10
      app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BaseBooksAdapter.kt
  76. 10
      app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterGrid.kt
  77. 10
      app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BooksAdapterList.kt
  78. 38
      app/src/main/java/io/legado/app/ui/main/bookshelf/style2/BookshelfFragment2.kt
  79. 2
      app/src/main/java/io/legado/app/ui/widget/dialog/CodeDialog.kt
  80. 41
      app/src/main/java/io/legado/app/utils/DataExtensions.kt
  81. 15
      app/src/main/java/io/legado/app/utils/GsonExtensions.kt
  82. 4
      app/src/main/java/io/legado/app/utils/IntentExtensions.kt
  83. 3
      app/src/main/java/io/legado/app/web/socket/BookSourceDebugWebSocket.kt
  84. 3
      app/src/main/java/io/legado/app/web/socket/RssSourceDebugWebSocket.kt
  85. 1
      app/src/main/java/io/legado/app/web/utils/AssetsWeb.kt

@ -95,7 +95,7 @@ https://gedoor.github.io/about.html
* [书源规则](https://alanskycn.gitee.io/teachme) * [书源规则](https://alanskycn.gitee.io/teachme)
* [更新日志](/app/src/main/assets/updateLog.md) * [更新日志](/app/src/main/assets/updateLog.md)
* [帮助文档](/app/src/main/assets/help/appHelp.md) * [帮助文档](/app/src/main/assets/help/appHelp.md)
* [web端](https://github.com/celetor/web-yuedu3) * [web端](https://github.com/gedoor/legado_web_bookshelf)
<a href="#readme"> <a href="#readme">
<img src="https://img.shields.io/badge/-返回顶部-orange.svg" alt="#" align="right"> <img src="https://img.shields.io/badge/-返回顶部-orange.svg" alt="#" align="right">

@ -11,6 +11,12 @@
* 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。 * 正文出现缺字漏字、内容缺失、排版错乱等情况,有可能是净化规则或简繁转换出现问题。
* 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源! * 漫画源看书显示乱码,**阅读与其他软件的源并不通用**,请导入阅读的支持的漫画源!
**2022/03/02**
* ~~js引擎rhino升级到1.7.4~~
* 优化web端,修复一些封面无法访问bug,代理正文图片
* 其它一些优化
**2022/02/28** **2022/02/28**
* APP内编写规则时,对由XPath|JSOUP|CSS组成的规则进行简单的默认补全。需手动开启,单次生效 * APP内编写规则时,对由XPath|JSOUP|CSS组成的规则进行简单的默认补全。需手动开启,单次生效

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 799 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

@ -1,149 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="16.000000pt" height="16.000000pt" viewBox="0 0 16.000000 16.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,16.000000) scale(0.000320,-0.000320)"
fill="#000000" stroke="none">
<path d="M18 46618 c45 -75 122 -207 122 -211 0 -2 25 -45 55 -95 30 -50 55
-96 55 -102 0 -5 5 -10 10 -10 6 0 10 -4 10 -9 0 -5 73 -135 161 -288 89 -153
173 -298 187 -323 14 -25 32 -57 41 -72 88 -149 187 -324 189 -335 2 -7 8 -13
13 -13 5 0 9 -4 9 -10 0 -5 46 -89 103 -187 175 -302 490 -846 507 -876 8 -16
20 -36 25 -45 28 -46 290 -498 339 -585 13 -23 74 -129 136 -236 61 -107 123
-215 137 -240 14 -25 29 -50 33 -56 5 -5 23 -37 40 -70 18 -33 38 -67 44 -75
11 -16 21 -33 63 -109 14 -25 29 -50 33 -56 4 -5 21 -35 38 -65 55 -100 261
-455 269 -465 4 -5 14 -21 20 -35 15 -29 41 -75 103 -180 24 -41 52 -88 60
-105 9 -16 57 -100 107 -185 112 -193 362 -626 380 -660 8 -14 23 -38 33 -55
11 -16 23 -37 27 -45 4 -8 26 -46 48 -85 23 -38 53 -90 67 -115 46 -81 64
-113 178 -310 62 -107 121 -210 132 -227 37 -67 56 -99 85 -148 16 -27 32 -57
36 -65 4 -8 15 -27 25 -42 9 -15 53 -89 96 -165 44 -76 177 -307 296 -513 120
-206 268 -463 330 -570 131 -227 117 -203 200 -348 36 -62 73 -125 82 -140 10
-15 21 -34 25 -42 4 -8 20 -37 36 -65 17 -27 38 -65 48 -82 49 -85 64 -111 87
-153 13 -25 28 -49 32 -55 4 -5 78 -134 165 -285 87 -151 166 -288 176 -305
10 -16 26 -43 35 -59 9 -17 125 -217 257 -445 132 -229 253 -441 270 -471 17
-30 45 -79 64 -108 18 -29 33 -54 33 -57 0 -2 20 -37 44 -77 24 -40 123 -212
221 -383 97 -170 190 -330 205 -355 16 -25 39 -65 53 -90 13 -25 81 -144 152
-265 70 -121 137 -238 150 -260 12 -22 37 -65 55 -95 18 -30 43 -73 55 -95 12
-22 48 -85 80 -140 77 -132 163 -280 190 -330 13 -22 71 -123 130 -225 59
-102 116 -199 126 -217 10 -17 29 -50 43 -72 15 -22 26 -43 26 -45 0 -2 27
-50 60 -106 33 -56 60 -103 60 -105 0 -2 55 -98 90 -155 8 -14 182 -316 239
-414 13 -22 45 -79 72 -124 27 -46 49 -86 49 -89 0 -2 14 -24 30 -48 16 -24
30 -46 30 -49 0 -5 74 -135 100 -176 5 -8 24 -42 43 -75 50 -88 58 -101 262
-455 104 -179 199 -345 213 -370 14 -25 28 -49 32 -55 4 -5 17 -26 28 -45 10
-19 62 -109 114 -200 114 -197 133 -230 170 -295 16 -27 33 -57 38 -65 17 -28
96 -165 103 -180 4 -8 16 -28 26 -45 10 -16 77 -131 148 -255 72 -124 181
-313 243 -420 62 -107 121 -209 131 -227 35 -62 323 -560 392 -678 38 -66 83
-145 100 -175 16 -30 33 -59 37 -65 4 -5 17 -27 29 -47 34 -61 56 -100 90
-156 17 -29 31 -55 31 -57 0 -2 17 -32 39 -67 21 -35 134 -229 251 -433 117
-203 235 -407 261 -451 27 -45 49 -85 49 -88 0 -4 8 -19 19 -34 15 -21 200
-341 309 -533 10 -19 33 -58 51 -87 17 -29 31 -54 31 -56 0 -2 25 -44 55 -94
30 -50 55 -95 55 -98 0 -4 6 -15 14 -23 7 -9 27 -41 43 -71 17 -30 170 -297
342 -594 171 -296 311 -542 311 -547 0 -5 5 -9 10 -9 6 0 10 -4 10 -10 0 -5
22 -47 49 -92 27 -46 58 -99 68 -118 24 -43 81 -140 93 -160 5 -8 66 -114 135
-235 69 -121 130 -227 135 -235 12 -21 259 -447 283 -490 10 -19 28 -47 38
-62 11 -14 19 -29 19 -32 0 -3 37 -69 83 -148 99 -170 305 -526 337 -583 13
-22 31 -53 41 -70 11 -16 22 -37 26 -45 7 -14 82 -146 103 -180 14 -24 181
-311 205 -355 13 -22 46 -80 75 -130 29 -49 64 -110 78 -135 14 -25 51 -88 82
-140 31 -52 59 -102 63 -110 4 -8 18 -33 31 -55 205 -353 284 -489 309 -535
17 -30 45 -78 62 -106 18 -28 36 -60 39 -72 4 -12 12 -22 17 -22 5 0 9 -4 9
-10 0 -5 109 -197 241 -427 133 -230 250 -431 259 -448 51 -90 222 -385 280
-485 37 -63 78 -135 92 -160 14 -25 67 -117 118 -205 51 -88 101 -175 111
-193 34 -58 55 -95 149 -257 51 -88 101 -173 110 -190 9 -16 76 -131 147 -255
72 -124 140 -241 151 -260 61 -108 281 -489 355 -615 38 -66 77 -133 87 -150
35 -63 91 -161 100 -175 14 -23 99 -169 128 -220 54 -97 135 -235 142 -245 4
-5 20 -32 35 -60 26 -48 238 -416 276 -480 10 -16 26 -46 37 -65 30 -53 382
-661 403 -695 10 -16 22 -37 26 -45 4 -8 26 -48 50 -88 24 -41 43 -75 43 -77
0 -2 22 -40 50 -85 27 -45 50 -84 50 -86 0 -3 38 -69 83 -147 84 -142 302
-520 340 -587 10 -19 34 -60 52 -90 18 -30 44 -75 57 -100 14 -25 45 -79 70
-120 25 -41 56 -96 70 -121 14 -25 77 -133 138 -240 62 -107 122 -210 132
-229 25 -43 310 -535 337 -581 11 -19 26 -45 34 -59 17 -32 238 -414 266 -460
11 -19 24 -41 28 -49 3 -7 75 -133 160 -278 84 -146 153 -269 153 -274 0 -5 5
-9 10 -9 6 0 10 -4 10 -10 0 -5 82 -150 181 -322 182 -314 201 -346 240 -415
12 -21 80 -139 152 -263 71 -124 141 -245 155 -270 14 -25 28 -49 32 -55 6 -8
145 -248 220 -380 37 -66 209 -362 229 -395 11 -19 24 -42 28 -49 4 -8 67
-118 140 -243 73 -125 133 -230 133 -233 0 -2 15 -28 33 -57 19 -29 47 -78 64
-108 17 -30 53 -93 79 -139 53 -90 82 -141 157 -272 82 -142 115 -199 381
-659 142 -245 268 -463 281 -485 12 -22 71 -125 132 -230 60 -104 172 -298
248 -430 76 -132 146 -253 156 -270 11 -16 22 -36 26 -44 3 -8 30 -54 60 -103
29 -49 53 -91 53 -93 0 -3 18 -34 40 -70 22 -36 40 -67 40 -69 0 -2 37 -66 81
-142 45 -77 98 -168 119 -204 20 -36 47 -81 58 -100 12 -19 27 -47 33 -62 6
-16 15 -28 20 -28 5 0 9 -4 9 -9 0 -6 63 -118 140 -251 77 -133 140 -243 140
-245 0 -2 18 -33 41 -70 22 -37 49 -83 60 -101 10 -19 29 -51 40 -71 25 -45
109 -189 126 -218 7 -11 17 -29 22 -40 6 -11 22 -38 35 -60 14 -22 37 -62 52
-90 14 -27 35 -62 45 -77 11 -14 19 -29 19 -32 0 -3 18 -35 40 -71 22 -36 40
-67 40 -69 0 -2 19 -35 42 -72 23 -38 55 -94 72 -124 26 -47 139 -244 171
-298 6 -9 21 -36 34 -60 28 -48 37 -51 51 -19 6 12 19 36 29 52 10 17 27 46
38 65 11 19 104 181 208 360 103 179 199 345 213 370 14 25 42 74 64 109 21
34 38 65 38 67 0 2 18 33 40 69 22 36 40 67 40 69 0 3 177 310 199 346 16 26
136 234 140 244 2 5 25 44 52 88 27 44 49 81 49 84 0 2 18 34 40 70 22 36 40
67 40 69 0 2 20 36 43 77 35 58 169 289 297 513 9 17 50 86 90 155 40 69 86
150 103 180 16 30 35 62 41 70 6 8 16 24 22 35 35 64 72 129 167 293 59 100
116 199 127 220 11 20 30 53 41 72 43 72 1070 1850 1121 1940 14 25 65 113
113 195 48 83 96 166 107 185 10 19 28 50 38 68 11 18 73 124 137 235 64 111
175 303 246 427 71 124 173 299 225 390 52 91 116 202 143 248 27 45 49 85 49
89 0 4 6 14 14 22 7 9 28 43 46 76 26 47 251 436 378 655 11 19 29 51 40 70
11 19 101 176 201 348 99 172 181 317 181 323 0 5 5 9 10 9 6 0 10 5 10 11 0
6 8 23 18 37 11 15 32 52 49 82 16 30 130 228 253 440 122 212 234 405 248
430 13 25 39 70 57 100 39 65 69 117 130 225 25 44 50 87 55 95 12 19 78 134
220 380 61 107 129 224 150 260 161 277 222 382 246 425 15 28 47 83 71 123
24 41 43 78 43 83 0 5 4 9 8 9 4 0 13 12 19 28 7 15 23 45 36 67 66 110 277
478 277 483 0 3 6 13 14 21 7 9 27 41 43 71 17 30 45 80 63 110 34 57 375 649
394 685 6 11 16 27 22 35 6 8 26 42 44 75 18 33 41 74 51 90 10 17 24 41 32
55 54 97 72 128 88 152 11 14 19 28 19 30 0 3 79 141 175 308 96 167 175 305
175 308 0 3 6 13 14 21 7 9 26 39 41 66 33 60 276 483 338 587 24 40 46 80 50
88 4 8 13 24 20 35 14 23 95 163 125 215 11 19 52 91 92 160 40 69 80 139 90
155 9 17 103 179 207 360 105 182 200 346 211 365 103 181 463 802 489 845 7
11 15 27 19 35 4 8 29 51 55 95 64 110 828 1433 848 1470 9 17 24 41 33 55 9
14 29 48 45 77 15 28 52 93 82 145 30 51 62 107 71 123 17 30 231 398 400 690
51 88 103 179 115 202 12 23 26 48 32 55 6 7 24 38 40 68 17 30 61 107 98 170
37 63 84 144 103 180 19 36 41 72 48 81 8 8 14 18 14 21 0 4 27 51 59 106 32
55 72 124 89 154 16 29 71 125 122 213 51 88 104 180 118 205 13 25 28 50 32
55 4 6 17 26 28 45 11 19 45 80 77 135 31 55 66 116 77 135 11 19 88 152 171
295 401 694 620 1072 650 1125 11 19 87 152 170 295 83 143 158 273 166 288 9
16 21 36 26 45 6 9 31 52 55 96 25 43 54 94 66 115 11 20 95 164 186 321 91
157 173 299 182 315 9 17 26 46 37 65 12 19 66 114 121 210 56 96 108 186 117
200 8 14 24 40 34 59 24 45 383 664 412 713 5 9 17 29 26 45 15 28 120 210
241 419 36 61 68 117 72 125 4 8 12 23 19 34 35 57 245 420 262 453 11 20 35
61 53 90 17 29 32 54 32 56 0 3 28 51 62 108 33 57 70 119 80 138 10 19 23 42
28 50 5 8 32 53 59 100 27 47 149 258 271 470 122 212 234 405 248 430 30 53
62 108 80 135 6 11 15 27 19 35 4 8 85 150 181 315 96 165 187 323 202 350 31
56 116 202 130 225 5 8 25 42 43 75 19 33 92 159 162 280 149 257 157 271 202
350 19 33 38 67 43 75 9 14 228 392 275 475 12 22 55 96 95 165 40 69 80 139
90 155 24 42 202 350 221 383 9 15 27 47 41 72 14 25 75 131 136 236 61 106
121 210 134 232 99 172 271 470 279 482 5 8 23 40 40 70 18 30 81 141 142 245
60 105 121 210 135 235 14 25 71 124 127 220 56 96 143 247 194 335 51 88 96
167 102 175 14 24 180 311 204 355 23 43 340 590 356 615 5 8 50 87 101 175
171 301 517 898 582 1008 25 43 46 81 46 83 0 2 12 23 27 47 14 23 40 67 56
97 16 30 35 62 42 70 7 8 15 22 18 30 4 8 20 38 37 65 16 28 33 57 37 65 6 12
111 196 143 250 5 8 55 95 112 193 57 98 113 195 126 215 12 20 27 46 32 57 6
11 14 27 20 35 5 8 76 130 156 270 80 140 165 287 187 325 23 39 52 90 66 115
13 25 30 52 37 61 8 8 14 18 14 21 0 4 41 77 92 165 50 87 175 302 276 478
101 176 208 360 236 408 28 49 67 117 86 152 19 35 41 70 48 77 6 6 12 15 12
19 0 7 124 224 167 291 12 21 23 40 23 42 0 2 21 40 46 83 26 43 55 92 64 109
54 95 327 568 354 614 19 30 45 75 59 100 71 128 82 145 89 148 4 2 8 8 8 13
0 5 42 82 94 172 311 538 496 858 518 897 14 25 40 70 58 100 18 30 42 71 53
90 10 19 79 139 152 265 73 127 142 246 153 265 10 19 43 76 72 125 29 50 63
108 75 130 65 116 80 140 87 143 4 2 8 8 8 12 0 8 114 212 140 250 6 8 14 24
20 35 5 11 54 97 108 190 l100 170 -9611 3 c-5286 1 -9614 -1 -9618 -5 -5 -6
-419 -719 -619 -1068 -89 -155 -267 -463 -323 -560 -38 -66 -81 -140 -95 -165
-31 -56 -263 -457 -526 -910 -110 -190 -224 -388 -254 -440 -29 -52 -61 -109
-71 -125 -23 -39 -243 -420 -268 -465 -11 -19 -204 -352 -428 -740 -224 -388
-477 -826 -563 -975 -85 -148 -185 -322 -222 -385 -37 -63 -120 -207 -185
-320 -65 -113 -177 -306 -248 -430 -72 -124 -172 -297 -222 -385 -51 -88 -142
-245 -202 -350 -131 -226 -247 -427 -408 -705 -65 -113 -249 -432 -410 -710
-160 -278 -388 -673 -506 -877 -118 -205 -216 -373 -219 -373 -3 0 -52 82
-109 183 -58 100 -144 250 -192 332 -95 164 -402 696 -647 1120 -85 149 -228
396 -317 550 -212 365 -982 1700 -1008 1745 -10 19 -43 76 -72 125 -29 50 -64
110 -77 135 -14 25 -63 110 -110 190 -47 80 -96 165 -110 190 -14 25 -99 171
-188 325 -89 154 -174 300 -188 325 -13 25 -64 113 -112 195 -48 83 -140 242
-205 355 -65 113 -183 317 -263 454 -79 137 -152 264 -163 282 -50 89 -335
583 -354 614 -12 19 -34 58 -50 85 -15 28 -129 226 -253 440 -124 215 -235
408 -247 430 -12 22 -69 121 -127 220 -58 99 -226 389 -373 645 -148 256 -324
561 -392 678 -67 117 -134 232 -147 255 -13 23 -33 59 -46 80 l-22 37 -9615 0
-9615 0 20 -32z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 10 KiB

@ -4,40 +4,30 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1">
<!--[if IE]> <link rel="icon" href="../favicon.ico" type="image/x-icon">
<link rel="icon" href="favicon.ico"/><![endif]--><title>Legado Bookshelf</title> <link rel="shortcut icon" href="../favicon.ico" type="image/x-icon">
<link href="css/about.b9bb4fe0.css" rel="prefetch"> <title>Legado Bookshelf</title>
<link href="css/detail.e03dc50b.css" rel="prefetch"> <link href="css/about.71e409b6.css" rel="prefetch">
<link href="js/about.9f8f9ac0.js" rel="prefetch"> <link href="css/detail.cfefeb27.css" rel="prefetch">
<link href="js/about~detail.8270a871.js" rel="prefetch"> <link href="js/about.29795c94.js" rel="prefetch">
<link href="js/detail.4e6a53a9.js" rel="prefetch"> <link href="js/about~detail.67be2f55.js" rel="prefetch">
<link href="js/detail.989b5dbd.js" rel="prefetch">
<link href="css/app.e4c919b7.css" rel="preload" as="style"> <link href="css/app.e4c919b7.css" rel="preload" as="style">
<link href="css/chunk-vendors.8a465a1d.css" rel="preload" as="style"> <link href="css/chunk-vendors.5f0f4fba.css" rel="preload" as="style">
<link href="js/app.e84ee963.js" rel="preload" as="script"> <link href="js/app.79f613ac.js" rel="preload" as="script">
<link href="js/chunk-vendors.3ef7796f.js" rel="preload" as="script"> <link href="js/chunk-vendors.6d9073dd.js" rel="preload" as="script">
<link href="css/chunk-vendors.8a465a1d.css" rel="stylesheet"> <link href="css/chunk-vendors.5f0f4fba.css" rel="stylesheet">
<link href="css/app.e4c919b7.css" rel="stylesheet"> <link href="css/app.e4c919b7.css" rel="stylesheet">
<link rel="icon" type="image/png" sizes="32x32" href="img/icons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="img/icons/favicon-16x16.png">
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#4DBA87">
<meta name="apple-mobile-web-app-capable" content="no">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="yd-web-tool">
<link rel="apple-touch-icon" href="img/icons/apple-touch-icon-152x152.png">
<link rel="mask-icon" href="img/icons/safari-pinned-tab.svg" color="#4DBA87">
<meta name="msapplication-TileImage" content="img/icons/msapplication-icon-144x144.png">
<meta name="msapplication-TileColor" content="#000000">
</head> </head>
<style>body::-webkit-scrollbar { <style>body::-webkit-scrollbar {
display: none; display: none;
} }
</style> </style>
<body style="margin: 0;height:100%"> <body style="margin: 0;height:100%">
<noscript><strong>We're sorry but yd-web-tool doesn't work properly without JavaScript enabled. <noscript><strong>We're sorry but yd-web-tool doesn't work properly without JavaScript enabled.
Please enable it to continue.</strong></noscript> Please enable it to continue.</strong></noscript>
<div id="app"></div> <div id="app"></div>
<script src="js/chunk-vendors.3ef7796f.js"></script> <script src="js/chunk-vendors.6d9073dd.js"></script>
<script src="js/app.e84ee963.js"></script> <script src="js/app.79f613ac.js"></script>
</body> </body>
</html> </html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -1 +0,0 @@
{"name":"yd-web-tool","short_name":"yd-web-tool","theme_color":"#4DBA87","icons":[{"src":"./img/icons/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"./img/icons/android-chrome-512x512.png","sizes":"512x512","type":"image/png"},{"src":"./img/icons/android-chrome-maskable-192x192.png","sizes":"192x192","type":"image/png","purpose":"maskable"},{"src":"./img/icons/android-chrome-maskable-512x512.png","sizes":"512x512","type":"image/png","purpose":"maskable"}],"start_url":".","display":"standalone","background_color":"#000000"}

@ -1,70 +0,0 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "c6913d775f2f965ac5f3",
"url": "css/about.b9bb4fe0.css"
},
{
"revision": "c7b3e35a4e0391b1ed37",
"url": "css/app.e4c919b7.css"
},
{
"revision": "9a65f05f9810a3ea7f46",
"url": "css/chunk-vendors.8a465a1d.css"
},
{
"revision": "5ab5c6be15b21e2d609b",
"url": "css/detail.e03dc50b.css"
},
{
"revision": "535877f50039c0cb49a6196a5b7517cd",
"url": "fonts/element-icons.535877f5.woff"
},
{
"revision": "732389ded34cb9c52dd88271f1345af9",
"url": "fonts/element-icons.732389de.ttf"
},
{
"revision": "f9a3fb0e145017e166dd4d91d9280cc4",
"url": "fonts/iconfont.f9a3fb0e.woff"
},
{
"revision": "f39ecc1a1d2a1eff3aca8aadd818bb61",
"url": "fonts/popfont.f39ecc1a.ttf"
},
{
"revision": "6c094b6d4ae9404dbed273c41b06fae8",
"url": "fonts/shelffont.6c094b6d.ttf"
},
{
"revision": "8a8424347500238b7b6c08a98d0f89af",
"url": "index.html"
},
{
"revision": "c6913d775f2f965ac5f3",
"url": "js/about.9f8f9ac0.js"
},
{
"revision": "5998ccb313ed338c15e1",
"url": "js/about~detail.8270a871.js"
},
{
"revision": "c7b3e35a4e0391b1ed37",
"url": "js/app.e84ee963.js"
},
{
"revision": "9a65f05f9810a3ea7f46",
"url": "js/chunk-vendors.3ef7796f.js"
},
{
"revision": "5ab5c6be15b21e2d609b",
"url": "js/detail.4e6a53a9.js"
},
{
"revision": "b46d04eb43bc31ca0f9f95121646440d",
"url": "manifest.json"
},
{
"revision": "b6216d61c03e6ce0c9aea6ca7808f7ca",
"url": "robots.txt"
}
]);

@ -1,34 +0,0 @@
/**
* Welcome to your Workbox-powered service worker!
*
* You'll need to register this file in your web app and you should
* disable HTTP caching for this file too.
* See https://goo.gl/nhQhGp
*
* The rest of the code is auto-generated. Please don't update this file
* directly; instead, make changes to your Workbox build configuration
* and re-run your build process.
* See https://goo.gl/2aRDsh
*/
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts(
"precache-manifest.5ae9ceec57e7f0f3cc808807b7fe5f32.js"
);
workbox.core.setCacheNameDetails({prefix: "yd-web-tool"});
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
* See https://goo.gl/S9QRab
*/
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});

@ -21,6 +21,7 @@ import io.legado.app.model.localBook.LocalBook
import io.legado.app.model.localBook.UmdFile import io.legado.app.model.localBook.UmdFile
import io.legado.app.model.webBook.WebBook import io.legado.app.model.webBook.WebBook
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import splitties.init.appCtx import splitties.init.appCtx
import java.io.File import java.io.File
@ -153,7 +154,16 @@ object BookController {
return returnData.setErrorMsg("参数index不能为空, 请指定目录序号") return returnData.setErrorMsg("参数index不能为空, 请指定目录序号")
} }
val book = appDb.bookDao.getBook(bookUrl) val book = appDb.bookDao.getBook(bookUrl)
val chapter = appDb.bookChapterDao.getChapter(bookUrl, index) val chapter = runBlocking {
var chapter = appDb.bookChapterDao.getChapter(bookUrl, index)
var wait = 0
while (chapter == null && wait < 30) {
delay(1000)
chapter = appDb.bookChapterDao.getChapter(bookUrl, index)
wait++
}
chapter
}
if (book == null || chapter == null) { if (book == null || chapter == null) {
return returnData.setErrorMsg("未找到") return returnData.setErrorMsg("未找到")
} }
@ -188,9 +198,8 @@ object BookController {
* 保存书籍 * 保存书籍
*/ */
fun saveBook(postData: String?): ReturnData { fun saveBook(postData: String?): ReturnData {
val book = GSON.fromJsonObject<Book>(postData)
val returnData = ReturnData() val returnData = ReturnData()
if (book != null) { GSON.fromJsonObject<Book>(postData).getOrNull()?.let { book ->
book.save() book.save()
AppWebDav.uploadBookProgress(book) AppWebDav.uploadBookProgress(book)
if (ReadBook.book?.bookUrl == book.bookUrl) { if (ReadBook.book?.bookUrl == book.bookUrl) {
@ -279,6 +288,9 @@ object BookController {
return returnData.setData(true) return returnData.setData(true)
} }
/**
*
*/
fun saveWebReadConfig(postData: String?): ReturnData { fun saveWebReadConfig(postData: String?): ReturnData {
val returnData = ReturnData() val returnData = ReturnData()
postData?.let { postData?.let {
@ -287,6 +299,9 @@ object BookController {
return returnData.setData("") return returnData.setData("")
} }
/**
*
*/
fun getWebReadConfig(): ReturnData { fun getWebReadConfig(): ReturnData {
val returnData = ReturnData() val returnData = ReturnData()
val data = CacheManager.get("webReadConfig") ?: "{}" val data = CacheManager.get("webReadConfig") ?: "{}"

@ -73,11 +73,13 @@ object BookSourceController {
fun deleteSources(postData: String?): ReturnData { fun deleteSources(postData: String?): ReturnData {
kotlin.runCatching { kotlin.runCatching {
GSON.fromJsonArray<BookSource>(postData)?.let { GSON.fromJsonArray<BookSource>(postData).getOrThrow()?.let {
it.forEach { source -> it.forEach { source ->
appDb.bookSourceDao.delete(source) appDb.bookSourceDao.delete(source)
} }
} }
}.onFailure {
return ReturnData().setErrorMsg(it.localizedMessage ?: "数据格式错误")
} }
return ReturnData().setData("已执行"/*okSources*/) return ReturnData().setData("已执行"/*okSources*/)
} }

@ -19,7 +19,8 @@ object AppLog {
mLogs.add(0, Triple(System.currentTimeMillis(), message, throwable)) mLogs.add(0, Triple(System.currentTimeMillis(), message, throwable))
if (throwable != null) { if (throwable != null) {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Log.e(message, throwable.stackTraceToString()) val stackTrace = Thread.currentThread().stackTrace
Log.e(stackTrace[3].className, message, throwable)
} }
} }
} }

@ -1,6 +1,7 @@
package io.legado.app.data.dao package io.legado.app.data.dao
import androidx.room.* import androidx.room.*
import io.legado.app.constant.AppConst
import io.legado.app.constant.BookType import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
@ -8,6 +9,16 @@ import kotlinx.coroutines.flow.Flow
@Dao @Dao
interface BookDao { interface BookDao {
@Query(
"""
select * from books where type != ${BookType.audio}
and origin != '${BookType.local}'
and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0
and (select show from book_groups where groupId = ${AppConst.bookGroupNoneId}) != 1
"""
)
fun flowRoot(): Flow<List<Book>>
@Query("SELECT * FROM books order by durChapterTime desc") @Query("SELECT * FROM books order by durChapterTime desc")
fun flowAll(): Flow<List<Book>> fun flowAll(): Flow<List<Book>>
@ -17,7 +28,13 @@ interface BookDao {
@Query("SELECT * FROM books WHERE origin = '${BookType.local}'") @Query("SELECT * FROM books WHERE origin = '${BookType.local}'")
fun flowLocal(): Flow<List<Book>> fun flowLocal(): Flow<List<Book>>
@Query("select * from books where type != ${BookType.audio} and origin != '${BookType.local}' and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0") @Query(
"""
select * from books where type != ${BookType.audio}
and origin != '${BookType.local}'
and ((SELECT sum(groupId) FROM book_groups where groupId > 0) & `group`) = 0
"""
)
fun flowNoGroup(): Flow<List<Book>> fun flowNoGroup(): Flow<List<Book>>
@Query("SELECT bookUrl FROM books WHERE origin = '${BookType.local}'") @Query("SELECT bookUrl FROM books WHERE origin = '${BookType.local}'")

@ -8,10 +8,7 @@ import io.legado.app.help.AppConfig
import io.legado.app.help.CacheManager import io.legado.app.help.CacheManager
import io.legado.app.help.JsExtensions import io.legado.app.help.JsExtensions
import io.legado.app.help.http.CookieStore import io.legado.app.help.http.CookieStore
import io.legado.app.utils.EncoderUtils import io.legado.app.utils.*
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray
import io.legado.app.utils.fromJsonObject
import javax.script.SimpleBindings import javax.script.SimpleBindings
/** /**
@ -30,7 +27,10 @@ interface BaseSource : JsExtensions {
fun getKey(): String fun getKey(): String
fun loginUi(): List<RowUi>? { fun loginUi(): List<RowUi>? {
return GSON.fromJsonArray(loginUi) return GSON.fromJsonArray<RowUi>(loginUi)
.onFailure {
it.printOnDebug()
}.getOrNull()
} }
fun getLoginJs(): String? { fun getLoginJs(): String? {
@ -64,7 +64,7 @@ interface BaseSource : JsExtensions {
evalJS(it.substring(4, it.lastIndexOf("<"))).toString() evalJS(it.substring(4, it.lastIndexOf("<"))).toString()
else -> it else -> it
} }
)?.let { map -> ).getOrNull()?.let { map ->
putAll(map) putAll(map)
} }
} }
@ -84,7 +84,7 @@ interface BaseSource : JsExtensions {
fun getLoginHeaderMap(): Map<String, String>? { fun getLoginHeaderMap(): Map<String, String>? {
val cache = getLoginHeader() ?: return null val cache = getLoginHeader() ?: return null
return GSON.fromJsonObject(cache) return GSON.fromJsonObject<Map<String, String>>(cache).getOrNull()
} }
/** /**
@ -117,7 +117,7 @@ interface BaseSource : JsExtensions {
} }
fun getLoginInfoMap(): Map<String, String>? { fun getLoginInfoMap(): Map<String, String>? {
return GSON.fromJsonObject(getLoginInfo()) return GSON.fromJsonObject<Map<String, String>>(getLoginInfo()).getOrNull()
} }
/** /**

@ -92,8 +92,8 @@ data class Book(
@delegate:Transient @delegate:Transient
@delegate:Ignore @delegate:Ignore
@IgnoredOnParcel @IgnoredOnParcel
override val variableMap by lazy { override val variableMap: HashMap<String, String> by lazy {
GSON.fromJsonObject<HashMap<String, String>>(variable) ?: HashMap() GSON.fromJsonObject<HashMap<String, String>>(variable).getOrNull() ?: hashMapOf()
} }
override fun putVariable(key: String, value: String?) { override fun putVariable(key: String, value: String?) {
@ -307,6 +307,6 @@ data class Book(
fun readConfigToString(config: ReadConfig?): String = GSON.toJson(config) fun readConfigToString(config: ReadConfig?): String = GSON.toJson(config)
@TypeConverter @TypeConverter
fun stringToReadConfig(json: String?) = GSON.fromJsonObject<ReadConfig>(json) fun stringToReadConfig(json: String?) = GSON.fromJsonObject<ReadConfig>(json).getOrNull()
} }
} }

@ -50,8 +50,8 @@ data class BookChapter(
@delegate:Transient @delegate:Transient
@delegate:Ignore @delegate:Ignore
@IgnoredOnParcel @IgnoredOnParcel
override val variableMap by lazy { override val variableMap: HashMap<String, String> by lazy {
GSON.fromJsonObject<HashMap<String, String>>(variable) ?: HashMap() GSON.fromJsonObject<HashMap<String, String>>(variable).getOrNull() ?: hashMapOf()
} }
override fun putVariable(key: String, value: String?) { override fun putVariable(key: String, value: String?) {

@ -108,7 +108,7 @@ data class BookSource(
} }
} }
if (ruleStr.isJsonArray()) { if (ruleStr.isJsonArray()) {
GSON.fromJsonArray<ExploreKind>(ruleStr)?.let { GSON.fromJsonArray<ExploreKind>(ruleStr).getOrThrow()?.let {
kinds.addAll(it) kinds.addAll(it)
} }
} else { } else {
@ -219,34 +219,44 @@ data class BookSource(
class Converters { class Converters {
@TypeConverter @TypeConverter
fun exploreRuleToString(exploreRule: ExploreRule?): String = GSON.toJson(exploreRule) fun exploreRuleToString(exploreRule: ExploreRule?): String =
GSON.toJson(exploreRule)
@TypeConverter @TypeConverter
fun stringToExploreRule(json: String?) = GSON.fromJsonObject<ExploreRule>(json) fun stringToExploreRule(json: String?) =
GSON.fromJsonObject<ExploreRule>(json).getOrNull()
@TypeConverter @TypeConverter
fun searchRuleToString(searchRule: SearchRule?): String = GSON.toJson(searchRule) fun searchRuleToString(searchRule: SearchRule?): String =
GSON.toJson(searchRule)
@TypeConverter @TypeConverter
fun stringToSearchRule(json: String?) = GSON.fromJsonObject<SearchRule>(json) fun stringToSearchRule(json: String?) =
GSON.fromJsonObject<SearchRule>(json).getOrNull()
@TypeConverter @TypeConverter
fun bookInfoRuleToString(bookInfoRule: BookInfoRule?): String = GSON.toJson(bookInfoRule) fun bookInfoRuleToString(bookInfoRule: BookInfoRule?): String =
GSON.toJson(bookInfoRule)
@TypeConverter @TypeConverter
fun stringToBookInfoRule(json: String?) = GSON.fromJsonObject<BookInfoRule>(json) fun stringToBookInfoRule(json: String?) =
GSON.fromJsonObject<BookInfoRule>(json).getOrNull()
@TypeConverter @TypeConverter
fun tocRuleToString(tocRule: TocRule?): String = GSON.toJson(tocRule) fun tocRuleToString(tocRule: TocRule?): String =
GSON.toJson(tocRule)
@TypeConverter @TypeConverter
fun stringToTocRule(json: String?) = GSON.fromJsonObject<TocRule>(json) fun stringToTocRule(json: String?) =
GSON.fromJsonObject<TocRule>(json).getOrNull()
@TypeConverter @TypeConverter
fun contentRuleToString(contentRule: ContentRule?): String = GSON.toJson(contentRule) fun contentRuleToString(contentRule: ContentRule?): String =
GSON.toJson(contentRule)
@TypeConverter @TypeConverter
fun stringToContentRule(json: String?) = GSON.fromJsonObject<ContentRule>(json) fun stringToContentRule(json: String?) =
GSON.fromJsonObject<ContentRule>(json).getOrNull()
} }
} }

@ -36,8 +36,8 @@ data class RssArticle(
@delegate:Transient @delegate:Transient
@delegate:Ignore @delegate:Ignore
@IgnoredOnParcel @IgnoredOnParcel
override val variableMap by lazy { override val variableMap: HashMap<String, String> by lazy {
GSON.fromJsonObject<HashMap<String, String>>(variable) ?: HashMap() GSON.fromJsonObject<HashMap<String, String>>(variable).getOrNull() ?: hashMapOf()
} }
override fun putVariable(key: String, value: String?) { override fun putVariable(key: String, value: String?) {

@ -29,7 +29,7 @@ data class RssStar(
@delegate:Ignore @delegate:Ignore
@IgnoredOnParcel @IgnoredOnParcel
override val variableMap by lazy { override val variableMap by lazy {
GSON.fromJsonObject<HashMap<String, String>>(variable) ?: HashMap() GSON.fromJsonObject<HashMap<String, String>>(variable).getOrNull() ?: hashMapOf()
} }
override fun putVariable(key: String, value: String?) { override fun putVariable(key: String, value: String?) {

@ -59,8 +59,8 @@ data class SearchBook(
@delegate:Transient @delegate:Transient
@delegate:Ignore @delegate:Ignore
@IgnoredOnParcel @IgnoredOnParcel
override val variableMap by lazy { override val variableMap: HashMap<String, String> by lazy {
GSON.fromJsonObject<HashMap<String, String>>(variable) ?: HashMap() GSON.fromJsonObject<HashMap<String, String>>(variable).getOrNull() ?: HashMap()
} }
override fun putVariable(key: String, value: String?) { override fun putVariable(key: String, value: String?) {

@ -79,7 +79,7 @@ class ContentProcessor private constructor(
try { try {
val name = Pattern.quote(book.name) val name = Pattern.quote(book.name)
val title = Pattern.quote(chapter.title) val title = Pattern.quote(chapter.title)
val titleRegex = "^(\\s|\\p{P}|${name})*${title}(\\s|\\p{P})+".toRegex() val titleRegex = "^(\\s|\\p{P}|${name})*${title}(\\s)+".toRegex()
mContent = mContent.replace(titleRegex, "") mContent = mContent.replace(titleRegex, "")
} catch (e: Exception) { } catch (e: Exception) {
AppLog.put("去除重复标题出错\n${e.localizedMessage}", e) AppLog.put("去除重复标题出错\n${e.localizedMessage}", e)

@ -28,7 +28,8 @@ object DefaultData {
appCtx.assets.open("defaultData${File.separator}${ReadBookConfig.configFileName}") appCtx.assets.open("defaultData${File.separator}${ReadBookConfig.configFileName}")
.readBytes() .readBytes()
) )
GSON.fromJsonArray(json)!! GSON.fromJsonArray<ReadBookConfig.Config>(json).getOrNull()
?: emptyList()
} }
val txtTocRules: List<TxtTocRule> by lazy { val txtTocRules: List<TxtTocRule> by lazy {
@ -36,7 +37,7 @@ object DefaultData {
appCtx.assets.open("defaultData${File.separator}$txtTocRuleFileName") appCtx.assets.open("defaultData${File.separator}$txtTocRuleFileName")
.readBytes() .readBytes()
) )
GSON.fromJsonArray(json)!! GSON.fromJsonArray<TxtTocRule>(json).getOrNull() ?: emptyList()
} }
val themeConfigs: List<ThemeConfig.Config> by lazy { val themeConfigs: List<ThemeConfig.Config> by lazy {
@ -44,15 +45,17 @@ object DefaultData {
appCtx.assets.open("defaultData${File.separator}${ThemeConfig.configFileName}") appCtx.assets.open("defaultData${File.separator}${ThemeConfig.configFileName}")
.readBytes() .readBytes()
) )
GSON.fromJsonArray(json)!! GSON.fromJsonArray<ThemeConfig.Config>(json).getOrNull() ?: emptyList()
} }
val rssSources: List<RssSource> by lazy { val rssSources: List<RssSource> by lazy {
val json = String( kotlin.runCatching {
appCtx.assets.open("defaultData${File.separator}rssSources.json") val json = String(
.readBytes() appCtx.assets.open("defaultData${File.separator}rssSources.json")
) .readBytes()
RssSource.fromJsonArray(json) )
RssSource.fromJsonArray(json)
}.getOrDefault(emptyList())
} }
fun importDefaultHttpTTS() { fun importDefaultHttpTTS() {

@ -97,7 +97,7 @@ interface JsExtensions {
fun connect(urlStr: String, header: String?): StrResponse { fun connect(urlStr: String, header: String?): StrResponse {
return runBlocking { return runBlocking {
val headerMap = GSON.fromJsonObject<Map<String, String>>(header) val headerMap = GSON.fromJsonObject<Map<String, String>>(header).getOrNull()
val analyzeUrl = AnalyzeUrl(urlStr, headerMapF = headerMap, source = getSource()) val analyzeUrl = AnalyzeUrl(urlStr, headerMapF = headerMap, source = getSource())
kotlin.runCatching { kotlin.runCatching {
analyzeUrl.getStrResponseAwait() analyzeUrl.getStrResponseAwait()

@ -6,15 +6,17 @@ import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import androidx.annotation.Keep import androidx.annotation.Keep
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppLog
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.NoStackTraceException
import io.legado.app.ui.book.read.page.provider.ChapterProvider import io.legado.app.ui.book.read.page.provider.ChapterProvider
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import splitties.init.appCtx import splitties.init.appCtx
import java.io.File import java.io.File
import java.io.IOException
/** /**
* 阅读界面配置 * 阅读界面配置
@ -60,9 +62,9 @@ object ReadBookConfig {
if (configFile.exists()) { if (configFile.exists()) {
try { try {
val json = configFile.readText() val json = configFile.readText()
configs = GSON.fromJsonArray(json) configs = GSON.fromJsonArray<Config>(json).getOrThrow()
} catch (e: Exception) { } catch (e: Exception) {
e.printOnDebug() AppLog.put("读取排版配置文件出错", e)
} }
} }
(configs ?: DefaultData.readConfigs).let { (configs ?: DefaultData.readConfigs).let {
@ -77,7 +79,7 @@ object ReadBookConfig {
if (configFile.exists()) { if (configFile.exists()) {
try { try {
val json = configFile.readText() val json = configFile.readText()
c = GSON.fromJsonObject(json) c = GSON.fromJsonObject<Config>(json).getOrThrow()
} catch (e: Exception) { } catch (e: Exception) {
e.printOnDebug() e.printOnDebug()
} }
@ -325,7 +327,7 @@ object ReadBookConfig {
} }
fun getExportConfig(): Config { fun getExportConfig(): Config {
val exportConfig = GSON.fromJsonObject<Config>(GSON.toJson(durConfig))!! val exportConfig = durConfig.deepCopy()
if (shareLayout) { if (shareLayout) {
exportConfig.textFont = shareConfig.textFont exportConfig.textFont = shareConfig.textFont
exportConfig.textBold = shareConfig.textBold exportConfig.textBold = shareConfig.textBold
@ -364,6 +366,7 @@ object ReadBookConfig {
return exportConfig return exportConfig
} }
@Throws(IOException::class)
suspend fun import(byteArray: ByteArray): Config { suspend fun import(byteArray: ByteArray): Config {
return withContext(IO) { return withContext(IO) {
val configZipPath = FileUtils.getPath(appCtx.externalCache, "readConfig.zip") val configZipPath = FileUtils.getPath(appCtx.externalCache, "readConfig.zip")
@ -376,7 +379,8 @@ object ReadBookConfig {
ZipUtils.unzipFile(zipFile, FileUtils.createFolderIfNotExist(configDirPath)) ZipUtils.unzipFile(zipFile, FileUtils.createFolderIfNotExist(configDirPath))
val configDir = FileUtils.createFolderIfNotExist(configDirPath) val configDir = FileUtils.createFolderIfNotExist(configDirPath)
val configFile = configDir.getFile(configFileName) val configFile = configDir.getFile(configFileName)
val config: Config = GSON.fromJsonObject(configFile.readText())!! val config: Config = GSON.fromJsonObject<Config>(configFile.readText()).getOrThrow()
?: throw NoStackTraceException("排版配置格式错误")
if (config.textFont.isNotEmpty()) { if (config.textFont.isNotEmpty()) {
val fontName = FileUtils.getName(config.textFont) val fontName = FileUtils.getName(config.textFont)
val fontPath = val fontPath =

@ -3,6 +3,7 @@ package io.legado.app.help
import androidx.annotation.Keep import androidx.annotation.Keep
import com.jayway.jsonpath.JsonPath import com.jayway.jsonpath.JsonPath
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.AppLog
import io.legado.app.constant.BookType import io.legado.app.constant.BookType
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.data.entities.rule.* import io.legado.app.data.entities.rule.*
@ -31,11 +32,10 @@ object SourceAnalyzer {
fun jsonToBookSource(json: String): BookSource? { fun jsonToBookSource(json: String): BookSource? {
val source = BookSource() val source = BookSource()
val sourceAny = try { val sourceAny = GSON.fromJsonObject<BookSourceAny>(json.trim())
GSON.fromJsonObject<BookSourceAny>(json.trim()) .onFailure {
} catch (e: Exception) { AppLog.put("转化书源出错", it)
null }.getOrNull()
}
try { try {
if (sourceAny?.ruleToc == null) { if (sourceAny?.ruleToc == null) {
source.apply { source.apply {
@ -132,30 +132,40 @@ object SourceAnalyzer {
source.weight = sourceAny.weight source.weight = sourceAny.weight
source.exploreUrl = sourceAny.exploreUrl source.exploreUrl = sourceAny.exploreUrl
source.ruleExplore = if (sourceAny.ruleExplore is String) { source.ruleExplore = if (sourceAny.ruleExplore is String) {
GSON.fromJsonObject(sourceAny.ruleExplore.toString()) GSON.fromJsonObject<ExploreRule>(sourceAny.ruleExplore.toString())
.getOrNull()
} else { } else {
GSON.fromJsonObject(GSON.toJson(sourceAny.ruleExplore)) GSON.fromJsonObject<ExploreRule>(GSON.toJson(sourceAny.ruleExplore))
.getOrNull()
} }
source.searchUrl = sourceAny.searchUrl source.searchUrl = sourceAny.searchUrl
source.ruleSearch = if (sourceAny.ruleSearch is String) { source.ruleSearch = if (sourceAny.ruleSearch is String) {
GSON.fromJsonObject(sourceAny.ruleSearch.toString()) GSON.fromJsonObject<SearchRule>(sourceAny.ruleSearch.toString())
.getOrNull()
} else { } else {
GSON.fromJsonObject(GSON.toJson(sourceAny.ruleSearch)) GSON.fromJsonObject<SearchRule>(GSON.toJson(sourceAny.ruleSearch))
.getOrNull()
} }
source.ruleBookInfo = if (sourceAny.ruleBookInfo is String) { source.ruleBookInfo = if (sourceAny.ruleBookInfo is String) {
GSON.fromJsonObject(sourceAny.ruleBookInfo.toString()) GSON.fromJsonObject<BookInfoRule>(sourceAny.ruleBookInfo.toString())
.getOrNull()
} else { } else {
GSON.fromJsonObject(GSON.toJson(sourceAny.ruleBookInfo)) GSON.fromJsonObject<BookInfoRule>(GSON.toJson(sourceAny.ruleBookInfo))
.getOrNull()
} }
source.ruleToc = if (sourceAny.ruleToc is String) { source.ruleToc = if (sourceAny.ruleToc is String) {
GSON.fromJsonObject(sourceAny.ruleToc.toString()) GSON.fromJsonObject<TocRule>(sourceAny.ruleToc.toString())
.getOrNull()
} else { } else {
GSON.fromJsonObject(GSON.toJson(sourceAny.ruleToc)) GSON.fromJsonObject<TocRule>(GSON.toJson(sourceAny.ruleToc))
.getOrNull()
} }
source.ruleContent = if (sourceAny.ruleContent is String) { source.ruleContent = if (sourceAny.ruleContent is String) {
GSON.fromJsonObject(sourceAny.ruleContent.toString()) GSON.fromJsonObject<ContentRule>(sourceAny.ruleContent.toString())
.getOrNull()
} else { } else {
GSON.fromJsonObject(GSON.toJson(sourceAny.ruleContent)) GSON.fromJsonObject<ContentRule>(GSON.toJson(sourceAny.ruleContent))
.getOrNull()
} }
} }
} catch (e: Exception) { } catch (e: Exception) {

@ -83,10 +83,11 @@ object ThemeConfig {
} }
fun addConfig(json: String): Boolean { fun addConfig(json: String): Boolean {
GSON.fromJsonObject<Config>(json.trim { it < ' ' })?.let { GSON.fromJsonObject<Config>(json.trim { it < ' ' }).getOrNull()
addConfig(it) ?.let {
return true addConfig(it)
} return true
}
return false return false
} }
@ -106,7 +107,7 @@ object ThemeConfig {
if (configFile.exists()) { if (configFile.exists()) {
kotlin.runCatching { kotlin.runCatching {
val json = configFile.readText() val json = configFile.readText()
return GSON.fromJsonArray(json) return GSON.fromJsonArray<Config>(json).getOrThrow()
}.onFailure { }.onFailure {
it.printOnDebug() it.printOnDebug()
} }

@ -172,9 +172,7 @@ object AppWebDav {
WebDav(url).download()?.let { byteArray -> WebDav(url).download()?.let { byteArray ->
val json = String(byteArray) val json = String(byteArray)
if (json.isJson()) { if (json.isJson()) {
GSON.fromJsonObject<BookProgress>(json)?.let { return GSON.fromJsonObject<BookProgress>(json).getOrNull()
return it
}
} }
} }
} }

@ -13,7 +13,7 @@ abstract class BackupRestore {
val ignoreConfig: HashMap<String, Boolean> by lazy { val ignoreConfig: HashMap<String, Boolean> by lazy {
val file = FileUtils.createFileIfNotExist(ignoreConfigPath) val file = FileUtils.createFileIfNotExist(ignoreConfigPath)
val json = file.readText() val json = file.readText()
GSON.fromJsonObject<HashMap<String, Boolean>>(json) ?: hashMapOf() GSON.fromJsonObject<HashMap<String, Boolean>>(json).getOrNull() ?: hashMapOf()
} }
//忽略key //忽略key

@ -20,12 +20,9 @@ object OldReplace {
} }
private fun jsonToReplaceRule(json: String): ReplaceRule? { private fun jsonToReplaceRule(json: String): ReplaceRule? {
var replaceRule: ReplaceRule? = null val replaceRule: ReplaceRule? = GSON.fromJsonObject<ReplaceRule>(json.trim()).getOrNull()
runCatching { runCatching {
replaceRule = GSON.fromJsonObject<ReplaceRule>(json.trim()) if (replaceRule == null || replaceRule.pattern.isBlank()) {
}
runCatching {
if (replaceRule == null || replaceRule?.pattern.isNullOrBlank()) {
val jsonItem = jsonPath.parse(json.trim()) val jsonItem = jsonPath.parse(json.trim())
val rule = ReplaceRule() val rule = ReplaceRule()
rule.id = jsonItem.readLong("$.id") ?: System.currentTimeMillis() rule.id = jsonItem.readLong("$.id") ?: System.currentTimeMillis()

@ -6,6 +6,7 @@ import androidx.documentfile.provider.DocumentFile
import io.legado.app.BuildConfig import io.legado.app.BuildConfig
import io.legado.app.R import io.legado.app.R
import io.legado.app.constant.AppConst.androidId import io.legado.app.constant.AppConst.androidId
import io.legado.app.constant.AppLog
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.data.appDb import io.legado.app.data.appDb
@ -20,7 +21,6 @@ import kotlinx.coroutines.Dispatchers.Main
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import splitties.init.appCtx import splitties.init.appCtx
import java.io.File import java.io.File
@ -193,9 +193,10 @@ object Restore : BackupRestore() {
try { try {
val file = FileUtils.createFileIfNotExist(path + File.separator + fileName) val file = FileUtils.createFileIfNotExist(path + File.separator + fileName)
val json = file.readText() val json = file.readText()
return GSON.fromJsonArray(json) return GSON.fromJsonArray<T>(json).getOrThrow()
} catch (e: Exception) { } catch (e: Exception) {
e.printOnDebug() AppLog.put("$fileName\n读取解析出错\n${e.localizedMessage}", e)
appCtx.toastOnUi("$fileName\n读取文件出错\n${e.localizedMessage}")
} }
return null return null
} }

@ -10,6 +10,7 @@ import io.legado.app.data.appDb
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.BookSource import io.legado.app.data.entities.BookSource
import io.legado.app.help.ContentProcessor
import io.legado.app.help.coroutine.Coroutine import io.legado.app.help.coroutine.Coroutine
import io.legado.app.service.AudioPlayService import io.legado.app.service.AudioPlayService
import io.legado.app.utils.postEvent import io.legado.app.utils.postEvent
@ -148,7 +149,9 @@ object AudioPlay {
book.durChapterTime = System.currentTimeMillis() book.durChapterTime = System.currentTimeMillis()
Coroutine.async { Coroutine.async {
appDb.bookChapterDao.getChapter(book.bookUrl, book.durChapterIndex)?.let { appDb.bookChapterDao.getChapter(book.bookUrl, book.durChapterIndex)?.let {
book.durChapterTitle = it.title book.durChapterTitle = it.getDisplayTitle(
ContentProcessor.get(book.name, book.origin).getTitleReplaceRules()
)
} }
book.save() book.save()
} }

@ -25,7 +25,6 @@ import kotlinx.coroutines.delay
import splitties.init.appCtx import splitties.init.appCtx
@Suppress("MemberVisibilityCanBePrivate") @Suppress("MemberVisibilityCanBePrivate")
object ReadBook : CoroutineScope by MainScope() { object ReadBook : CoroutineScope by MainScope() {
var book: Book? = null var book: Book? = null
@ -422,7 +421,9 @@ object ReadBook : CoroutineScope by MainScope() {
book.durChapterIndex = durChapterIndex book.durChapterIndex = durChapterIndex
book.durChapterPos = durChapterPos book.durChapterPos = durChapterPos
appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let { appDb.bookChapterDao.getChapter(book.bookUrl, durChapterIndex)?.let {
book.durChapterTitle = it.title book.durChapterTitle = it.getDisplayTitle(
ContentProcessor.get(book.name, book.origin).getTitleReplaceRules()
)
} }
appDb.bookDao.update(book) appDb.bookDao.update(book)
} }

@ -341,8 +341,11 @@ class AnalyzeRule(
val putMatcher = putPattern.matcher(vRuleStr) val putMatcher = putPattern.matcher(vRuleStr)
while (putMatcher.find()) { while (putMatcher.find()) {
vRuleStr = vRuleStr.replace(putMatcher.group(), "") vRuleStr = vRuleStr.replace(putMatcher.group(), "")
val map = GSON.fromJsonObject<Map<String, String>>(putMatcher.group(1)) GSON.fromJsonObject<Map<String, String>>(putMatcher.group(1))
map?.let { putMap.putAll(map) } .getOrNull()
?.let {
putMap.putAll(it)
}
} }
return vRuleStr return vRuleStr
} }
@ -379,7 +382,7 @@ class AnalyzeRule(
* 分解规则生成规则列表 * 分解规则生成规则列表
*/ */
fun splitSourceRule(ruleStr: String?, allInOne: Boolean = false): List<SourceRule> { fun splitSourceRule(ruleStr: String?, allInOne: Boolean = false): List<SourceRule> {
if (ruleStr.isNullOrEmpty()) return ArrayList<SourceRule>() if (ruleStr.isNullOrEmpty()) return emptyList()
val ruleList = ArrayList<SourceRule>() val ruleList = ArrayList<SourceRule>()
var mMode: Mode = Mode.Default var mMode: Mode = Mode.Default
var start = 0 var start = 0
@ -597,9 +600,9 @@ class AnalyzeRule(
private fun isRule(ruleStr: String): Boolean { private fun isRule(ruleStr: String): Boolean {
return ruleStr.startsWith('@') //js首个字符不可能是@,除非是装饰器,所以@开头规定为规则 return ruleStr.startsWith('@') //js首个字符不可能是@,除非是装饰器,所以@开头规定为规则
|| ruleStr.startsWith("$.") || ruleStr.startsWith("$.")
|| ruleStr.startsWith("$[") || ruleStr.startsWith("$[")
|| ruleStr.startsWith("//") || ruleStr.startsWith("//")
} }
} }

@ -167,34 +167,35 @@ class AnalyzeUrl(
baseUrl = it baseUrl = it
} }
if (urlNoOption.length != ruleUrl.length) { if (urlNoOption.length != ruleUrl.length) {
GSON.fromJsonObject<UrlOption>(ruleUrl.substring(urlMatcher.end()))?.let { option -> GSON.fromJsonObject<UrlOption>(ruleUrl.substring(urlMatcher.end())).getOrNull()
option.method?.let { ?.let { option ->
if (it.equals("POST", true)) method = RequestMethod.POST option.method?.let {
} if (it.equals("POST", true)) method = RequestMethod.POST
option.headers?.let { headers -> }
if (headers is Map<*, *>) { option.headers?.let { headers ->
headers.forEach { entry -> if (headers is Map<*, *>) {
headerMap[entry.key.toString()] = entry.value.toString() headers.forEach { entry ->
headerMap[entry.key.toString()] = entry.value.toString()
}
} else if (headers is String) {
GSON.fromJsonObject<Map<String, String>>(headers).getOrNull()
?.let { headerMap.putAll(it) }
} }
} else if (headers is String) {
GSON.fromJsonObject<Map<String, String>>(headers)
?.let { headerMap.putAll(it) }
} }
} option.body?.let {
option.body?.let { body = if (it is String) it else GSON.toJson(it)
body = if (it is String) it else GSON.toJson(it) }
} type = option.type
type = option.type charset = option.charset
charset = option.charset retry = option.retry
retry = option.retry useWebView = option.webView?.toString()?.isNotBlank() == true
useWebView = option.webView?.toString()?.isNotBlank() == true webJs = option.webJs
webJs = option.webJs option.js?.let { jsStr ->
option.js?.let { jsStr -> evalJS(jsStr, url)?.toString()?.let {
evalJS(jsStr, url)?.toString()?.let { url = it
url = it }
} }
} }
}
} }
headerMap[UA_NAME] ?: let { headerMap[UA_NAME] ?: let {
headerMap[UA_NAME] = AppConfig.userAgent headerMap[UA_NAME] = AppConfig.userAgent
@ -510,7 +511,7 @@ class AnalyzeUrl(
suspend fun upload(fileName: String, file: Any, contentType: String): StrResponse { suspend fun upload(fileName: String, file: Any, contentType: String): StrResponse {
return getProxyClient(proxy).newCallStrResponse(retry) { return getProxyClient(proxy).newCallStrResponse(retry) {
url(urlNoQuery) url(urlNoQuery)
val bodyMap = GSON.fromJsonObject<HashMap<String, Any>>(body)!! val bodyMap = GSON.fromJsonObject<HashMap<String, Any>>(body).getOrNull()!!
bodyMap.forEach { entry -> bodyMap.forEach { entry ->
if (entry.value.toString() == "fileRequest") { if (entry.value.toString() == "fileRequest") {
bodyMap[entry.key] = mapOf( bodyMap[entry.key] = mapOf(

@ -141,8 +141,8 @@ object LocalBook {
//将文件名注入到脚步的src变量中 //将文件名注入到脚步的src变量中
SimpleBindings().also { it["src"] = tempFileName } SimpleBindings().also { it["src"] = tempFileName }
).toString() ).toString()
val bookMess = val bookMess = GSON.fromJsonObject<HashMap<String, String>>(jsonStr)
GSON.fromJsonObject<HashMap<String, String>>(jsonStr) ?: HashMap() .getOrThrow() ?: HashMap()
name = bookMess["name"] ?: tempFileName name = bookMess["name"] ?: tempFileName
author = bookMess["author"]?.takeIf { it.length != tempFileName.length } ?: "" author = bookMess["author"]?.takeIf { it.length != tempFileName.length } ?: ""
} catch (e: Exception) { } catch (e: Exception) {

@ -37,7 +37,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
@Synchronized @Synchronized
private fun initTts() { private fun initTts() {
ttsInitFinish = false ttsInitFinish = false
val engine = GSON.fromJsonObject<SelectItem<String>>(ReadAloud.ttsEngine)?.value val engine = GSON.fromJsonObject<SelectItem<String>>(ReadAloud.ttsEngine).getOrNull()?.value
textToSpeech = if (engine.isNullOrBlank()) { textToSpeech = if (engine.isNullOrBlank()) {
TextToSpeech(this, this) TextToSpeech(this, this)
} else { } else {

@ -19,11 +19,11 @@ abstract class BaseAssociationViewModel(application: Application) : BaseViewMode
fun importTextTocRule(json: String, finally: (title: String, msg: String) -> Unit) { fun importTextTocRule(json: String, finally: (title: String, msg: String) -> Unit) {
execute { execute {
if (json.isJsonArray()) { if (json.isJsonArray()) {
GSON.fromJsonArray<TxtTocRule>(json)?.let { GSON.fromJsonArray<TxtTocRule>(json).getOrThrow()?.let {
appDb.txtTocRuleDao.insert(*it.toTypedArray()) appDb.txtTocRuleDao.insert(*it.toTypedArray())
} ?: throw NoStackTraceException("格式不对") } ?: throw NoStackTraceException("格式不对")
} else { } else {
GSON.fromJsonObject<TxtTocRule>(json)?.let { GSON.fromJsonObject<TxtTocRule>(json).getOrThrow()?.let {
appDb.txtTocRuleDao.insert(it) appDb.txtTocRuleDao.insert(it)
} ?: throw NoStackTraceException("格式不对") } ?: throw NoStackTraceException("格式不对")
} }
@ -63,11 +63,11 @@ abstract class BaseAssociationViewModel(application: Application) : BaseViewMode
fun importTheme(json: String, finally: (title: String, msg: String) -> Unit) { fun importTheme(json: String, finally: (title: String, msg: String) -> Unit) {
execute { execute {
if (json.isJsonArray()) { if (json.isJsonArray()) {
GSON.fromJsonArray<ThemeConfig.Config>(json)?.forEach { GSON.fromJsonArray<ThemeConfig.Config>(json).getOrThrow()?.forEach {
ThemeConfig.addConfig(it) ThemeConfig.addConfig(it)
} ?: throw NoStackTraceException("格式不对") } ?: throw NoStackTraceException("格式不对")
} else { } else {
GSON.fromJsonObject<ThemeConfig.Config>(json)?.let { GSON.fromJsonObject<ThemeConfig.Config>(json).getOrThrow()?.let {
ThemeConfig.addConfig(it) ThemeConfig.addConfig(it)
} ?: throw NoStackTraceException("格式不对") } ?: throw NoStackTraceException("格式不对")
} }

@ -181,7 +181,7 @@ class ImportReplaceRuleDialog() : BaseDialogFragment(R.layout.dialog_recycler_vi
override fun onCodeSave(code: String, requestId: String?) { override fun onCodeSave(code: String, requestId: String?) {
requestId?.toInt()?.let { requestId?.toInt()?.let {
GSON.fromJsonObject<ReplaceRule>(code)?.let { rule -> GSON.fromJsonObject<ReplaceRule>(code).getOrNull()?.let { rule ->
viewModel.allRules[it] = rule viewModel.allRules[it] = rule
adapter.setItem(it, rule) adapter.setItem(it, rule)
} }

@ -42,7 +42,7 @@ class ChangeCoverDialog() : BaseDialogFragment(R.layout.dialog_change_cover),
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
setLayout(0.98f, ViewGroup.LayoutParams.MATCH_PARENT) setLayout(1f, ViewGroup.LayoutParams.MATCH_PARENT)
} }
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {

@ -57,7 +57,7 @@ class ChangeBookSourceDialog() : BaseDialogFragment(R.layout.dialog_book_change_
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
setLayout(0.98f, ViewGroup.LayoutParams.MATCH_PARENT) setLayout(1f, ViewGroup.LayoutParams.MATCH_PARENT)
} }
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {

@ -81,7 +81,7 @@ class ChangeChapterSourceDialog() : BaseDialogFragment(R.layout.dialog_chapter_c
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
setLayout(0.98f, ViewGroup.LayoutParams.MATCH_PARENT) setLayout(1f, ViewGroup.LayoutParams.MATCH_PARENT)
dialog?.setOnKeyListener(this) dialog?.setOnKeyListener(this)
} }

@ -6,7 +6,6 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import com.google.android.material.snackbar.Snackbar
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.data.appDb import io.legado.app.data.appDb
@ -22,8 +21,8 @@ import io.legado.app.ui.widget.recycler.ItemTouchCallback
import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.ACache import io.legado.app.utils.ACache
import io.legado.app.utils.setEdgeEffectColor import io.legado.app.utils.setEdgeEffectColor
import io.legado.app.utils.snackbar
import io.legado.app.utils.splitNotBlank import io.legado.app.utils.splitNotBlank
import io.legado.app.utils.toastOnUi
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -184,10 +183,8 @@ class TxtTocRuleActivity : VMBaseActivity<ActivityTxtTocRuleBinding, TxtTocRuleV
cacheUrls.add(0, it) cacheUrls.add(0, it)
aCache.put(importTocRuleKey, cacheUrls.joinToString(",")) aCache.put(importTocRuleKey, cacheUrls.joinToString(","))
} }
Snackbar.make(binding.root, R.string.importing, Snackbar.LENGTH_INDEFINITE)
.show()
viewModel.importOnLine(it) { msg -> viewModel.importOnLine(it) { msg ->
binding.root.snackbar(msg) toastOnUi(msg)
} }
} }
} }

@ -42,14 +42,14 @@ class TxtTocRuleViewModel(app: Application) : BaseViewModel(app) {
okHttpClient.newCallResponseBody { okHttpClient.newCallResponseBody {
url(url) url(url)
}.text("utf-8").let { json -> }.text("utf-8").let { json ->
GSON.fromJsonArray<TxtTocRule>(json)?.let { GSON.fromJsonArray<TxtTocRule>(json).getOrThrow()?.let {
appDb.txtTocRuleDao.insert(*it.toTypedArray()) appDb.txtTocRuleDao.insert(*it.toTypedArray())
} }
} }
}.onSuccess { }.onSuccess {
finally("导入成功") finally("导入成功")
}.onError { }.onError {
finally("导入失败") finally("导入失败\n${it.localizedMessage}")
} }
} }

@ -36,7 +36,7 @@ class ContentEditDialog : BaseDialogFragment(R.layout.dialog_content_edit) {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
setLayout(0.96f, ViewGroup.LayoutParams.MATCH_PARENT) setLayout(1f, ViewGroup.LayoutParams.MATCH_PARENT)
} }
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
@ -80,7 +80,7 @@ class ContentEditDialog : BaseDialogFragment(R.layout.dialog_content_edit) {
ReadBook.loadContent(ReadBook.durChapterIndex, resetPageOffset = false) ReadBook.loadContent(ReadBook.durChapterIndex, resetPageOffset = false)
} }
R.id.menu_copy_all -> requireContext() R.id.menu_copy_all -> requireContext()
.sendToClip("${binding.toolBar.title}\n${viewModel.content}") .sendToClip("${binding.toolBar.title}\n${binding.contentView.text}")
} }
return@setOnMenuItemClickListener true return@setOnMenuItemClickListener true
} }
@ -129,6 +129,7 @@ class ContentEditDialog : BaseDialogFragment(R.layout.dialog_content_edit) {
.joinToString("\n") .joinToString("\n")
} }
}.onSuccess { }.onSuccess {
content = it
success.invoke(it ?: "") success.invoke(it ?: "")
} }
} }

@ -26,7 +26,7 @@ class PhotoDialog() : BaseDialogFragment(R.layout.dialog_photo_view) {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
setLayout(0.98f, 0.98f) setLayout(1f, 1f)
} }
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {

@ -227,10 +227,8 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
if (index < ReadBook.chapterSize) { if (index < ReadBook.chapterSize) {
ReadBook.clearTextChapter() ReadBook.clearTextChapter()
ReadBook.callBack?.upContent() ReadBook.callBack?.upContent()
if (index != ReadBook.durChapterIndex) { ReadBook.durChapterIndex = index
ReadBook.durChapterIndex = index ReadBook.durChapterPos = durChapterPos
ReadBook.durChapterPos = durChapterPos
}
ReadBook.saveRead() ReadBook.saveRead()
ReadBook.loadContent(resetPageOffset = true) { ReadBook.loadContent(resetPageOffset = true) {
success?.invoke() success?.invoke()

@ -65,7 +65,7 @@ class ReadAloudConfigDialog : DialogFragment() {
return appDb.httpTTSDao.getName(ttsEngine.toLong()) return appDb.httpTTSDao.getName(ttsEngine.toLong())
?: getString(R.string.system_tts) ?: getString(R.string.system_tts)
} }
return GSON.fromJsonObject<SelectItem<String>>(ttsEngine)?.title return GSON.fromJsonObject<SelectItem<String>>(ttsEngine).getOrNull()?.title
?: getString(R.string.system_tts) ?: getString(R.string.system_tts)
} }

@ -95,8 +95,8 @@ class SpeakEngineDialog(val callBack: CallBack) : BaseDialogFragment(R.layout.di
labelSys.visible() labelSys.visible()
cbName.text = engine.label cbName.text = engine.label
cbName.tag = engine.name cbName.tag = engine.name
cbName.isChecked = cbName.isChecked = GSON.fromJsonObject<SelectItem<String>>(ttsEngine)
GSON.fromJsonObject<SelectItem<String>>(ttsEngine)?.value == cbName.tag .getOrNull()?.value == cbName.tag
cbName.setOnClickListener { cbName.setOnClickListener {
upTts(GSON.toJson(SelectItem(engine.label, engine.name))) upTts(GSON.toJson(SelectItem(engine.label, engine.name)))
} }
@ -192,7 +192,8 @@ class SpeakEngineDialog(val callBack: CallBack) : BaseDialogFragment(R.layout.di
private fun upTts(tts: String) { private fun upTts(tts: String) {
ttsEngine = tts ttsEngine = tts
sysTtsViews.forEach { sysTtsViews.forEach {
it.isChecked = GSON.fromJsonObject<SelectItem<String>>(ttsEngine)?.value == it.tag it.isChecked = GSON.fromJsonObject<SelectItem<String>>(ttsEngine)
.getOrNull()?.value == it.tag
} }
adapter.notifyItemRangeChanged(adapter.getHeaderCount(), adapter.itemCount) adapter.notifyItemRangeChanged(adapter.getHeaderCount(), adapter.itemCount)
} }

@ -10,7 +10,6 @@ import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.viewModels import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseDialogFragment import io.legado.app.base.BaseDialogFragment
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
@ -154,10 +153,8 @@ class TocRegexDialog() : BaseDialogFragment(R.layout.dialog_toc_regex),
cacheUrls.add(0, it) cacheUrls.add(0, it)
aCache.put(importTocRuleKey, cacheUrls.joinToString(",")) aCache.put(importTocRuleKey, cacheUrls.joinToString(","))
} }
Snackbar.make(binding.toolBar, R.string.importing, Snackbar.LENGTH_INDEFINITE)
.show()
viewModel.importOnLine(it) { msg -> viewModel.importOnLine(it) { msg ->
binding.toolBar.snackbar(msg) toastOnUi(msg)
} }
} }
} }

@ -33,14 +33,14 @@ class TocRegexViewModel(application: Application) : BaseViewModel(application) {
okHttpClient.newCallResponseBody { okHttpClient.newCallResponseBody {
url(url) url(url)
}.text("utf-8").let { json -> }.text("utf-8").let { json ->
GSON.fromJsonArray<TxtTocRule>(json)?.let { GSON.fromJsonArray<TxtTocRule>(json).getOrThrow()?.let {
appDb.txtTocRuleDao.insert(*it.toTypedArray()) appDb.txtTocRuleDao.insert(*it.toTypedArray())
} }
} }
}.onSuccess { }.onSuccess {
finally("导入成功") finally("导入成功")
}.onError { }.onError {
finally("导入失败") finally("导入失败\n${it.localizedMessage}")
} }
} }

@ -126,7 +126,7 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
private fun importBookshelfByJson(json: String, groupId: Long) { private fun importBookshelfByJson(json: String, groupId: Long) {
execute { execute {
val bookSources = appDb.bookSourceDao.allEnabled val bookSources = appDb.bookSourceDao.allEnabled
GSON.fromJsonArray<Map<String, String?>>(json)?.forEach { bookInfo -> GSON.fromJsonArray<Map<String, String?>>(json).getOrThrow()?.forEach { bookInfo ->
if (!isActive) return@execute if (!isActive) return@execute
val name = bookInfo["name"] ?: "" val name = bookInfo["name"] ?: ""
val author = bookInfo["author"] ?: "" val author = bookInfo["author"] ?: ""
@ -143,6 +143,8 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
} }
} }
} }
}.onError {
it.printOnDebug()
}.onFinally { }.onFinally {
context.toastOnUi(R.string.success) context.toastOnUi(R.string.success)
} }

@ -73,6 +73,14 @@ abstract class BaseBooksAdapter<VH : RecyclerView.ViewHolder>(
} }
} }
override fun getItemCount(): Int {
return callBack.getItemCount()
}
override fun getItemViewType(position: Int): Int {
return callBack.getItemType(position)
}
interface CallBack { interface CallBack {
fun onItemClick(position: Int) fun onItemClick(position: Int)
@ -80,6 +88,6 @@ abstract class BaseBooksAdapter<VH : RecyclerView.ViewHolder>(
fun isUpdate(bookUrl: String): Boolean fun isUpdate(bookUrl: String): Boolean
fun getItemCount(): Int fun getItemCount(): Int
fun getItemType(position: Int): Int fun getItemType(position: Int): Int
fun getItem(position: Int): Any fun getItem(position: Int): Any?
} }
} }

@ -17,14 +17,6 @@ import splitties.views.onLongClick
class BooksAdapterGrid(context: Context, callBack: CallBack) : class BooksAdapterGrid(context: Context, callBack: CallBack) :
BaseBooksAdapter<RecyclerView.ViewHolder>(context, callBack) { BaseBooksAdapter<RecyclerView.ViewHolder>(context, callBack) {
override fun getItemCount(): Int {
return callBack.getItemCount()
}
override fun getItemViewType(position: Int): Int {
return callBack.getItemType(position)
}
override fun onCreateViewHolder( override fun onCreateViewHolder(
parent: ViewGroup, parent: ViewGroup,
viewType: Int viewType: Int
@ -67,7 +59,7 @@ class BooksAdapterGrid(context: Context, callBack: CallBack) :
private fun onBindBook(binding: ItemBookshelfGridBinding, position: Int, bundle: Bundle) { private fun onBindBook(binding: ItemBookshelfGridBinding, position: Int, bundle: Bundle) {
binding.run { binding.run {
val item = callBack.getItem(position) as Book val item = callBack.getItem(position) as? Book ?: return
bundle.keySet().forEach { bundle.keySet().forEach {
when (it) { when (it) {
"name" -> tvName.text = item.name "name" -> tvName.text = item.name

@ -19,14 +19,6 @@ import splitties.views.onLongClick
class BooksAdapterList(context: Context, callBack: CallBack) : class BooksAdapterList(context: Context, callBack: CallBack) :
BaseBooksAdapter<RecyclerView.ViewHolder>(context, callBack) { BaseBooksAdapter<RecyclerView.ViewHolder>(context, callBack) {
override fun getItemCount(): Int {
return callBack.getItemCount()
}
override fun getItemViewType(position: Int): Int {
return callBack.getItemType(position)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) { return when (viewType) {
1 -> GroupViewHolder( 1 -> GroupViewHolder(
@ -65,7 +57,7 @@ class BooksAdapterList(context: Context, callBack: CallBack) :
private fun onBindBook(binding: ItemBookshelfListBinding, position: Int, bundle: Bundle) { private fun onBindBook(binding: ItemBookshelfListBinding, position: Int, bundle: Bundle) {
binding.run { binding.run {
val item = callBack.getItem(position) as Book val item = callBack.getItem(position) as? Book ?: return
tvRead.text = item.durChapterTitle tvRead.text = item.durChapterTitle
tvLast.text = item.latestChapterTitle tvLast.text = item.latestChapterTitle
bundle.keySet().forEach { bundle.keySet().forEach {

@ -43,6 +43,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
BaseBooksAdapter.CallBack { BaseBooksAdapter.CallBack {
private val binding by viewBinding(FragmentBookshelf1Binding::bind) private val binding by viewBinding(FragmentBookshelf1Binding::bind)
private val rootGroupId = -100L
private val bookshelfLayout by lazy { private val bookshelfLayout by lazy {
getPrefInt(PreferKey.bookshelfLayout) getPrefInt(PreferKey.bookshelfLayout)
} }
@ -55,7 +56,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
} }
private var bookGroups: List<BookGroup> = emptyList() private var bookGroups: List<BookGroup> = emptyList()
private var booksFlowJob: Job? = null private var booksFlowJob: Job? = null
override var groupId = AppConst.bookGroupNoneId override var groupId = rootGroupId
override var books: List<Book> = emptyList() override var books: List<Book> = emptyList()
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
@ -108,7 +109,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")
private fun initBooksData() { private fun initBooksData() {
if (groupId == AppConst.bookGroupNoneId) { if (groupId == -100L) {
binding.titleBar.title = getString(R.string.bookshelf) binding.titleBar.title = getString(R.string.bookshelf)
} else { } else {
bookGroups.forEach { bookGroups.forEach {
@ -120,6 +121,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
booksFlowJob?.cancel() booksFlowJob?.cancel()
booksFlowJob = launch { booksFlowJob = launch {
when (groupId) { when (groupId) {
rootGroupId -> appDb.bookDao.flowRoot()
AppConst.bookGroupAllId -> appDb.bookDao.flowAll() AppConst.bookGroupAllId -> appDb.bookDao.flowAll()
AppConst.bookGroupLocalId -> appDb.bookDao.flowLocal() AppConst.bookGroupLocalId -> appDb.bookDao.flowLocal()
AppConst.bookGroupAudioId -> appDb.bookDao.flowAudio() AppConst.bookGroupAudioId -> appDb.bookDao.flowAudio()
@ -152,8 +154,8 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
} }
fun back(): Boolean { fun back(): Boolean {
if (groupId != AppConst.bookGroupNoneId) { if (groupId != -100L) {
groupId = AppConst.bookGroupNoneId groupId = -100L
initBooksData() initBooksData()
return true return true
} }
@ -210,7 +212,7 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
return if (groupId == AppConst.bookGroupNoneId) { return if (groupId == rootGroupId) {
bookGroups.size + books.size bookGroups.size + books.size
} else { } else {
books.size books.size
@ -218,23 +220,23 @@ class BookshelfFragment2 : BaseBookshelfFragment(R.layout.fragment_bookshelf1),
} }
override fun getItemType(position: Int): Int { override fun getItemType(position: Int): Int {
return if (groupId == AppConst.bookGroupNoneId) { if (groupId != rootGroupId) {
if (position < bookGroups.size) 1 else 0 return 0
} else { }
0 if (position < bookGroups.size) {
return 1
} }
return 0
} }
override fun getItem(position: Int): Any { override fun getItem(position: Int): Any? {
return if (groupId == AppConst.bookGroupNoneId) { if (groupId != rootGroupId) {
if (position < bookGroups.size) { return books.getOrNull(position)
bookGroups[position] }
} else { if (position < bookGroups.size) {
books[position - bookGroups.size] return bookGroups[position]
}
} else {
books[position]
} }
return books.getOrNull(position - bookGroups.size)
} }
@SuppressLint("NotifyDataSetChanged") @SuppressLint("NotifyDataSetChanged")

@ -29,7 +29,7 @@ class CodeDialog() : BaseDialogFragment(R.layout.dialog_code_view) {
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
setLayout(0.96f, ViewGroup.LayoutParams.MATCH_PARENT) setLayout(1f, ViewGroup.LayoutParams.MATCH_PARENT)
} }
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) { override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {

@ -0,0 +1,41 @@
package io.legado.app.utils
import kotlin.reflect.KClass
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor
@Suppress("UNCHECKED_CAST")
fun <T : Any> T.deepCopy(): T {
//如果不是数据类,直接返回
if (!this::class.isData) {
return this
}
//拿到构造函数
return this::class.primaryConstructor!!.let { primaryConstructor ->
//转换类型
//memberProperties 返回非扩展属性中的第一个并将构造函数赋值给其
//最终value=第一个参数类型的对象
//如果当前类(这里的当前类指的是参数对应的类型,比如说这里如果非基本类型时)是数据类
//最终返回一个新的映射map,即返回一个属性值重新组合的map,并调用callBy返回指定的对象
primaryConstructor.parameters.associate { parameter ->
//转换类型
//memberProperties 返回非扩展属性中的第一个并将构造函数赋值给其
//最终value=第一个参数类型的对象
val value = (this::class as KClass<T>).memberProperties.first {
it.name == parameter.name
}.get(this)
//如果当前类(这里的当前类指的是参数对应的类型,比如说这里如果非基本类型时)是数据类
if ((parameter.type.classifier as? KClass<*>)?.isData == true) {
parameter to value?.deepCopy()
} else {
parameter to value
}
//最终返回一个新的映射map,即返回一个属性值重新组合的map,并调用callBy返回指定的对象
}.let(primaryConstructor::callBy)
}
}

@ -4,7 +4,6 @@ import com.google.gson.*
import com.google.gson.internal.LinkedTreeMap import com.google.gson.internal.LinkedTreeMap
import com.google.gson.reflect.TypeToken import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonWriter import com.google.gson.stream.JsonWriter
import java.io.OutputStream import java.io.OutputStream
import java.io.OutputStreamWriter import java.io.OutputStreamWriter
import java.lang.reflect.ParameterizedType import java.lang.reflect.ParameterizedType
@ -26,22 +25,16 @@ val GSON: Gson by lazy {
inline fun <reified T> genericType(): Type = object : TypeToken<T>() {}.type inline fun <reified T> genericType(): Type = object : TypeToken<T>() {}.type
inline fun <reified T> Gson.fromJsonObject(json: String?): T? {//可转成任意类型 inline fun <reified T> Gson.fromJsonObject(json: String?): Result<T?> {
json ?: return null
return kotlin.runCatching { return kotlin.runCatching {
fromJson(json, genericType<T>()) as? T fromJson(json, genericType<T>()) as? T
}.onFailure { }
DebugLog.e("GSON解析出错", json, it)
}.getOrNull()
} }
inline fun <reified T> Gson.fromJsonArray(json: String?): List<T>? { inline fun <reified T> Gson.fromJsonArray(json: String?): Result<List<T>?> {
json ?: return null
return kotlin.runCatching { return kotlin.runCatching {
fromJson(json, ParameterizedTypeImpl(T::class.java)) as? List<T> fromJson(json, ParameterizedTypeImpl(T::class.java)) as? List<T>
}.onFailure { }
DebugLog.e("GSON解析出错", json, it)
}.getOrNull()
} }
fun Gson.writeToOutputStream(out: OutputStream, any: Any) { fun Gson.writeToOutputStream(out: OutputStream, any: Any) {

@ -12,10 +12,10 @@ fun Intent.putJson(key: String, any: Any?) {
inline fun <reified T> Intent.getJsonObject(key: String): T? { inline fun <reified T> Intent.getJsonObject(key: String): T? {
val value = getStringExtra(key) val value = getStringExtra(key)
return GSON.fromJsonObject<T>(value) return GSON.fromJsonObject<T>(value).getOrNull()
} }
inline fun <reified T> Intent.getJsonArray(key: String): List<T>? { inline fun <reified T> Intent.getJsonArray(key: String): List<T>? {
val value = getStringExtra(key) val value = getStringExtra(key)
return GSON.fromJsonArray(value) return GSON.fromJsonArray<T>(value).getOrNull()
} }

@ -49,7 +49,8 @@ class BookSourceDebugWebSocket(handshakeRequest: NanoHTTPD.IHTTPSession) :
close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false) close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false)
return@launch return@launch
} }
val debugBean = GSON.fromJsonObject<Map<String, String>>(message.textPayload) val debugBean =
GSON.fromJsonObject<Map<String, String>>(message.textPayload).getOrNull()
if (debugBean != null) { if (debugBean != null) {
val tag = debugBean["tag"] val tag = debugBean["tag"]
val key = debugBean["key"] val key = debugBean["key"]

@ -49,7 +49,8 @@ class RssSourceDebugWebSocket(handshakeRequest: NanoHTTPD.IHTTPSession) :
close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false) close(NanoWSD.WebSocketFrame.CloseCode.NormalClosure, "调试结束", false)
return@launch return@launch
} }
val debugBean = GSON.fromJsonObject<Map<String, String>>(message.textPayload) val debugBean =
GSON.fromJsonObject<Map<String, String>>(message.textPayload).getOrNull()
if (debugBean != null) { if (debugBean != null) {
val tag = debugBean["tag"] val tag = debugBean["tag"]
if (tag.isNullOrBlank()) { if (tag.isNullOrBlank()) {

@ -38,6 +38,7 @@ class AssetsWeb(rootPath: String) {
suffix.equals(".js", ignoreCase = true) -> "text/javascript" suffix.equals(".js", ignoreCase = true) -> "text/javascript"
suffix.equals(".css", ignoreCase = true) -> "text/css" suffix.equals(".css", ignoreCase = true) -> "text/css"
suffix.equals(".ico", ignoreCase = true) -> "image/x-icon" suffix.equals(".ico", ignoreCase = true) -> "image/x-icon"
suffix.equals(".jpg", ignoreCase = true) -> "image/jpg"
else -> "text/html" else -> "text/html"
} }
} }

Loading…
Cancel
Save