pull/241/head
383bjz82bu 5 years ago
commit 695696438b
  1. 8
      .gitignore
  2. 19
      app/build.gradle
  3. 26
      app/proguard-rules.pro
  4. 1
      app/src/main/assets/18PlusList.txt
  5. 20
      app/src/main/assets/readConfig.json
  6. 36
      app/src/main/assets/updateLog.md
  7. 2
      app/src/main/java/io/legado/app/constant/PreferKey.kt
  8. 2
      app/src/main/java/io/legado/app/data/entities/BaseBook.kt
  9. 23
      app/src/main/java/io/legado/app/data/entities/Book.kt
  10. 23
      app/src/main/java/io/legado/app/data/entities/BookSource.kt
  11. 16
      app/src/main/java/io/legado/app/data/entities/SearchBook.kt
  12. 4
      app/src/main/java/io/legado/app/help/AdapterDataObserverHeader.kt
  13. 15
      app/src/main/java/io/legado/app/help/ReadBookConfig.kt
  14. 4
      app/src/main/java/io/legado/app/help/http/CookieStore.kt
  15. 1
      app/src/main/java/io/legado/app/help/storage/Restore.kt
  16. 45
      app/src/main/java/io/legado/app/service/AudioPlayService.kt
  17. 1
      app/src/main/java/io/legado/app/service/BaseReadAloudService.kt
  18. 19
      app/src/main/java/io/legado/app/service/CheckSourceService.kt
  19. 6
      app/src/main/java/io/legado/app/service/HttpReadAloudService.kt
  20. 27
      app/src/main/java/io/legado/app/service/TTSReadAloudService.kt
  21. 64
      app/src/main/java/io/legado/app/service/help/CheckSource.kt
  22. 12
      app/src/main/java/io/legado/app/service/help/ReadBook.kt
  23. 1
      app/src/main/java/io/legado/app/ui/about/AboutFragment.kt
  24. 1
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceAdapter.kt
  25. 18
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt
  26. 10
      app/src/main/java/io/legado/app/ui/book/changesource/DiffCallBack.kt
  27. 3
      app/src/main/java/io/legado/app/ui/book/explore/ExploreShowActivity.kt
  28. 18
      app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
  29. 30
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  30. 8
      app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
  31. 100
      app/src/main/java/io/legado/app/ui/book/read/config/AutoReadDialog.kt
  32. 3
      app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt
  33. 5
      app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
  34. 55
      app/src/main/java/io/legado/app/ui/book/read/config/TextFontWeightConverter.kt
  35. 10
      app/src/main/java/io/legado/app/ui/book/read/config/TipConfigDialog.kt
  36. 38
      app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt
  37. 6
      app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
  38. 6
      app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
  39. 62
      app/src/main/java/io/legado/app/ui/book/search/DiffCallBack.kt
  40. 23
      app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt
  41. 24
      app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt
  42. 69
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  43. 5
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
  44. 105
      app/src/main/java/io/legado/app/ui/config/FileAssociationViewModel.kt
  45. 22
      app/src/main/java/io/legado/app/ui/main/MainActivity.kt
  46. 30
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt
  47. 30
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterList.kt
  48. 57
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksDiffCallBack.kt
  49. 5
      app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt
  50. 10
      app/src/main/java/io/legado/app/ui/widget/prefs/PreferenceCategory.kt
  51. 2
      app/src/main/java/io/legado/app/ui/widget/prefs/SwitchPreference.kt
  52. 43
      app/src/main/java/io/legado/app/utils/GsonExtensions.kt
  53. 200
      app/src/main/res/layout/dialog_auto_read.xml
  54. 2
      app/src/main/res/layout/dialog_read_aloud.xml
  55. 5
      app/src/main/res/layout/dialog_read_book_style.xml
  56. 1
      app/src/main/res/layout/view_preference_category.xml
  57. 40
      app/src/main/res/menu/book_source.xml
  58. 5
      app/src/main/res/values-zh-rHK/arrays.xml
  59. 10
      app/src/main/res/values-zh-rHK/strings.xml
  60. 114
      app/src/main/res/values-zh-rTW/arrays.xml
  61. 705
      app/src/main/res/values-zh-rTW/strings.xml
  62. 6
      app/src/main/res/values/arrays.xml
  63. 2
      app/src/main/res/values/pref_key_value.xml
  64. 13
      app/src/main/res/values/strings.xml
  65. 6
      app/src/main/res/xml/about.xml
  66. 66
      app/src/main/res/xml/pref_config_backup.xml
  67. 6
      app/src/main/res/xml/pref_config_read.xml
  68. 2
      build.gradle
  69. 4
      gradle/wrapper/gradle-wrapper.properties

8
.gitignore vendored

@ -13,3 +13,11 @@ node_modules/
/app/google
/app/gradle.properties
package-lock.json
*__gen.go
*__ignore.go
*.swp
*.swo
~i_*
ig_*
zzzig_*

@ -56,7 +56,7 @@ android {
signingConfig signingConfigs.myConfig
}
applicationIdSuffix '.release'
minifyEnabled true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
@ -66,7 +66,6 @@ android {
applicationIdSuffix '.debug'
versionNameSuffix 'debug'
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
android.applicationVariants.all { variant ->
variant.outputs.all {
@ -117,11 +116,11 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
//fireBase
implementation 'com.google.firebase:firebase-core:17.4.1'
implementation 'com.google.firebase:firebase-core:17.4.3'
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
//androidX
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.media:media:1.1.0'
implementation 'androidx.preference:preference:1.1.1'
@ -154,7 +153,7 @@ dependencies {
implementation 'com.jeremyliao:live-event-bus-x:1.5.7'
//
def coroutines_version = '1.3.3'
def coroutines_version = '1.3.7'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
@ -199,8 +198,8 @@ dependencies {
apply plugin: 'com.google.gms.google-services'
afterEvaluate {
for (Task task : project.tasks.matching { it.name.startsWith('crashlyticsUploadDeobs') }) {
task.enabled = false
}
}
//afterEvaluate {
// for (Task task : project.tasks.matching { it.name.startsWith('crashlyticsUploadDeobs') }) {
// task.enabled = false
// }
//}

@ -58,14 +58,13 @@
# 保留我们使用的四大组件,自定义的Application等等这些类不被混淆
# 因为这些子类都有可能被外部调用
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
# 保留androidx下的所有类及其内部类
@ -128,16 +127,11 @@
}
# webView处理,项目中没有使用到webView忽略即可
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
-keepclassmembers class * extends android.webkit.webViewClient {
-keepclassmembers class * extends android.webkit.WebViewClient {
public void *(android.webkit.WebView, java.lang.String);
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.webView, jav.lang.String);
}
# 移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用
# 记得proguard-android.txt中一定不要加-dontoptimize才起作用
@ -170,20 +164,20 @@
-dontwarn org.conscrypt.**
-dontwarn com.jeremyliao.liveeventbus.**
-keep class com.google.gson.** { *; }
-keep class com.ke.gson.** { *; }
-keep class com.jeremyliao.liveeventbus.** { *; }
-keep class okhttp3.**{*;}
-keep class okio.**{*;}
-keep class com.hwangjr.rxbus.**{*;}
-keep class org.conscrypt.**{*;}
-keep class com.kunfei.bookshelf.widget.**{*;}
-keep class com.kunfei.bookshelf.bean.**{*;}
-keep class android.support.**{*;}
-keep class me.grantland.widget.**{*;}
-keep class de.hdodenhof.circleimageview.**{*;}
-keep class tyrant.explosionfield.**{*;}
-keep class tyrantgit.explosionfield.**{*;}
-keep class freemarker.**{*;}
-keep class com.gyf.barlibrary.* {*;}
-keep class com.gyf.barlibrary.** {*;}
##JSOUP
-keep class org.jsoup.**{*;}
-keep class **.xpath.**{*;}
@ -197,7 +191,7 @@
-dontwarn com.jayway.**
-keep class com.fasterxml.**{*;}
-keep class javax.swing..**{*;}
-keep class javax.swing.**{*;}
-dontwarn javax.swing.**
-keep class java.awt.**{*;}
-dontwarn java.awt.**
@ -215,10 +209,10 @@
-dontwarn nl.siegmann.epublib.**
-dontwarn org.xmlpull.**
-keep class nl.siegmann.epublib.**{*;}
-keep class javax.xml.**{*;}
-keep class org.xmlpull.**{*;}
-keep class javax.xml**{*;}
-keep class org.xmlpull**{*;}
-keep class org.simpleframework.xml.**{*;}
-keep class org.simpleframework.xml**{*;}
-dontwarn org.simpleframework.xml.**
-keepclassmembers class * {

@ -1,2 +1,3 @@
OGN5dS5jb20=
c2cwMC54eXo=
aXRyYWZmaWNuZXQuY29t

@ -7,9 +7,7 @@
"bgType": 0,
"bgTypeNight": 0,
"darkStatusIcon": true,
"textSize": 24,
"letterSpacing": 0,
"lineSpacingExtra": 10
"darkStatusIconNight": false
},
{
"bgStr": "#DDC090",
@ -19,9 +17,7 @@
"bgType": 0,
"bgTypeNight": 0,
"darkStatusIcon": true,
"textSize": 24,
"letterSpacing": 0,
"lineSpacingExtra": 10
"darkStatusIconNight": false
},
{
"bgStr": "#C2D8AA",
@ -31,9 +27,7 @@
"bgType": 0,
"bgTypeNight": 0,
"darkStatusIcon": false,
"textSize": 24,
"letterSpacing": 0,
"lineSpacingExtra": 10
"darkStatusIconNight": false
},
{
"bgStr": "#DBB8E2",
@ -43,9 +37,7 @@
"bgType": 0,
"bgTypeNight": 0,
"darkStatusIcon": false,
"textSize": 24,
"letterSpacing": 0,
"lineSpacingExtra": 10
"darkStatusIconNight": false
},
{
"bgStr": "#ABCEE0",
@ -55,8 +47,6 @@
"bgType": 0,
"bgTypeNight": 0,
"darkStatusIcon": false,
"textSize": 24,
"letterSpacing": 0,
"lineSpacingExtra": 10
"darkStatusIconNight": false
}
]

@ -3,6 +3,42 @@
* 请关注公众号[开源阅读]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。
* 新公众号[开源阅读]()已启用,[开源阅读软件]()备用
**2020/06/13**
* 优化搜索
**2020/06/12**
* 修复分组变化的bug
**2020/06/10**
* 正文字体的粗细选择增加可以选择细体(Android O生效) by hingbong
* 修复bug
**2020/06/09**
* 修复从发现界面打开已在书架的书时,显示不对的问题
**2020/06/07**
* 优化书源检测,自定义搜索关键词
* 失效书源如果校验为有效会去掉失效标志
**2020/06/06**
* 修复一些bug,包括从阅读界面退出后还是显示红色更新的bug
**2020/06/03**
* zh-TW translation by david082321
* 修复音频播放时播放速度调节会再下一章失效的bug
**2020/05/31**
* 更新到android studio 4.0
* 书源排序添加按url
* 去除朗读通知的进度条
* 修复恢复问题,暂时去除混淆
**2020/05/24**
* 添加自动翻页速度调节
**2020/05/23**
* 添加文字两端对齐配置
**2020/05/20**
* Rss列表增加一种显示样式

@ -44,4 +44,6 @@ object PreferKey {
const val systemTypefaces = "system_typefaces"
const val readBodyToLh = "readBodyToLh"
const val einkMode = "isEInkMode"
const val textFullJustify = "textFullJustify"
const val autoReadSpeed = "autoReadSpeed"
}

@ -4,7 +4,7 @@ import io.legado.app.utils.splitNotBlank
interface BaseBook {
var bookUrl: String
var variableMap: HashMap<String, String>?
val variableMap: HashMap<String, String>
var kind: String?
var wordCount: String?

@ -71,15 +71,17 @@ data class Book(
return bookUrl.hashCode()
}
@Ignore
@delegate:Transient
@delegate:Ignore
@IgnoredOnParcel
override var variableMap: HashMap<String, String>? = null
get() {
if (field == null) {
field = GSON.fromJsonObject<HashMap<String, String>>(variable) ?: HashMap()
}
return field
}
override val variableMap by lazy {
GSON.fromJsonObject<HashMap<String, String>>(variable) ?: HashMap()
}
override fun putVariable(key: String, value: String) {
variableMap[key] = value
variable = GSON.toJson(variableMap)
}
@Ignore
@IgnoredOnParcel
@ -97,11 +99,6 @@ data class Book(
fun getDisplayIntro() = if (customIntro.isNullOrEmpty()) intro else customIntro
override fun putVariable(key: String, value: String) {
variableMap?.put(key, value)
variable = GSON.toJson(variableMap)
}
fun fileCharset(): Charset {
return charset(charset ?: "UTF-8")
}

@ -1,6 +1,7 @@
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
@ -8,10 +9,7 @@ import io.legado.app.constant.AppConst.userAgent
import io.legado.app.constant.BookType
import io.legado.app.data.entities.rule.*
import io.legado.app.help.JsExtensions
import io.legado.app.utils.ACache
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.getPrefString
import io.legado.app.utils.*
import kotlinx.android.parcel.Parcelize
import java.util.*
import javax.script.SimpleBindings
@ -105,6 +103,13 @@ data class BookSource(
}
}
fun removeGroup(group: String) {
bookSourceGroup?.splitNotBlank(",")?.toHashSet()?.let {
it.remove(group)
bookSourceGroup = TextUtils.join(",", it)
}
}
fun getExploreKinds(): ArrayList<ExploreKind>? {
val exploreKinds = arrayListOf<ExploreKind>()
exploreUrl?.let {
@ -179,7 +184,7 @@ data class BookSource(
class Converters {
@TypeConverter
fun exploreRuleToString(exploreRule: ExploreRule?): String {
fun exploreRuleToString(exploreRule: ExploreRule?): String? {
return GSON.toJson(exploreRule)
}
@ -189,7 +194,7 @@ data class BookSource(
}
@TypeConverter
fun searchRuleToString(searchRule: SearchRule): String {
fun searchRuleToString(searchRule: SearchRule?): String? {
return GSON.toJson(searchRule)
}
@ -199,7 +204,7 @@ data class BookSource(
}
@TypeConverter
fun bookInfoRuleToString(bookInfoRule: BookInfoRule): String {
fun bookInfoRuleToString(bookInfoRule: BookInfoRule?): String? {
return GSON.toJson(bookInfoRule)
}
@ -209,7 +214,7 @@ data class BookSource(
}
@TypeConverter
fun tocRuleToString(tocRule: TocRule): String {
fun tocRuleToString(tocRule: TocRule?): String? {
return GSON.toJson(tocRule)
}
@ -219,7 +224,7 @@ data class BookSource(
}
@TypeConverter
fun contentRuleToString(contentRule: ContentRule): String {
fun contentRuleToString(contentRule: ContentRule?): String? {
return GSON.toJson(contentRule)
}

@ -63,21 +63,19 @@ data class SearchBook(
return other.originOrder - this.originOrder
}
@Ignore
@delegate:Transient
@delegate:Ignore
@IgnoredOnParcel
override var variableMap: HashMap<String, String>? = null
get() {
if (field == null) {
field = GSON.fromJsonObject<HashMap<String, String>>(variable) ?: HashMap()
}
return field
}
override val variableMap by lazy {
GSON.fromJsonObject<HashMap<String, String>>(variable) ?: HashMap()
}
override fun putVariable(key: String, value: String) {
variableMap?.put(key, value)
variableMap[key] = value
variable = GSON.toJson(variableMap)
}
@delegate:Transient
@delegate:Ignore
@IgnoredOnParcel
val origins: LinkedHashSet<String> by lazy { linkedSetOf(origin) }

@ -3,8 +3,8 @@ package io.legado.app.help
import androidx.recyclerview.widget.RecyclerView
internal class AdapterDataObserverHeader(
var adapterDataObserver: RecyclerView.AdapterDataObserver,
var headerCount: Int
private var adapterDataObserver: RecyclerView.AdapterDataObserver,
private var headerCount: Int
) : RecyclerView.AdapterDataObserver() {
override fun onChanged() {
adapterDataObserver.onChanged()

@ -59,6 +59,7 @@ object ReadBookConfig {
val json = configFile.readText()
return GSON.fromJsonArray(json)
} catch (e: Exception) {
e.printStackTrace()
}
}
return null
@ -103,6 +104,11 @@ object ReadBookConfig {
}
//配置写入读取
var autoReadSpeed = App.INSTANCE.getPrefInt(PreferKey.autoReadSpeed, 46)
set(value) {
field = value
App.INSTANCE.putPrefInt(PreferKey.autoReadSpeed, value)
}
var styleSelect = App.INSTANCE.getPrefInt(PreferKey.readStyleSelect)
set(value) {
field = value
@ -127,6 +133,7 @@ object ReadBookConfig {
}
var isScroll = pageAnim == 3
val clickTurnPage get() = App.INSTANCE.getPrefBoolean(PreferKey.clickTurnPage, true)
val textFullJustify get() = App.INSTANCE.getPrefBoolean(PreferKey.textFullJustify, true)
var bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2)
set(value) {
field = value
@ -141,7 +148,7 @@ object ReadBookConfig {
private val config get() = if (shareLayout) getConfig(5) else durConfig
var textBold: Boolean
var textBold: Int
get() = config.textBold
set(value) {
config.textBold = value
@ -288,11 +295,11 @@ object ReadBookConfig {
private var darkStatusIconNight: Boolean = false,//晚上是否暗色状态栏
private var textColor: String = "#3E3D3B",//白天文字颜色
private var textColorNight: String = "#ADADAD",//夜间文字颜色
var textBold: Boolean = false,//是否粗体字
var textBold: Int = 0,//是否粗体字 0:正常, 1:粗体, 2:细体
var textSize: Int = 20,//文字大小
var letterSpacing: Float = 0.5f,//字间距
var letterSpacing: Float = 0.1f,//字间距
var lineSpacingExtra: Int = 12,//行间距
var paragraphSpacing: Int = 12,//段距
var paragraphSpacing: Int = 4,//段距
var titleMode: Int = 0,//标题居中
var titleSize: Int = 0,
var titleTopSpacing: Int = 0,

@ -39,10 +39,6 @@ object CookieStore {
App.db.cookieDao().delete(url)
}
fun clearCookies() {
}
private fun cookieToMap(cookie: String): MutableMap<String, String> {
val cookieMap = mutableMapOf<String, String>()
if (cookie.isBlank()) {

@ -126,6 +126,7 @@ object Restore {
hideStatusBar = App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar)
hideNavigationBar = App.INSTANCE.getPrefBoolean(PreferKey.hideNavigationBar)
bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2)
autoReadSpeed = App.INSTANCE.getPrefInt(PreferKey.autoReadSpeed, 46)
}
ChapterProvider.upStyle()
ReadBook.loadContent(resetPageOffset = false)

@ -63,6 +63,7 @@ class AudioPlayService : BaseService(),
private val dsRunnable: Runnable = Runnable { doDs() }
private var mpRunnable: Runnable = Runnable { upPlayProgress() }
private var bookChapter: BookChapter? = null
private var playSpeed: Float = 1f
override fun onCreate() {
super.onCreate()
@ -95,7 +96,12 @@ class AudioPlayService : BaseService(),
IntentAction.adjustSpeed -> upSpeed(intent.getFloatExtra("adjust", 1f))
IntentAction.addTimer -> addTimer()
IntentAction.setTimer -> setTimer(intent.getIntExtra("minute", 0))
IntentAction.adjustProgress -> adjustProgress(intent.getIntExtra("position", position))
IntentAction.adjustProgress -> adjustProgress(
intent.getIntExtra(
"position",
position
)
)
else -> stopSelf()
}
}
@ -140,14 +146,18 @@ class AudioPlayService : BaseService(),
if (url.contains(".m3u8", false)) {
stopSelf()
} else {
AudioPlayService.pause = pause
handler.removeCallbacks(mpRunnable)
position = mediaPlayer.currentPosition
mediaPlayer.pause()
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PAUSED)
AudioPlay.status = Status.PAUSE
postEvent(EventBus.AUDIO_STATE, Status.PAUSE)
upNotification()
try {
AudioPlayService.pause = pause
handler.removeCallbacks(mpRunnable)
position = mediaPlayer.currentPosition
mediaPlayer.pause()
upMediaSessionPlaybackState(PlaybackStateCompat.STATE_PAUSED)
AudioPlay.status = Status.PAUSE
postEvent(EventBus.AUDIO_STATE, Status.PAUSE)
upNotification()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
@ -174,12 +184,12 @@ class AudioPlayService : BaseService(),
private fun upSpeed(adjust: Float) {
kotlin.runCatching {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
with(mediaPlayer) {
if (isPlaying) {
playbackParams = playbackParams.apply { speed += adjust }
}
postEvent(EventBus.AUDIO_SPEED, playbackParams.speed)
playSpeed += adjust
if (mediaPlayer.isPlaying) {
mediaPlayer.playbackParams =
mediaPlayer.playbackParams.apply { speed = playSpeed }
}
postEvent(EventBus.AUDIO_SPEED, playSpeed)
}
}
}
@ -189,7 +199,11 @@ class AudioPlayService : BaseService(),
*/
override fun onPrepared(mp: MediaPlayer?) {
if (pause) return
mediaPlayer.start()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mediaPlayer.playbackParams = mediaPlayer.playbackParams.apply { speed = playSpeed }
} else {
mediaPlayer.start()
}
mediaPlayer.seekTo(position)
postEvent(EventBus.AUDIO_SIZE, mediaPlayer.duration)
bookChapter?.let {
@ -506,7 +520,6 @@ class AudioPlayService : BaseService(),
)
builder.setStyle(
androidx.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mediaSessionCompat?.sessionToken)
.setShowActionsInCompactView(0, 1, 2)
)
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

@ -323,7 +323,6 @@ abstract class BaseReadAloudService : BaseService(),
)
builder.setStyle(
androidx.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mediaSessionCompat.sessionToken)
.setShowActionsInCompactView(0, 1, 2)
)
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

@ -7,13 +7,11 @@ import io.legado.app.R
import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst
import io.legado.app.constant.IntentAction
import io.legado.app.data.entities.BookSource
import io.legado.app.help.AppConfig
import io.legado.app.help.IntentHelp
import io.legado.app.help.coroutine.CompositeCoroutine
import io.legado.app.model.WebBook
import io.legado.app.service.help.CheckSource
import io.legado.app.ui.book.source.manage.BookSourceActivity
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.asCoroutineDispatcher
import org.jetbrains.anko.toast
import java.util.concurrent.Executors
@ -80,24 +78,15 @@ class CheckSourceService : BaseService() {
if (source.searchUrl.isNullOrEmpty()) {
onNext(sourceUrl)
} else {
check(source)
CheckSource(source).check(this, searchPool) {
onNext(it)
}
}
} ?: onNext(sourceUrl)
}
}
}
private fun check(source: BookSource) {
val webBook = WebBook(source)
tasks.add(webBook.searchBook("我的", scope = this, context = searchPool)
.onError(IO) {
source.addGroup("失效")
App.db.bookSourceDao().update(source)
}.onFinally(IO) {
onNext(source.bookSourceUrl)
})
}
private fun onNext(sourceUrl: String) {
synchronized(this) {
check()

@ -76,7 +76,9 @@ class HttpReadAloudService : BaseReadAloudService(),
val file = getSpeakFile(index)
file.writeBytes(bytes)
if (index == nowSpeak) {
playAudio(FileInputStream(file).fd)
@Suppress("BlockingMethodInNonBlockingContext")
val fis = FileInputStream(file)
playAudio(fis.fd)
}
}
} else {
@ -204,7 +206,7 @@ class HttpReadAloudService : BaseReadAloudService(),
} else {
nextChapter()
}
}, 1000)
}, 100)
return true
}

@ -42,9 +42,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
private fun initTts() {
ttsInitFinish = false
textToSpeech = TextToSpeech(this, this).apply {
setOnUtteranceProgressListener(ttsUtteranceListener)
}
textToSpeech = TextToSpeech(this, this)
}
override fun onDestroy() {
@ -55,6 +53,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
override fun onInit(status: Int) {
if (status == TextToSpeech.SUCCESS) {
textToSpeech?.let {
it.setOnUtteranceProgressListener(ttsUtteranceListener)
it.language = Locale.CHINA
ttsInitFinish = true
play()
@ -69,16 +68,20 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
@Synchronized
override fun play() {
if (contentList.isNotEmpty() && ttsInitFinish && requestFocus()) {
MediaHelp.playSilentSound(this)
super.play()
textToSpeech?.stop()
for (i in nowSpeak until contentList.size) {
textToSpeech?.speak(
contentList[i],
TextToSpeech.QUEUE_ADD,
null,
AppConst.APP_TAG + i
)
execute {
MediaHelp.playSilentSound(this@TTSReadAloudService)
textToSpeech?.let {
it.speak("", TextToSpeech.QUEUE_FLUSH, null, null)
for (i in nowSpeak until contentList.size) {
it.speak(
contentList[i],
TextToSpeech.QUEUE_ADD,
null,
AppConst.APP_TAG + i
)
}
}
}
}
}

@ -2,34 +2,62 @@ 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.BookSource
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.WebBook
import io.legado.app.service.CheckSourceService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import org.jetbrains.anko.toast
import kotlin.coroutines.CoroutineContext
object CheckSource {
class CheckSource(val source: BookSource) {
fun start(context: Context, sources: List<BookSource>) {
if (sources.isEmpty()) {
context.toast(R.string.non_select)
return
}
val selectedIds: ArrayList<String> = arrayListOf()
sources.map {
selectedIds.add(it.bookSourceUrl)
companion object {
var keyword = "我的"
fun start(context: Context, sources: List<BookSource>) {
if (sources.isEmpty()) {
context.toast(R.string.non_select)
return
}
val selectedIds: ArrayList<String> = arrayListOf()
sources.map {
selectedIds.add(it.bookSourceUrl)
}
Intent(context, CheckSourceService::class.java).let {
it.action = IntentAction.start
it.putExtra("selectIds", selectedIds)
context.startService(it)
}
}
Intent(context, CheckSourceService::class.java).let {
it.action = IntentAction.start
it.putExtra("selectIds", selectedIds)
context.startService(it)
fun stop(context: Context) {
Intent(context, CheckSourceService::class.java).let {
it.action = IntentAction.stop
context.startService(it)
}
}
}
fun stop(context: Context) {
Intent(context, CheckSourceService::class.java).let {
it.action = IntentAction.stop
context.startService(it)
}
fun check(
scope: CoroutineScope,
context: CoroutineContext,
onNext: (sourceUrl: String) -> Unit
): Coroutine<*> {
val webBook = WebBook(source)
return webBook.searchBook(keyword, scope = scope, context = context)
.onError(Dispatchers.IO) {
source.addGroup("失效")
App.db.bookSourceDao().update(source)
}.onSuccess(Dispatchers.IO) {
source.removeGroup("失效")
App.db.bookSourceDao().update(source)
}.onFinally(Dispatchers.IO) {
onNext(source.bookSourceUrl)
}
}
}

@ -1,11 +1,13 @@
package io.legado.app.service.help
import androidx.lifecycle.MutableLiveData
import com.hankcs.hanlp.HanLP
import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.BookType
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter
import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp
import io.legado.app.help.IntentDataHelp
import io.legado.app.help.coroutine.Coroutine
@ -38,7 +40,6 @@ object ReadBook {
fun resetData(book: Book) {
this.book = book
titleDate.postValue(book.name)
durChapterIndex = book.durChapterIndex
durPageIndex = book.durChapterPos
isLocalBook = book.origin == BookType.local
@ -46,11 +47,11 @@ object ReadBook {
prevTextChapter = null
curTextChapter = null
nextTextChapter = null
titleDate.postValue(book.name)
upWebBook(book)
}
fun upWebBook(book: Book?) {
book ?: return
fun upWebBook(book: Book) {
webBook = if (book.origin == BookType.local) {
null
} else {
@ -292,6 +293,11 @@ object ReadBook {
) {
Coroutine.async {
if (chapter.index in durChapterIndex - 1..durChapterIndex + 1) {
chapter.title = when (AppConfig.chineseConverterType) {
1 -> HanLP.convertToSimplifiedChinese(chapter.title)
2 -> HanLP.convertToTraditionalChinese(chapter.title)
else -> chapter.title
}
val contents = BookHelp.disposeContent(
chapter.title,
book!!.name,

@ -47,6 +47,7 @@ class AboutFragment : PreferenceFragmentCompat() {
"update_log" -> showUpdateLog()
"check_update" -> openUrl(R.string.latest_release_url)
"mail" -> sendMail()
"sourceRuleSummary" -> openUrl(R.string.source_rule_url)
"git" -> openUrl(R.string.this_github_url)
"home_page" -> openUrl(R.string.home_page_url)
"license" -> requireContext().openUrl(licenseUrl)

@ -60,7 +60,6 @@ class ChangeSourceAdapter(context: Context, val callBack: CallBack) :
when (it.itemId) {
R.id.menu_disable_book_source -> {
callBack.disableSource(searchBook)
removeItem(searchBook)
}
}
true

@ -2,6 +2,7 @@ package io.legado.app.ui.book.changesource
import android.app.Application
import android.os.Bundle
import android.os.Handler
import androidx.lifecycle.MutableLiveData
import io.legado.app.App
import io.legado.app.R
@ -22,6 +23,7 @@ import java.util.concurrent.Executors
class ChangeSourceViewModel(application: Application) : BaseViewModel(application) {
private var searchPool =
Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher()
val handler = Handler()
val searchStateData = MutableLiveData<Boolean>()
val searchBooksLiveData = MutableLiveData<List<SearchBook>>()
var name: String = ""
@ -29,6 +31,9 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
private var task: Coroutine<*>? = null
private var screenKey: String = ""
private val searchBooks = hashSetOf<SearchBook>()
private var postTime = 0L
private val sendRunnable = Runnable { upAdapter() }
fun initData(arguments: Bundle?) {
arguments?.let { bundle ->
@ -55,9 +60,17 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
}
}
@Synchronized
private fun upAdapter() {
val books = searchBooks.toList()
searchBooksLiveData.postValue(books.sortedBy { it.originOrder })
if (System.currentTimeMillis() >= postTime + 500) {
handler.removeCallbacks(sendRunnable)
postTime = System.currentTimeMillis()
val books = searchBooks.toList()
searchBooksLiveData.postValue(books.sortedBy { it.originOrder })
} else {
handler.removeCallbacks(sendRunnable)
handler.postDelayed(sendRunnable, 500 - System.currentTimeMillis() + postTime)
}
}
private fun searchFinish(searchBook: SearchBook) {
@ -172,6 +185,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
App.db.bookSourceDao().update(source)
}
searchBooks.remove(searchBook)
upAdapter()
}
}

@ -24,13 +24,11 @@ class DiffCallBack(private val oldItems: List<SearchBook>, private val newItems:
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
if (oldItem.originName != newItem.originName) {
return false
}
if (oldItem.latestChapterTitle != newItem.latestChapterTitle) {
return false
return when {
oldItem.originName != newItem.originName -> false
oldItem.latestChapterTitle != newItem.latestChapterTitle -> false
else -> true
}
return true
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {

@ -74,7 +74,8 @@ class ExploreShowActivity : VMBaseActivity<ExploreShowViewModel>(R.layout.activi
override fun showBookInfo(book: Book) {
startActivity<BookInfoActivity>(
Pair("bookUrl", book.bookUrl)
Pair("name", book.name),
Pair("author", book.author)
)
}
}

@ -12,6 +12,7 @@ import io.legado.app.help.BookHelp
import io.legado.app.model.WebBook
import io.legado.app.model.localBook.AnalyzeTxtFile
import io.legado.app.model.localBook.LocalBook
import io.legado.app.service.help.ReadBook
import kotlinx.coroutines.Dispatchers.IO
class BookInfoViewModel(application: Application) : BaseViewModel(application) {
@ -22,13 +23,13 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
fun initData(intent: Intent) {
execute {
intent.getStringExtra("bookUrl")?.let {
App.db.bookDao().getBook(it)?.let { book ->
inBookshelf = true
setBook(book)
} ?: App.db.searchBookDao().getSearchBook(it)?.toBook()?.let { book ->
setBook(book)
}
val name = intent.getStringExtra("name") ?: ""
val author = intent.getStringExtra("author") ?: ""
App.db.bookDao().getBook(name, author)?.let { book ->
inBookshelf = true
setBook(book)
} ?: App.db.searchBookDao().getFirstByNameAuthor(name, author)?.toBook()?.let { book ->
setBook(book)
}
}
}
@ -178,6 +179,9 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
book.durChapterTitle = it.durChapterTitle
}
App.db.bookDao().insert(book)
if (ReadBook.book?.name == book.name && ReadBook.book?.author == book.author) {
ReadBook.book = book
}
}
}.onSuccess {
success?.invoke()

@ -69,6 +69,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
ReadAloudDialog.CallBack,
ChangeSourceDialog.CallBack,
ReadBook.CallBack,
AutoReadDialog.CallBack,
TocRegexDialog.CallBack,
ReplaceEditDialog.CallBack,
ColorPickerDialogListener {
@ -131,6 +132,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
override fun onPause() {
super.onPause()
ReadBook.saveRead()
timeBatteryReceiver?.let {
unregisterReceiver(it)
timeBatteryReceiver = null
@ -240,7 +242,10 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
onReplaceRuleSave()
}
R.id.menu_book_info -> ReadBook.book?.let {
startActivity<BookInfoActivity>(Pair("bookUrl", it.bookUrl))
startActivity<BookInfoActivity>(
Pair("name", it.name),
Pair("author", it.author)
)
}
R.id.menu_toc_regex -> TocRegexDialog.show(
supportFragmentManager,
@ -518,6 +523,9 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
seek_read_page.progress = ReadBook.durPageIndex
tv_pre.isEnabled = ReadBook.durChapterIndex != 0
tv_next.isEnabled = ReadBook.durChapterIndex != ReadBook.chapterSize - 1
} ?: let {
tv_chapter_name.gone()
tv_chapter_url.gone()
}
}
}
@ -547,10 +555,16 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
}
override fun clickCenter() {
if (BaseReadAloudService.isRun) {
showReadAloudDialog()
} else {
read_menu.runMenuIn()
when {
BaseReadAloudService.isRun -> {
showReadAloudDialog()
}
isAutoPage -> {
AutoReadDialog().show(supportFragmentManager, "autoRead")
}
else -> {
read_menu.runMenuIn()
}
}
}
@ -569,12 +583,14 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
autoPageStop()
} else {
isAutoPage = true
page_view.upContent()
page_view.upContent(1)
autoPagePlus()
}
read_menu.setAutoPage(isAutoPage)
}
private fun autoPageStop() {
override fun autoPageStop() {
isAutoPage = false
mHandler.removeCallbacks(autoPageRunnable)
page_view.upContent()
@ -583,7 +599,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
private fun autoPagePlus() {
mHandler.removeCallbacks(autoPageRunnable)
autoPageProgress++
if (autoPageProgress >= 460) {
if (autoPageProgress >= ReadBookConfig.autoReadSpeed * 10) {
autoPageProgress = 0
page_view.fillPage(PageDelegate.Direction.NEXT)
} else {

@ -35,6 +35,10 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
} ?: App.db.bookDao().lastReadBook?.let {
initBook(it)
}
}.onFinally {
if (ReadBook.inBookshelf) {
ReadBook.saveRead()
}
}
}
@ -59,11 +63,9 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
}
ReadBook.loadContent(resetPageOffset = true)
}
if (ReadBook.inBookshelf) {
ReadBook.saveRead()
}
} else {
isInitFinish = true
ReadBook.book!!.group = book.group
ReadBook.titleDate.postValue(book.name)
ReadBook.upWebBook(book)
if (!book.isLocalBook() && ReadBook.webBook == null) {

@ -0,0 +1,100 @@
package io.legado.app.ui.book.read.config
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.bottomBackground
import io.legado.app.service.BaseReadAloudService
import io.legado.app.service.help.ReadAloud
import io.legado.app.ui.book.read.Help
import kotlinx.android.synthetic.main.dialog_auto_read.*
import org.jetbrains.anko.sdk27.listeners.onClick
class AutoReadDialog : BaseDialogFragment() {
var callBack: CallBack? = null
override fun onStart() {
super.onStart()
val dm = DisplayMetrics()
activity?.let {
Help.upSystemUiVisibility(it)
it.windowManager?.defaultDisplay?.getMetrics(dm)
}
dialog?.window?.let {
it.setBackgroundDrawableResource(R.color.background)
it.decorView.setPadding(0, 0, 0, 0)
val attr = it.attributes
attr.dimAmount = 0.0f
attr.gravity = Gravity.BOTTOM
it.attributes = attr
it.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
callBack = activity as? CallBack
return inflater.inflate(R.layout.dialog_auto_read, container)
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
root_view.setBackgroundColor(requireContext().bottomBackground)
initOnChange()
initData()
initEvent()
}
private fun initData() {
seek_auto_read.progress = ReadBookConfig.autoReadSpeed
}
private fun initOnChange() {
seek_auto_read.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
}
override fun onStartTrackingTouch(seekBar: SeekBar?) = Unit
override fun onStopTrackingTouch(seekBar: SeekBar?) {
ReadBookConfig.autoReadSpeed = seek_auto_read.progress
upTtsSpeechRate()
}
})
}
private fun initEvent() {
ll_main_menu.onClick { callBack?.showMenuBar(); dismiss() }
ll_setting.onClick {
ReadAloudConfigDialog().show(childFragmentManager, "readAloudConfigDialog")
}
ll_catalog.onClick { callBack?.openChapterList() }
ll_auto_page_stop.onClick {
callBack?.autoPageStop()
dismiss()
}
}
private fun upTtsSpeechRate() {
ReadAloud.upTtsSpeechRate(requireContext())
if (!BaseReadAloudService.pause) {
ReadAloud.pause(requireContext())
ReadAloud.resume(requireContext())
}
}
interface CallBack {
fun showMenuBar()
fun openChapterList()
fun autoPageStop()
}
}

@ -111,6 +111,9 @@ class MoreConfigDialog : DialogFragment() {
Help.setOrientation(it)
}
}
PreferKey.textFullJustify -> {
postEvent(EventBus.UP_CONFIG, true)
}
}
}

@ -103,9 +103,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
tv_title_mode.onClick {
showTitleConfig()
}
tv_text_bold.onClick {
ReadBookConfig.textBold = !ReadBookConfig.textBold
tv_text_bold.isSelected = ReadBookConfig.textBold
text_font_weight_converter.onChanged {
postEvent(EventBus.UP_CONFIG, true)
}
tv_text_font.onClick {
@ -215,7 +213,6 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
private fun upStyle() {
ReadBookConfig.let {
tv_text_bold.isSelected = it.textBold
dsb_text_size.progress = it.textSize - 5
dsb_text_letter_spacing.progress = (it.letterSpacing * 100).toInt() + 50
dsb_line_size.progress = it.lineSpacingExtra

@ -0,0 +1,55 @@
package io.legado.app.ui.book.read.config
import android.content.Context
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.util.AttributeSet
import io.legado.app.R
import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.widget.text.StrokeTextView
import org.jetbrains.anko.sdk27.listeners.onClick
class TextFontWeightConverter(context: Context, attrs: AttributeSet?) : StrokeTextView(context, attrs) {
private val spannableString = SpannableString("中/粗/细")
private var enabledSpan: ForegroundColorSpan = ForegroundColorSpan(context.accentColor)
private var onChanged: (() -> Unit)? = null
init {
text = spannableString
if (!isInEditMode) {
upUi(ReadBookConfig.textBold)
}
onClick {
selectType()
}
}
private fun upUi(type: Int) {
spannableString.removeSpan(enabledSpan)
when (type) {
0 -> spannableString.setSpan(enabledSpan, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
1 -> spannableString.setSpan(enabledSpan, 2, 3, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
2 -> spannableString.setSpan(enabledSpan, 4, 5, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
}
text = spannableString
}
private fun selectType() {
context.alert(titleResource = R.string.text_font_weight_converter) {
items(context.resources.getStringArray(R.array.text_font_weight).toList()) { _, i ->
ReadBookConfig.textBold = i
upUi(i)
onChanged?.invoke()
}
}.show()
}
fun onChanged(unit: () -> Unit) {
onChanged = unit
}
}

@ -25,12 +25,10 @@ class TipConfigDialog : BaseDialogFragment() {
Help.upSystemUiVisibility(it)
it.windowManager?.defaultDisplay?.getMetrics(dm)
}
dialog?.window?.let {
val attr = it.attributes
attr.dimAmount = 0.0f
it.attributes = attr
it.setLayout((dm.widthPixels * 0.9).toInt(), ViewGroup.LayoutParams.WRAP_CONTENT)
}
dialog?.window?.setLayout(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
}
override fun onCreateView(

@ -6,6 +6,7 @@ import android.text.Layout
import android.text.StaticLayout
import android.text.TextPaint
import android.text.TextUtils
import com.hankcs.hanlp.HanLP
import io.legado.app.App
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.BookChapter
@ -78,6 +79,7 @@ object ChapterProvider {
item.title = bookChapter.title
item.upLinesPosition()
}
return TextChapter(
bookChapter.index,
bookChapter.title,
@ -161,6 +163,10 @@ object ChapterProvider {
desiredWidth: Float
) {
var x = 0f
if (!ReadBookConfig.textFullJustify) {
addCharsToLineLast(textLine, words, textPaint, x)
return
}
val bodyIndent = ReadBookConfig.bodyIndent
val icw = StaticLayout.getDesiredWidth(bodyIndent, textPaint) / bodyIndent.length
bodyIndent.toStringArray().forEach {
@ -186,6 +192,10 @@ object ChapterProvider {
desiredWidth: Float,
startX: Float
) {
if (!ReadBookConfig.textFullJustify) {
addCharsToLineLast(textLine, words, textPaint, startX)
return
}
val gapCount: Int = words.length - 1
val d = (visibleWidth - desiredWidth) / gapCount
var x = startX
@ -262,23 +272,37 @@ object ChapterProvider {
App.INSTANCE.removePref(PreferKey.readBookFont)
Typeface.SANS_SERIF
}
// 字体统一处理
val bold = Typeface.create(typeface, Typeface.BOLD)
val normal = Typeface.create(typeface, Typeface.NORMAL)
val (titleFont, textFont) = when (ReadBookConfig.textBold) {
1 -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
Pair(Typeface.create(typeface, 900, false), bold)
else
Pair(bold, bold)
}
2 -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
Pair(normal, Typeface.create(typeface, 300, false))
else
Pair(normal, normal)
}
else -> Pair(bold, normal)
}
//标题
titlePaint = TextPaint()
titlePaint.color = ReadBookConfig.durConfig.textColor()
titlePaint.letterSpacing = ReadBookConfig.letterSpacing
titlePaint.typeface = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
Typeface.create(typeface, if (ReadBookConfig.textBold) 900 else 700, false)
} else {
Typeface.create(typeface, Typeface.BOLD)
}
titlePaint.typeface = titleFont
titlePaint.textSize = with(ReadBookConfig) { textSize + titleSize }.sp.toFloat()
titlePaint.isAntiAlias = true
//正文
contentPaint = TextPaint()
contentPaint.color = ReadBookConfig.durConfig.textColor()
contentPaint.letterSpacing = ReadBookConfig.letterSpacing
val style = if (ReadBookConfig.textBold) Typeface.BOLD else Typeface.NORMAL
contentPaint.typeface = Typeface.create(typeface, style)
contentPaint.typeface = textFont
contentPaint.textSize = ReadBookConfig.textSize.sp.toFloat()
contentPaint.isAntiAlias = true
//间距

@ -207,11 +207,7 @@ class ContentView(context: Context) : FrameLayout(context) {
@SuppressLint("SetTextI18n")
fun setProgress(textPage: TextPage) = textPage.apply {
val title = when (AppConfig.chineseConverterType) {
1 -> HanLP.convertToSimplifiedChinese(textPage.title)
2 -> HanLP.convertToTraditionalChinese(textPage.title)
else -> textPage.title
}
val title = textPage.title
tvTitle?.text = title
tvPage?.text = "${index.plus(1)}/$pageSize"
tvTotalProgress?.text = readProgress

@ -58,7 +58,8 @@ class PageView(context: Context, attrs: AttributeSet) :
pageDelegate?.onDraw(canvas)
if (callBack.isAutoPage) {
nextPage.screenshot()?.let {
val bottom = page_view.height * callBack.autoPageProgress / 460
val bottom =
page_view.height * callBack.autoPageProgress / (ReadBookConfig.autoReadSpeed * 10)
autoPageRect.set(0, 0, page_view.width, bottom)
canvas.drawBitmap(it, autoPageRect, autoPageRect, null)
canvas.drawRect(
@ -118,9 +119,10 @@ class PageView(context: Context, attrs: AttributeSet) :
}
override fun upContent(relativePosition: Int, resetPageOffset: Boolean) {
if (ReadBookConfig.isScroll) {
if (ReadBookConfig.isScroll && !callBack.isAutoPage) {
curPage.setContent(pageFactory.currentPage, resetPageOffset)
} else {
curPage.resetPageOffset()
when (relativePosition) {
-1 -> prevPage.setContent(pageFactory.prevPage)
1 -> nextPage.setContent(pageFactory.nextPage)

@ -18,64 +18,40 @@ class DiffCallBack(private val oldItems: List<SearchBook>, private val newItems:
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
if (oldItem.name != newItem.name) {
return false
return when {
oldItem.name != newItem.name -> false
oldItem.author != newItem.author -> false
else -> true
}
if (oldItem.author != newItem.author) {
return false
}
return true
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
if (oldItem.origins.size != newItem.origins.size) {
return false
}
if (oldItem.coverUrl != newItem.coverUrl) {
return false
}
if (oldItem.kind != newItem.kind) {
return false
}
if (oldItem.latestChapterTitle != newItem.latestChapterTitle) {
return false
return when {
oldItem.origins.size != newItem.origins.size -> false
oldItem.coverUrl != newItem.coverUrl -> false
oldItem.kind != newItem.kind -> false
oldItem.latestChapterTitle != newItem.latestChapterTitle -> false
oldItem.intro != newItem.intro -> false
else -> true
}
if (oldItem.intro != newItem.intro) {
return false
}
return true
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val payload = Bundle()
val newItem = newItems[newItemPosition]
val oldItem = oldItems[oldItemPosition]
if (oldItem.name != newItem.name) {
payload.putString("name", newItem.name)
}
if (oldItem.author != newItem.author) {
payload.putString("author", newItem.author)
}
if (oldItem.origins.size != newItem.origins.size) {
if (oldItem.name != newItem.name) payload.putString("name", newItem.name)
if (oldItem.author != newItem.author) payload.putString("author", newItem.author)
if (oldItem.origins.size != newItem.origins.size)
payload.putInt("origins", newItem.origins.size)
}
if (oldItem.coverUrl != newItem.coverUrl) {
payload.putString("cover", newItem.coverUrl)
}
if (oldItem.kind != newItem.kind) {
payload.putString("kind", newItem.kind)
}
if (oldItem.latestChapterTitle != newItem.latestChapterTitle) {
if (oldItem.coverUrl != newItem.coverUrl) payload.putString("cover", newItem.coverUrl)
if (oldItem.kind != newItem.kind) payload.putString("kind", newItem.kind)
if (oldItem.latestChapterTitle != newItem.latestChapterTitle)
payload.putString("last", newItem.latestChapterTitle)
}
if (oldItem.intro != newItem.intro) {
payload.putString("intro", newItem.intro)
}
if (payload.isEmpty) {
return null
}
if (oldItem.intro != newItem.intro) payload.putString("intro", newItem.intro)
if (payload.isEmpty) return null
return payload
}
}

@ -8,7 +8,6 @@ import android.view.View.VISIBLE
import androidx.appcompat.widget.SearchView
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.flexbox.FlexboxLayoutManager
@ -53,7 +52,6 @@ class SearchActivity : VMBaseActivity<SearchViewModel>(R.layout.activity_book_se
private var menu: Menu? = null
private var precisionSearchMenuItem: MenuItem? = null
private var groups = linkedSetOf<String>()
private var refreshTime = System.currentTimeMillis()
override fun onActivityCreated(savedInstanceState: Bundle?) {
initRecyclerView()
@ -185,14 +183,13 @@ class SearchActivity : VMBaseActivity<SearchViewModel>(R.layout.activity_book_se
upGroupMenu()
})
viewModel.searchBookLiveData.observe(this, Observer {
upSearchItems(it, false)
upSearchItems(it)
})
viewModel.isSearchLiveData.observe(this, Observer {
if (it) {
startSearch()
} else {
searchFinally()
upSearchItems(viewModel.searchBooks, true)
}
})
}
@ -288,14 +285,8 @@ class SearchActivity : VMBaseActivity<SearchViewModel>(R.layout.activity_book_se
* 更新搜索结果
*/
@Synchronized
private fun upSearchItems(items: List<SearchBook>, isMandatoryUpdate: Boolean) {
val searchItems = ArrayList(items)
if (isMandatoryUpdate || System.currentTimeMillis() - refreshTime > 500) {
refreshTime = System.currentTimeMillis()
val diffResult =
DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), searchItems))
adapter.setItems(searchItems, diffResult)
}
private fun upSearchItems(items: List<SearchBook>) {
adapter.setItems(items)
}
/**
@ -321,7 +312,10 @@ class SearchActivity : VMBaseActivity<SearchViewModel>(R.layout.activity_book_se
override fun showBookInfo(name: String, author: String) {
viewModel.getSearchBook(name, author) { searchBook ->
searchBook?.let {
startActivity<BookInfoActivity>(Pair("bookUrl", it.bookUrl))
startActivity<BookInfoActivity>(
Pair("name", it.name),
Pair("author", it.author)
)
}
}
}
@ -331,7 +325,8 @@ class SearchActivity : VMBaseActivity<SearchViewModel>(R.layout.activity_book_se
*/
override fun showBookInfo(book: Book) {
startActivity<BookInfoActivity>(
Pair("bookUrl", book.bookUrl)
Pair("name", book.name),
Pair("author", book.author)
)
}

@ -1,6 +1,7 @@
package io.legado.app.ui.book.search
import android.app.Application
import android.os.Handler
import androidx.lifecycle.MutableLiveData
import io.legado.app.App
import io.legado.app.base.BaseViewModel
@ -12,15 +13,18 @@ import io.legado.app.utils.getPrefBoolean
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.isActive
class SearchViewModel(application: Application) : BaseViewModel(application)
, SearchBookModel.CallBack {
class SearchViewModel(application: Application) : BaseViewModel(application),
SearchBookModel.CallBack {
val handler = Handler()
private val searchBookModel = SearchBookModel(this, this)
var isSearchLiveData = MutableLiveData<Boolean>()
var searchBookLiveData = MutableLiveData<List<SearchBook>>()
var searchKey: String = ""
var isLoading = false
var searchBooks = arrayListOf<SearchBook>()
private var searchBooks = arrayListOf<SearchBook>()
private var searchID = 0L
private var postTime = 0L
private val sendRunnable = Runnable { upAdapter() }
/**
* 开始搜索
@ -36,6 +40,18 @@ class SearchViewModel(application: Application) : BaseViewModel(application)
searchBookModel.search(searchID, searchKey)
}
@Synchronized
private fun upAdapter() {
if (System.currentTimeMillis() >= postTime + 500) {
handler.removeCallbacks(sendRunnable)
postTime = System.currentTimeMillis()
searchBookLiveData.postValue(searchBooks)
} else {
handler.removeCallbacks(sendRunnable)
handler.postDelayed(sendRunnable, 500 - System.currentTimeMillis() + postTime)
}
}
override fun onSearchStart() {
isSearchLiveData.postValue(true)
}
@ -151,7 +167,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application)
})
if (!scope.isActive) return
searchBooks = copyDataS
searchBookLiveData.postValue(copyDataS)
upAdapter()
}
}

@ -58,6 +58,7 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
private var bookSourceLiveDate: LiveData<List<BookSource>>? = null
private var groups = linkedSetOf<String>()
private var groupMenu: SubMenu? = null
private var sort = 0
override fun onActivityCreated(savedInstanceState: Bundle?) {
initUriScheme()
@ -75,6 +76,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
groupMenu = menu?.findItem(R.id.menu_group)?.subMenu
groupMenu?.findItem(R.id.action_sort)?.subMenu
?.setGroupCheckable(R.id.menu_group_sort, true, true)
upGroupMenu()
return super.onPrepareOptionsMenu(menu)
}
@ -93,6 +96,26 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
allowExtensions = arrayOf("txt", "json")
)
R.id.menu_import_source_onLine -> showImportDialog()
R.id.menu_sort_manual -> {
item.isChecked = true
sort = 0
initLiveDataBookSource(search_view.query?.toString())
}
R.id.menu_sort_auto -> {
item.isChecked = true
sort = 2
initLiveDataBookSource(search_view.query?.toString())
}
R.id.menu_sort_pin_yin -> {
item.isChecked = true
sort = 3
initLiveDataBookSource(search_view.query?.toString())
}
R.id.menu_sort_url -> {
item.isChecked = true
sort = 4
initLiveDataBookSource(search_view.query?.toString())
}
}
if (item.groupId == R.id.source_group) {
search_view.setQuery(item.title, true)
@ -105,14 +128,14 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
when (it.path) {
"/importonline" -> it.getQueryParameter("src")?.let { url ->
Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show()
if (url.startsWith("http", false)){
if (url.startsWith("http", false)) {
viewModel.importSource(url) { msg ->
title_bar.snackbar(msg)
}
}
else{
viewModel.importSourceFromFilePath(url){msg ->
title_bar.snackbar(msg)}
} else {
viewModel.importSourceFromFilePath(url) { msg ->
title_bar.snackbar(msg)
}
}
}
else -> {
@ -150,10 +173,16 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
} else {
App.db.bookSourceDao().liveDataSearch("%$searchKey%")
}
bookSourceLiveDate?.observe(this, Observer {
bookSourceLiveDate?.observe(this, Observer { data ->
val sourceList = when (sort) {
1 -> data.sortedBy { it.weight }
2 -> data.sortedBy { it.bookSourceName }
3 -> data.sortedBy { it.bookSourceUrl }
else -> data
}
val diffResult = DiffUtil
.calculateDiff(DiffCallBack(ArrayList(adapter.getItems()), it))
adapter.setItems(it, diffResult)
.calculateDiff(DiffCallBack(ArrayList(adapter.getItems()), sourceList))
adapter.setItems(sourceList, diffResult)
upCountView()
})
}
@ -204,13 +233,35 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
R.id.menu_enable_explore -> viewModel.enableSelectExplore(adapter.getSelection())
R.id.menu_disable_explore -> viewModel.disableSelectExplore(adapter.getSelection())
R.id.menu_export_selection -> FilePicker.selectFolder(this, exportRequestCode)
R.id.menu_check_source -> CheckSource.start(this, adapter.getSelection())
R.id.menu_check_source -> checkSource()
R.id.menu_top_sel -> viewModel.topSource(*adapter.getSelection().toTypedArray())
R.id.menu_bottom_sel -> viewModel.bottomSource(*adapter.getSelection().toTypedArray())
}
return true
}
@SuppressLint("InflateParams")
private fun checkSource() {
alert(titleResource = R.string.search_book_key) {
var editText: AutoCompleteTextView? = null
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view
edit_view.setText(CheckSource.keyword)
}
}
okButton {
editText?.text?.toString()?.let {
if (it.isNotEmpty()) {
CheckSource.keyword = it
}
}
CheckSource.start(this@BookSourceActivity, adapter.getSelection())
}
noButton { }
}.show().applyTint()
}
private fun upGroupMenu() {
groupMenu?.removeGroup(R.id.source_group)
groups.sortedWith(Collator.getInstance(java.util.Locale.CHINESE))

@ -158,10 +158,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
execute {
val sources = App.db.bookSourceDao().getByGroup(group)
sources.map { source ->
source.bookSourceGroup?.splitNotBlank(",")?.toHashSet()?.let {
it.remove(group)
source.bookSourceGroup = TextUtils.join(",", it)
}
source.removeGroup(group)
}
App.db.bookSourceDao().update(*sources.toTypedArray())
}

@ -13,66 +13,73 @@ import io.legado.app.utils.readText
import java.io.File
class FileAssociationViewModel(application: Application) : BaseViewModel(application) {
fun dispatchIndent(uri: Uri): Intent? {
val url: String
//如果是普通的url,需要根据返回的内容判断是什么
if (uri.scheme == "file" || uri.scheme == "content") {
val content = if (uri.scheme == "file") {
val file = File(uri.path.toString())
if (file.exists()) {
file.readText()
try {
val url: String
//如果是普通的url,需要根据返回的内容判断是什么
if (uri.scheme == "file" || uri.scheme == "content") {
val content = if (uri.scheme == "file") {
val file = File(uri.path.toString())
if (file.exists()) {
file.readText()
} else {
null
}
} else {
null
DocumentFile.fromSingleUri(context, uri)?.readText(context)
}
} else {
DocumentFile.fromSingleUri(context, uri)?.readText(context)
}
var scheme = ""
if (content != null) {
if (content.isJsonObject() || content.isJsonArray()) {
//暂时根据文件内容判断属于什么
when {
content.contains("bookSourceUrl") -> {
scheme = "booksource"
}
content.contains("sourceUrl") -> {
scheme = "rsssource"
}
content.contains("pattern") -> {
scheme = "replace"
var scheme = ""
if (content != null) {
if (content.isJsonObject() || content.isJsonArray()) {
//暂时根据文件内容判断属于什么
when {
content.contains("bookSourceUrl") -> {
scheme = "booksource"
}
content.contains("sourceUrl") -> {
scheme = "rsssource"
}
content.contains("pattern") -> {
scheme = "replace"
}
}
}
}
if (TextUtils.isEmpty(scheme)) {
execute {
if (uri.scheme == "content"){
LocalBook.importFile(uri.toString())
}else{
LocalBook.importFile(uri.path.toString())
if (TextUtils.isEmpty(scheme)) {
execute {
if (uri.scheme == "content") {
LocalBook.importFile(uri.toString())
} else {
LocalBook.importFile(uri.path.toString())
}
toast("添加本地文件成功${uri.path}")
}
toast("添加本地文件成功${uri.path}")
return null
}
} else {
toast("文件不存在")
return null
}
// content模式下,需要传递完整的路径,方便后续解析
url = if (uri.scheme == "content") {
"yuedu://${scheme}/importonline?src=$uri"
} else {
"yuedu://${scheme}/importonline?src=${uri.path}"
}
} else if (uri.scheme == "yuedu") {
url = uri.toString()
} else {
toast("文件不存在")
return null
}
// content模式下,需要传递完整的路径,方便后续解析
url = if (uri.scheme == "content"){
"yuedu://${scheme}/importonline?src=$uri"
}else{
"yuedu://${scheme}/importonline?src=${uri.path}"
url = "yuedu://booksource/importonline?src=${uri.path}"
}
} else if (uri.scheme == "yuedu") {
url = uri.toString()
} else {
url = "yuedu://booksource/importonline?src=${uri.path}"
val data = Uri.parse(url)
val newIndent = Intent(Intent.ACTION_VIEW)
newIndent.data = data
return newIndent
} catch (e: Exception) {
e.printStackTrace()
toast(e.localizedMessage)
return null
}
val data = Uri.parse(url)
val newIndent = Intent(Intent.ACTION_VIEW)
newIndent.data = data
return newIndent
}
}

@ -26,13 +26,14 @@ import io.legado.app.ui.main.rss.RssFragment
import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_main.*
import org.jetbrains.anko.toast
class MainActivity : VMBaseActivity<MainViewModel>(R.layout.activity_main),
BottomNavigationView.OnNavigationItemSelectedListener,
ViewPager.OnPageChangeListener by ViewPager.SimpleOnPageChangeListener() {
override val viewModel: MainViewModel
get() = getViewModel(MainViewModel::class.java)
private var exitTime: Long = 0
private var pagePosition = 0
private val fragmentId = arrayOf(0, 1, 2, 3)
private val fragmentMap = mapOf<Int, Fragment>(
@ -105,8 +106,14 @@ class MainActivity : VMBaseActivity<MainViewModel>(R.layout.activity_main),
return true
}
if (!BaseReadAloudService.pause) {
moveTaskToBack(true)
return true
if (System.currentTimeMillis() - exitTime > 2000) {
toast(R.string.double_click_exit)
exitTime = System.currentTimeMillis()
} else {
moveTaskToBack(true)
return true
}
return false
}
}
}
@ -121,6 +128,15 @@ class MainActivity : VMBaseActivity<MainViewModel>(R.layout.activity_main),
}
}
override fun finish() {
if (System.currentTimeMillis() - exitTime > 2000) {
toast(R.string.double_click_exit)
exitTime = System.currentTimeMillis()
} else {
super.finish()
}
}
override fun onDestroy() {
super.onDestroy()
ReadAloud.stop(this)

@ -2,6 +2,7 @@ package io.legado.app.ui.main.bookshelf.books
import android.content.Context
import android.os.Bundle
import android.view.View
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.constant.BookType
@ -22,33 +23,30 @@ class BooksAdapterGrid(context: Context, private val callBack: CallBack) :
ATH.applyBackgroundTint(this)
tv_name.text = item.name
iv_cover.load(item.getDisplayCover(), item.name, item.author)
if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) {
bv_unread.invisible()
rl_loading.show()
} else {
rl_loading.hide()
bv_unread.setBadgeCount(item.getUnreadChapterNum())
bv_unread.setHighlight(item.lastCheckCount > 0)
}
upRefresh(this, item)
} else {
bundle.keySet().map {
when (it) {
"name" -> tv_name.text = item.name
"cover" -> iv_cover.load(item.getDisplayCover(), item.name, item.author)
"refresh" -> if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) {
bv_unread.invisible()
rl_loading.show()
} else {
rl_loading.hide()
bv_unread.setBadgeCount(item.getUnreadChapterNum())
bv_unread.setHighlight(item.lastCheckCount > 0)
}
"refresh" -> upRefresh(this, item)
}
}
}
}
}
private fun upRefresh(itemView: View, item: Book) = with(itemView) {
if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) {
bv_unread.invisible()
rl_loading.show()
} else {
rl_loading.hide()
bv_unread.setBadgeCount(item.getUnreadChapterNum())
bv_unread.setHighlight(item.lastCheckCount > 0)
}
}
override fun registerListener(holder: ItemViewHolder) {
holder.itemView.apply {
onClick {

@ -2,6 +2,7 @@ package io.legado.app.ui.main.bookshelf.books
import android.content.Context
import android.os.Bundle
import android.view.View
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.constant.BookType
@ -25,14 +26,7 @@ class BooksAdapterList(context: Context, private val callBack: CallBack) :
tv_read.text = item.durChapterTitle
tv_last.text = item.latestChapterTitle
iv_cover.load(item.getDisplayCover(), item.name, item.author)
if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) {
bv_unread.invisible()
rl_loading.show()
} else {
rl_loading.hide()
bv_unread.setBadgeCount(item.getUnreadChapterNum())
bv_unread.setHighlight(item.lastCheckCount > 0)
}
upRefresh(this, item)
} else {
bundle.keySet().map {
when (it) {
@ -41,20 +35,24 @@ class BooksAdapterList(context: Context, private val callBack: CallBack) :
"dur" -> tv_read.text = item.durChapterTitle
"last" -> tv_last.text = item.latestChapterTitle
"cover" -> iv_cover.load(item.getDisplayCover(), item.name, item.author)
"refresh" -> if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) {
bv_unread.invisible()
rl_loading.show()
} else {
rl_loading.hide()
bv_unread.setBadgeCount(item.getUnreadChapterNum())
bv_unread.setHighlight(item.lastCheckCount > 0)
}
"refresh" -> upRefresh(this, item)
}
}
}
}
}
private fun upRefresh(itemView: View, item: Book) = with(itemView) {
if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) {
bv_unread.invisible()
rl_loading.show()
} else {
rl_loading.hide()
bv_unread.setHighlight(item.lastCheckCount > 0)
bv_unread.setBadgeCount(item.getUnreadChapterNum())
}
}
override fun registerListener(holder: ItemViewHolder) {
holder.itemView.apply {
onClick {

@ -16,54 +16,45 @@ class BooksDiffCallBack(private val oldItems: List<Book>, private val newItems:
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldItems[oldItemPosition].bookUrl == newItems[newItemPosition].bookUrl
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return oldItem.name == newItem.name
&& oldItem.author == newItem.author
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
if (oldItem.name != newItem.name)
return false
if (oldItem.author != newItem.author)
return false
if (oldItem.durChapterTitle != newItem.durChapterTitle)
return false
if (oldItem.latestChapterTitle != newItem.latestChapterTitle)
return false
if (oldItem.lastCheckCount != newItem.lastCheckCount)
return false
if (oldItem.getDisplayCover() != newItem.getDisplayCover())
return false
if (oldItem.getUnreadChapterNum() != newItem.getUnreadChapterNum())
return false
return true
return when {
oldItem.durChapterTime != newItem.durChapterTime -> false
oldItem.name != newItem.name -> false
oldItem.author != newItem.author -> false
oldItem.durChapterTitle != newItem.durChapterTitle -> false
oldItem.latestChapterTitle != newItem.latestChapterTitle -> false
oldItem.lastCheckCount != newItem.lastCheckCount -> false
oldItem.getDisplayCover() != newItem.getDisplayCover() -> false
oldItem.getUnreadChapterNum() != newItem.getUnreadChapterNum() -> false
else -> true
}
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
val bundle = bundleOf()
if (oldItem.name != newItem.name)
bundle.putString("name", null)
if (oldItem.author != newItem.author)
bundle.putString("author", null)
if (oldItem.durChapterTitle != newItem.durChapterTitle)
bundle.putString("dur", null)
if (oldItem.latestChapterTitle != newItem.latestChapterTitle)
bundle.putString("last", null)
if (oldItem.getDisplayCover() != newItem.getDisplayCover())
bundle.putString("cover", null)
if (oldItem.lastCheckCount != newItem.lastCheckCount)
bundle.putString("refresh", null)
if (oldItem.getUnreadChapterNum() != newItem.getUnreadChapterNum()
if (oldItem.name != newItem.name) bundle.putString("name", null)
if (oldItem.author != newItem.author) bundle.putString("author", null)
if (oldItem.durChapterTitle != newItem.durChapterTitle) bundle.putString("dur", null)
if (oldItem.latestChapterTitle != newItem.latestChapterTitle) bundle.putString("last", null)
if (oldItem.getDisplayCover() != newItem.getDisplayCover()) bundle.putString("cover", null)
if (oldItem.lastCheckCount != newItem.lastCheckCount
|| oldItem.durChapterTime != newItem.durChapterTime
|| oldItem.getUnreadChapterNum() != newItem.getUnreadChapterNum()
|| oldItem.lastCheckCount != newItem.lastCheckCount
) {
bundle.putString("refresh", null)
}
if (bundle.isEmpty) {
return null
}
if (bundle.isEmpty) return null
return bundle
}

@ -132,7 +132,10 @@ class BooksFragment : BaseFragment(R.layout.fragment_books),
}
override fun openBookInfo(book: Book) {
context?.startActivity<BookInfoActivity>(Pair("bookUrl", book.bookUrl))
context?.startActivity<BookInfoActivity>(
Pair("name", book.name),
Pair("author", book.author)
)
}
override fun isUpdate(bookUrl: String): Boolean {

@ -12,7 +12,6 @@ import io.legado.app.help.AppConfig
import io.legado.app.lib.theme.ColorUtils
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.utils.getCompatColor
class PreferenceCategory(context: Context, attrs: AttributeSet) : PreferenceCategory(context, attrs) {
@ -28,12 +27,9 @@ class PreferenceCategory(context: Context, attrs: AttributeSet) : PreferenceCate
val view = it.findViewById(R.id.preference_title)
if (view is TextView) { // && !view.isInEditMode
view.text = title
if (view.isInEditMode) {
view.setTextColor(context.getCompatColor(R.color.colorAccent))
} else {
view.setBackgroundColor(context.backgroundColor)
view.setTextColor(context.accentColor)
}
if (view.isInEditMode) return
view.setBackgroundColor(context.backgroundColor)
view.setTextColor(context.accentColor)
view.isVisible = title != null && title.isNotEmpty()
val da = it.findViewById(R.id.preference_divider_above)

@ -26,7 +26,7 @@ class SwitchPreference(context: Context, attrs: AttributeSet) :
widgetLayoutResource,
R.id.switchWidget
)
if (v is SwitchCompat) {
if (v is SwitchCompat && !v.isInEditMode) {
ATH.setTint(v, context.accentColor)
}
super.onBindViewHolder(holder)

@ -15,6 +15,7 @@ val GSON: Gson by lazy {
object : TypeToken<Map<String?, Any?>?>() {}.type,
MapDeserializerDoubleAsIntFix()
)
.registerTypeAdapter(Int::class.java, IntJsonDeserializer())
.disableHtmlEscaping()
.setPrettyPrinting()
.create()
@ -22,7 +23,6 @@ val GSON: Gson by lazy {
inline fun <reified T> genericType(): Type = object : TypeToken<T>() {}.type
@Throws(JsonSyntaxException::class)
inline fun <reified T> Gson.fromJsonObject(json: String?): T? {//可转成任意类型
return attempt {
val result: T? = fromJson(json, genericType<T>())
@ -30,7 +30,6 @@ inline fun <reified T> Gson.fromJsonObject(json: String?): T? {//可转成任意
}.value
}
@Throws(JsonSyntaxException::class)
inline fun <reified T> Gson.fromJsonArray(json: String?): List<T>? {
return attempt {
val result: List<T>? = fromJson(json, ParameterizedTypeImpl(T::class.java))
@ -46,6 +45,32 @@ class ParameterizedTypeImpl(private val clazz: Class<*>) : ParameterizedType {
override fun getActualTypeArguments(): Array<Type> = arrayOf(clazz)
}
/**
* int类型转化失败时跳过
*/
class IntJsonDeserializer : JsonDeserializer<Int?> {
override fun deserialize(
json: JsonElement,
typeOfT: Type?,
context: JsonDeserializationContext?
): Int? {
return when {
json.isJsonPrimitive -> {
val prim = json.asJsonPrimitive
if (prim.isNumber) {
prim.asNumber.toInt()
} else {
null
}
}
else -> null
}
}
}
/**
* 修复Int变为Double的问题
*/
@ -62,20 +87,20 @@ class MapDeserializerDoubleAsIntFix :
return read(jsonElement) as? Map<String, Any?>
}
fun read(`in`: JsonElement): Any? {
fun read(json: JsonElement): Any? {
when {
`in`.isJsonArray -> {
json.isJsonArray -> {
val list: MutableList<Any?> = ArrayList()
val arr = `in`.asJsonArray
val arr = json.asJsonArray
for (anArr in arr) {
list.add(read(anArr))
}
return list
}
`in`.isJsonObject -> {
json.isJsonObject -> {
val map: MutableMap<String, Any?> =
LinkedTreeMap()
val obj = `in`.asJsonObject
val obj = json.asJsonObject
val entitySet =
obj.entrySet()
for ((key, value) in entitySet) {
@ -83,8 +108,8 @@ class MapDeserializerDoubleAsIntFix :
}
return map
}
`in`.isJsonPrimitive -> {
val prim = `in`.asJsonPrimitive
json.isJsonPrimitive -> {
val prim = json.asJsonPrimitive
when {
prim.isBoolean -> {
return prim.asBoolean

@ -0,0 +1,200 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/background_menu"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_tts_SpeechRate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="1"
android:text="@string/auto_page_speed"
android:textColor="@color/tv_text_default"
android:textSize="14sp" />
</LinearLayout>
<io.legado.app.lib.theme.view.ATESeekBar
android:id="@+id/seek_auto_read"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:min="10"
android:max="100"
tools:ignore="UnusedAttribute" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="8dp"
android:baselineAligned="false"
android:orientation="horizontal">
<!--目录按钮-->
<LinearLayout
android:id="@+id/ll_catalog"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/chapter_list"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="7dp">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:contentDescription="@string/chapter_list"
android:src="@drawable/ic_toc"
app:tint="@color/tv_text_default"
tools:ignore="NestedWeights" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:text="@string/chapter_list"
android:maxLines="1"
android:textColor="@color/tv_text_default"
android:textSize="12sp" />
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
<!--调节按钮-->
<LinearLayout
android:id="@+id/ll_main_menu"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/read_aloud"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="7dp">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:contentDescription="@string/main_menu"
android:src="@drawable/ic_menu"
app:tint="@color/tv_text_default"
tools:ignore="NestedWeights" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:text="@string/main_menu"
android:maxLines="1"
android:textColor="@color/tv_text_default"
android:textSize="12sp" />
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
<!--界面按钮-->
<LinearLayout
android:id="@+id/ll_auto_page_stop"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/stop"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="7dp">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:contentDescription="@string/stop"
android:src="@drawable/ic_auto_page_stop"
app:tint="@color/tv_text_default"
tools:ignore="NestedWeights" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:text="@string/stop"
android:maxLines="1"
android:textColor="@color/tv_text_default"
android:textSize="12sp" />
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
<!--设置按钮-->
<LinearLayout
android:id="@+id/ll_setting"
android:layout_width="50dp"
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/setting"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="7dp">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:contentDescription="@string/aloud_config"
android:src="@drawable/ic_settings"
app:tint="@color/tv_text_default"
tools:ignore="NestedWeights" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="3dp"
android:text="@string/setting"
android:maxLines="1"
android:textColor="@color/tv_text_default"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

@ -273,7 +273,7 @@
android:layout_height="50dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/interface_setting"
android:contentDescription="@string/to_backstage"
android:focusable="true"
android:orientation="vertical"
android:paddingBottom="7dp">

@ -37,15 +37,14 @@
android:layout_height="wrap_content"
android:layout_weight="1" />
<io.legado.app.ui.widget.text.StrokeTextView
android:id="@+id/tv_text_bold"
<io.legado.app.ui.book.read.config.TextFontWeightConverter
android:id="@+id/text_font_weight_converter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="6dp"
android:paddingRight="6dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:text="@string/text_bold"
android:gravity="center"
android:textSize="14sp"
app:radius="3dp" />

@ -19,6 +19,7 @@
android:paddingLeft="16dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colorAccent"
tools:ignore="RtlHardcoded,RtlSymmetry" />
<View

@ -23,22 +23,30 @@
<menu>
<group
android:id="@+id/menu_group_sort"
android:checkableBehavior="single" />
<item
android:id="@+id/sort_manual"
android:checkable="true"
android:title="@string/sort_manual" />
<item
android:id="@+id/sort_auto"
android:checkable="true"
android:title="@string/sort_auto" />
<item
android:id="@+id/sort_pin_yin"
android:checkable="true"
android:title="@string/sort_pin_yin" />
android:checkableBehavior="single">
<item
android:id="@+id/menu_sort_manual"
android:checkable="true"
android:checked="true"
android:title="@string/sort_manual" />
<item
android:id="@+id/menu_sort_auto"
android:checkable="true"
android:title="@string/sort_auto" />
<item
android:id="@+id/menu_sort_pin_yin"
android:checkable="true"
android:title="@string/sort_pin_yin" />
<item
android:id="@+id/menu_sort_url"
android:checkable="true"
android:title="@string/sort_by_url" />
</group>
</menu>

@ -49,4 +49,9 @@
<item>系統等寬字體</item>
</string-array>
<string-array name="text_font_weight">
<item>正常</item>
<item>粗體</item>
<item>細體</item>
</string-array>
</resources>

@ -691,4 +691,14 @@
<string name="hideFooter">隱藏頁脚</string>
<string name="auto_change_source">自動換源</string>
<string name="to_bottom">置底</string>
<string name="text_full_justify">文字兩端對齊</string>
<string name="auto_page_speed">自動翻頁速度</string>
<string name="sort_by_url">地址排序</string>
<string name="text_font_weight_converter">文章字體轉換</string>
<string name="select_backup_path">請選擇備份路徑</string>
<string name="other">其它</string>
<string name="official_account" translatable="false">legado-top</string>
<string name="backup_summary">本地和WebDav壹起備份</string>
<string name="restore_summary">優先從WebDav恢復,失敗時從本地恢復</string>
<string name="import_old_summary">選擇舊版備份文件夾</string>
</resources>

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="book_type">
<item>@string/book_type_text</item>
<item>@string/book_type_audio</item>
</string-array>
<string-array name="tts_speech_per">
<item>度小美</item>
<item>度小宇</item>
<item>度逍遙</item>
<item>度丫丫</item>
<item>度小嬌</item>
<item>度米朵</item>
<item>度博文</item>
<item>度小童</item>
<item>度小萌</item>
<item>百度騷男</item>
<item>百度評書</item>
<item>百度主持</item>
</string-array>
<string-array name="indent">
<item>@string/indent_0</item>
<item>@string/indent_1</item>
<item>@string/indent_2</item>
<item>@string/indent_3</item>
<item>@string/indent_4</item>
</string-array>
<string-array name="text_suffix">
<item>.txt</item>
<item>.json</item>
<item>.xml</item>
</string-array>
<string-array name="convert_s">
<item>@string/jf_convert_o</item>
<item>@string/jf_convert_j</item>
<item>@string/jf_convert_f</item>
</string-array>
<string-array name="theme_mode">
<item>跟隨系統</item>
<item>亮色主題</item>
<item>暗色主題</item>
</string-array>
<string-array name="NavBarColors">
<item>自動</item>
<item>黑色</item>
<item>白色</item>
<item>跟隨背景</item>
</string-array>
<string-array name="screen_time_out">
<item>預設</item>
<item>1分鐘</item>
<item>2分鐘</item>
<item>3分鐘</item>
<item>常亮</item>
</string-array>
<string-array name="select_folder">
<item>@string/default_path</item>
<item>@string/sys_folder_picker</item>
<item>@string/app_folder_picker</item>
</string-array>
<string-array name="screen_direction_title">
<item>@string/screen_unspecified</item>
<item>@string/screen_portrait</item>
<item>@string/screen_landscape</item>
<item>@string/screen_sensor</item>
</string-array>
<string-array name="icon_names">
<item>iconMain</item>
<item>icon1</item>
<item>icon2</item>
<item>icon3</item>
<item>icon4</item>
<item>icon5</item>
<item>icon6</item>
</string-array>
<string-array name="chinese_mode">
<item>關閉</item>
<item>繁體轉簡體</item>
<item>簡體轉繁體</item>
</string-array>
<string-array name="system_typefaces">
<item>系統預設字體</item>
<item>系統襯線字體</item>
<item>系統等寬字體</item>
</string-array>
<string-array name="read_tip">
<item></item>
<item>標題</item>
<item>時間</item>
<item>電量</item>
<item>頁數</item>
<item>進度</item>
<item>頁數及進度</item>
</string-array>
<string-array name="text_font_weight">
<item>正常</item>
<item>粗體</item>
<item>細體</item>
</string-array>
</resources>

@ -0,0 +1,705 @@
<resources>
<!--App-->
<string name="app_name">閱讀</string>
<string name="receiving_shared_label">閱讀·搜尋</string>
<string name="tip_perm_request_storage">閱讀需要存取記憶卡權限,請前往“設定”—“應用權限”—打開所需權限</string>
<!--Other-->
<string name="menu_backup">Home</string>
<string name="menu_restore">復原</string>
<string name="menu_import_old">匯入閱讀資料</string>
<string name="mkdirs">建立子資料夾</string>
<string name="mkdirs_description">建立legado資料夾作為備份資料夾</string>
<string name="backup_path">備份路徑</string>
<string name="menu_import_old_version">匯入舊版資料</string>
<string name="menu_import_github">匯入Github資料</string>
<string name="menu_replace_rule">淨化取代</string>
<string name="menu_send">Send</string>
<string name="dialog_title">提示</string>
<string name="dialog_cancel">取消</string>
<string name="dialog_confirm">確定</string>
<string name="dialog_setting">去設定</string>
<string name="tip_cannot_jump_setting_page">無法跳轉至設定介面</string>
<string name="dynamic_click_retry">點擊重試</string>
<string name="dynamic_loading">正在載入</string>
<string name="draw">提醒</string>
<string name="edit">編輯</string>
<string name="delete">刪除</string>
<string name="replace">取代</string>
<string name="replace_purify">取代淨化</string>
<string name="replace_purify_desc">配置取代淨化規則</string>
<string name="not_available">暫無</string>
<string name="enable">啟用</string>
<string name="replace_purify_search">取代淨化-搜尋</string>
<string name="bookshelf">書架</string>
<string name="favorites">收藏夾</string>
<string name="favorite">收藏</string>
<string name="in_favorites">已收藏</string>
<string name="out_favorites">未收藏</string>
<string name="rss">訂閱</string>
<string name="all">全部</string>
<string name="recent_reading">最近閱讀</string>
<string name="last_read">最後閱讀</string>
<string name="update_log">更新日誌</string>
<string name="bookshelf_empty">書架還空著,先去添加吧!</string>
<string name="action_search">搜尋</string>
<string name="action_download">下載</string>
<string name="layout_list">列表</string>
<string name="layout_grid3">網格三列</string>
<string name="layout_grid4">網格四列</string>
<string name="layout_grid5">網格五列</string>
<string name="layout_grid6">網格六列</string>
<string name="bookshelf_layout">書架布局</string>
<string name="view">檢視</string>
<string name="book_library">書城</string>
<string name="book_local">添加本機</string>
<string name="book_source">書源</string>
<string name="book_source_manage">書源管理</string>
<string name="book_source_manage_desc">建立/匯入/編輯/管理書源</string>
<string name="setting">設定</string>
<string name="theme_setting">主題設定</string>
<string name="theme_setting_s">與介面/顏色相關的一些設定</string>
<string name="other_setting">其它設定</string>
<string name="other_setting_s">與功能相關的一些設定</string>
<string name="about">關於</string>
<string name="donate">捐贈</string>
<string name="exit">退出</string>
<string name="exit_no_save">尚未儲存,是否繼續編輯</string>
<string name="read_style">閱讀樣式設定</string>
<string name="version">版本</string>
<string name="local">本機</string>
<string name="search">搜尋</string>
<string name="origin_format">來源: %s</string>
<string name="read_dur_progress">最近: %s</string>
<string name="book_name">書名</string>
<string name="lasted_show">最新: %s</string>
<string name="check_add_bookshelf">是否將《%s》放入書架?</string>
<string name="import_books_count">共%s個Text文件</string>
<string name="is_loading">載入中…</string>
<string name="retry">重試</string>
<string name="web_service">Web 服務</string>
<string name="web_service_desc">啟用Web服務</string>
<string name="web_edit_source">web編輯書源</string>
<string name="http_ip">http://%1$s:%2$d</string>
<string name="download_offline">離線下載</string>
<string name="download_offline_t">離線下載</string>
<string name="download_offline_s">下載選擇的章節到本機</string>
<string name="change_origin">換源</string>
<string name="about_description">
\u3000\u3000這是一款使用Kotlin全新開發的開源的閱讀軟體,歡迎您的加入。關注公眾號[開源閱讀]!
</string>
<string name="app_share_description">
閱讀3.0下載網址:\nhttps://play.google.com/store/apps/details?id=io.legado.app
</string>
<string name="version_name">Version %s</string>
<string name="pt_auto_refresh">自動重新整理</string>
<string name="ps_auto_refresh">打開軟體時自動更新書籍</string>
<string name="pt_auto_download">自動下載最新章節</string>
<string name="ps_auto_download">更新書籍時自動下載最新章節</string>
<string name="backup_restore">備份與復原</string>
<string name="web_dav_set">WebDav設定</string>
<string name="web_dav_set_import_old">WebDav設定/匯入舊版本資料</string>
<string name="backup">備份</string>
<string name="restore">復原</string>
<string name="backup_permission">備份請給與儲存權限</string>
<string name="restore_permission">復原請給與儲存權限</string>
<string name="ok">確認</string>
<string name="cancel">取消</string>
<string name="backup_confirmation">確認備份嗎?</string>
<string name="backup_message">新備份會取代原有備份。\n備份資料夾YueDu</string>
<string name="restore_confirmation">確認復原嗎?</string>
<string name="restore_message">復原書架會覆蓋現有書架。</string>
<string name="backup_success">備份成功</string>
<string name="backup_fail">備份失敗</string>
<string name="on_restore">正在復原</string>
<string name="restore_success">復原成功</string>
<string name="restore_fail">復原失敗</string>
<string name="screen_direction">螢幕方向</string>
<string name="screen_sensor">跟隨感測器</string>
<string name="screen_landscape">橫向</string>
<string name="screen_portrait">豎向</string>
<string name="screen_unspecified">跟隨系統</string>
<string name="disclaimer">免責聲明</string>
<string name="all_chapter_num">共%d章</string>
<string name="interface_setting">介面</string>
<string name="brightness">亮度</string>
<string name="chapter_list">目錄</string>
<string name="next_chapter">下一章</string>
<string name="previous_chapter">上一章</string>
<string name="pt_hide_status_bar">隱藏狀態欄</string>
<string name="ps_hide_status_bar">閱讀介面隱藏狀態欄</string>
<string name="read_aloud">朗讀</string>
<string name="read_aloud_t">正在朗讀</string>
<string name="read_aloud_s">點擊打開閱讀介面</string>
<string name="audio_play">播放</string>
<string name="audio_play_t">正在播放</string>
<string name="audio_play_s">點擊打開播放介面</string>
<string name="audio_pause">播放暫停</string>
<string name="text_return">返回</string>
<string name="refresh">重新整理</string>
<string name="start">開始</string>
<string name="stop">停止</string>
<string name="pause">暫停</string>
<string name="resume">繼續</string>
<string name="set_timer">定時</string>
<string name="read_aloud_pause">朗讀暫停</string>
<string name="read_aloud_timer">正在朗讀(還剩%d分鐘)</string>
<string name="ps_hide_navigation_bar">閱讀介面隱藏虛擬按鍵</string>
<string name="pt_hide_navigation_bar">隱藏導航欄</string>
<string name="re_navigation_bar_color">導航欄顏色</string>
<string name="git_hub">GitHub</string>
<string name="scoring">評分</string>
<string name="send_mail">發送郵件</string>
<string name="can_not_open">無法打開</string>
<string name="can_not_share">分享失敗</string>
<string name="no_chapter">無章節</string>
<string name="add_url">添加網址</string>
<string name="add_book_url">添加書籍網址</string>
<string name="background">背景</string>
<string name="author">作者</string>
<string name="author_show">作者: %s</string>
<string name="aloud_stop">朗讀停止</string>
<string name="clear_cache">清除快取</string>
<string name="clear_cache_success">成功清理快取</string>
<string name="action_save">儲存</string>
<string name="edit_source">編輯源</string>
<string name="edit_book_source">編輯書源</string>
<string name="disable_book_source">禁用書源</string>
<string name="add_book_source">建立書源</string>
<string name="add_rss_source">建立訂閱源</string>
<string name="book_file_selector">添加書籍</string>
<string name="scan_book_source">掃描</string>
<string name="copy_source">複製源</string>
<string name="paste_source">貼上源</string>
<string name="source_rule_s">源規則說明</string>
<string name="check_update">檢查更新</string>
<string name="camera_scan">掃描二維碼</string>
<string name="scan_image">掃描本機圖片</string>
<string name="rule_summary">規則說明</string>
<string name="share">分享</string>
<string name="share_app">軟體分享</string>
<string name="flow_sys">跟隨系統</string>
<string name="add">添加</string>
<string name="import_book_source">匯入書源</string>
<string name="import_book_source_local">本機匯入</string>
<string name="import_book_source_on_line">網路匯入</string>
<string name="replace_rule_title">取代淨化</string>
<string name="replace_rule_edit">取代規則編輯</string>
<string name="replace_rule">取代規則</string>
<string name="replace_to">取代為</string>
<string name="img_cover">封面</string>
<string name="book"></string>
<string name="volume_key_page">音量鍵翻頁</string>
<string name="click_turn_page">點擊翻頁</string>
<string name="click_all_next_page">點擊總是翻下一頁</string>
<string name="page_anim">翻頁動畫</string>
<string name="keep_light">螢幕超時</string>
<string name="back">返回</string>
<string name="menu">選單</string>
<string name="adjust">調節</string>
<string name="scroll_bar">滾動條</string>
<string name="clear_all_content">清除快取會刪除所有已儲存章節,是否確認刪除?</string>
<string name="book_source_share_url">書源共享</string>
<string name="replace_rule_summary">取代規則名稱</string>
<string name="replace_rule_invalid">取代規則為空或者不滿足正規表示式要求</string>
<string name="select_action">選擇操作</string>
<string name="select_all">全選</string>
<string name="select_all_count">全選(%1$d/%2$d)</string>
<string name="select_cancel_count">取消(%1$d/%2$d)</string>
<string name="dark_theme">深色模式</string>
<string name="welcome">啟動頁</string>
<string name="download_start">開始下載</string>
<string name="download_cancel">取消下載</string>
<string name="no_download">暫無任務</string>
<string name="download_count">已下載 %1$d/%2$d</string>
<string name="import_select_book">匯入選擇書籍</string>
<string name="threads_num_title">更新和搜尋執行緒數,太多會卡頓</string>
<string name="change_icon">切換圖示</string>
<string name="remove_from_bookshelf">刪除書籍</string>
<string name="start_read">開始閱讀</string>
<string name="data_loading">載入資料中…</string>
<string name="load_error_retry">載入失敗,點擊重試</string>
<string name="book_intro">內容簡介</string>
<string name="intro_show">簡介:%s</string>
<string name="open_from_other">打開外部書籍</string>
<string name="origin_show">來源: %s</string>
<string name="import_replace_rule">本機匯入</string>
<string name="import_replace_rule_on_line">匯入線上規則</string>
<string name="check_update_interval">檢查更新間隔</string>
<string name="bookshelf_px_0">按閱讀時間</string>
<string name="bookshelf_px_1">按更新時間</string>
<string name="bookshelf_px_2">按書名</string>
<string name="bookshelf_px_3">手動排序</string>
<string name="read_type">閱讀方式</string>
<string name="compose_type">排版</string>
<string name="del_select">刪除所選</string>
<string name="del_msg">是否確認刪除?</string>
<string name="clear_font">預設字體</string>
<string name="find_on_www">發現</string>
<string name="find_source_manage">發現管理</string>
<string name="find_empty">沒有內容,去書源裡自訂吧!</string>
<string name="del_all">刪除所有</string>
<string name="searchHistory">搜尋歷史</string>
<string name="clear">清除</string>
<string name="showTitle">正文顯示標題</string>
<string name="refresh_default">書源同步</string>
<string name="no_last_chapter">無最新章節訊息</string>
<string name="showTimeBattery">顯示時間和電量</string>
<string name="showLine">顯示分隔線</string>
<string name="dark_status_icon">深色狀態欄圖示</string>
<string name="content">內容</string>
<string name="copy_text">複製內容</string>
<string name="download_all">一鍵快取</string>
<string name="content_sl">這是一段測試文字\n\u3000\u3000只是讓你看看效果的</string>
<string name="text_bg_style">文字顏色和背景(長按自訂)</string>
<string name="immersion_status_bar">沉浸式狀態欄</string>
<string name="un_download">還剩%d章未下載</string>
<string name="non_select">沒有選擇</string>
<string name="long_click_input_color">長按輸入顏色值</string>
<string name="loading">載入中…</string>
<string name="group_zg">追更區</string>
<string name="group_yf">養肥區</string>
<string name="bookmark">書籤</string>
<string name="bookmark_add">添加書籤</string>
<string name="action_del">刪除</string>
<string name="load_over_time">載入超時</string>
<string name="join_group">關注:%s</string>
<string name="copy_complete">已複製</string>
<string name="arrange_bookshelf">整理書架</string>
<string name="clear_bookshelf_s">這將會刪除所有書籍,請謹慎操作。</string>
<string name="search_book_source">搜尋書源</string>
<string name="search_rss_source">搜尋訂閱源</string>
<string name="search_book_source_num">搜尋(共%d個書源)</string>
<string name="chapter_list_size">目錄(%d)</string>
<string name="text_bold">加粗</string>
<string name="text_font">字體</string>
<string name="text">文字</string>
<string name="home_page">軟體首頁</string>
<string name="right"></string>
<string name="left"></string>
<string name="bottom"></string>
<string name="top"></string>
<string name="padding">邊距</string>
<string name="padding_top">上邊距</string>
<string name="padding_bottom">下邊距</string>
<string name="padding_left">左邊距</string>
<string name="padding_right">右邊距</string>
<string name="check_book_source">校驗書源</string>
<string name="check_select_source">校驗所選</string>
<string name="progress_show">進度 %1$d/%2$d</string>
<string name="tts_fix">請安裝並選擇中文TTS!</string>
<string name="tts_init_failed">TTS初始化失敗!</string>
<string name="jf_convert">簡繁轉換</string>
<string name="jf_convert_o">關閉</string>
<string name="jf_convert_f">簡轉繁</string>
<string name="jf_convert_j">繁轉簡</string>
<string name="page_mode">翻頁模式</string>
<string name="nb_file_sub_count">%1$d 項</string>
<string name="nb_file_path">記憶卡:</string>
<string name="nb_file_add_shelf">加入書架</string>
<string name="nb_file_add_shelves">加入書架(%1$d)</string>
<string name="nb_file_add_succeed">成功添加%1$d本書</string>
<string name="fonts_folder">請將字體檔案放到SD根目錄Fonts資料夾下重新選擇</string>
<string name="default_font">預設字體</string>
<string name="select_font">選擇字體</string>
<string name="text_size">字號</string>
<string name="line_size">行距</string>
<string name="paragraph_size">段距</string>
<string name="to_top">置頂</string>
<string name="to_bottom">置底</string>
<string name="auto_expand_find">自動展開發現</string>
<string name="default_expand_first">預設展開第一組發現</string>
<string name="threads_num">目前執行緒數 %s</string>
<string name="read_aloud_speed">朗讀語速</string>
<string name="auto_next_page">自動翻頁</string>
<string name="auto_next_page_stop">停止自動翻頁</string>
<string name="auto_next_page_speed">自動翻頁間隔</string>
<string name="book_info">書籍訊息</string>
<string name="book_info_edit">書籍訊息編輯</string>
<string name="ps_default_read">預設打開書架</string>
<string name="pt_default_read">自動跳轉最近閱讀</string>
<string name="use_to">取代範圍,選填書名或者源名</string>
<string name="menu_action_group">分組</string>
<string name="download_path">內容快取路徑</string>
<string name="cleanCache">清理快取</string>
<string name="sys_file_picker">系統檔案選擇器</string>
<string name="new_version">新版本</string>
<string name="download_update">下載更新</string>
<string name="volume_key_page_on_play">朗讀時音量鍵翻頁</string>
<string name="tip_margin_change">Tip邊距跟隨邊距調整</string>
<string name="allow_update">允許更新</string>
<string name="disable_update">禁止更新</string>
<string name="revert_selection">反選</string>
<string name="search_book_key">搜尋書名、作者</string>
<string name="debug_hint">書名、作者、URL</string>
<string name="faq">常見問題</string>
<string name="pt_show_all_find">顯示所有發現</string>
<string name="ps_show_all_find">關閉則只顯示勾選源的發現</string>
<string name="update_toc">更新目錄</string>
<string name="txt_toc_regex">Txt目錄正則</string>
<string name="set_charset">設定編碼</string>
<string name="swap_sort">倒序-順序</string>
<string name="sort">排序</string>
<string name="sort_auto">智慧排序</string>
<string name="sort_manual">手動排序</string>
<string name="sort_pin_yin">拼音排序</string>
<string name="go_to_top">滾動到頂部</string>
<string name="go_to_bottom">滾動到底部</string>
<string name="read_y">已讀: %s</string>
<string name="pursue_more">追更</string>
<string name="fattening">養肥</string>
<string name="finish">完結</string>
<string name="all_book">所有書籍</string>
<string name="pursue_more_book">追更書籍</string>
<string name="fattening_book">養肥書籍</string>
<string name="finish_book">完結書籍</string>
<string name="local_book">本機書籍</string>
<string name="status_bar_immersion">狀態欄顏色透明</string>
<string name="navigation_bar_color_change">導航欄變色</string>
<string name="navigation_bar_color_change_s">導航欄根據夜間模式變化</string>
<string name="add_to_shelf">放入書架</string>
<string name="continue_read">繼續閱讀</string>
<string name="cover_path">封面地址</string>
<string name="page_anim_cover">覆蓋</string>
<string name="page_anim_slide">滑動</string>
<string name="page_anim_simulation">模擬</string>
<string name="page_anim_scroll">滾動</string>
<string name="page_anim_none">無動畫</string>
<string name="donate_s">此書源使用了進階功能,請到捐贈裡點擊支付寶紅包搜尋碼領取紅包開啟。</string>
<string name="up_change_source_last_chapter_t">後台更新換源最新章節</string>
<string name="up_change_source_last_chapter_s">開啟則會在軟體打開1分鐘後開始更新</string>
<string name="behavior_main_t">書架ToolBar自動隱藏</string>
<string name="behavior_main_s">滾動書架時ToolBar自動隱藏與顯示</string>
<string name="login">登入</string>
<string name="login_source">登入%s</string>
<string name="success">成功</string>
<string name="source_no_login">目前源沒有配置登入地址</string>
<!-- source start-->
<string name="source_name">源名稱(sourceName)</string>
<string name="source_url">源URL(sourceUrl)</string>
<string name="source_group">源分組(sourceGroup)</string>
<string name="sort_url">分類Url</string>
<string name="login_url">登入URL(loginUrl)</string>
<string name="r_search_url">搜尋地址(url)</string>
<string name="r_find_url">發現地址規則(url)</string>
<string name="r_book_list">書籍列表規則(bookList)</string>
<string name="r_book_name">書名規則(name)</string>
<string name="r_book_url">詳情頁url規則(bookUrl)</string>
<string name="r_author">作者規則(author)</string>
<string name="rule_book_kind">分類規則(kind)</string>
<string name="rule_book_intro">簡介規則(intro)</string>
<string name="rule_cover_url">封面規則(coverUrl)</string>
<string name="rule_last_chapter">最新章節規則(lastChapter)</string>
<string name="rule_word_count">字數規則(wordCount)</string>
<string name="book_url_pattern">書籍URL正則(bookUrlPattern)</string>
<string name="rule_book_info_init">預處理規則(bookInfoInit)</string>
<string name="rule_toc_url">目錄URL規則(tocUrl)</string>
<string name="rule_next_toc_url">目錄下一頁規則(nextTocUrl)</string>
<string name="rule_chapter_list">目錄列表規則(chapterList)</string>
<string name="rule_chapter_name">章節名稱規則(ChapterName)</string>
<string name="rule_chapter_url">章節URL規則(chapterUrl)</string>
<string name="rule_is_vip">VIP標識(isVip)</string>
<string name="rule_update_time">更新時間(ChapterInfo)</string>
<string name="rule_book_content">正文規則(content)</string>
<string name="rule_next_content">正文下一頁URL規則(nextContentUrl)</string>
<string name="rule_web_js">webJs</string>
<string name="rule_source_regex">資源正則(sourceRegex)</string>
<string name="source_icon">圖示(sourceIcon)</string>
<string name="r_articles">列表規則(ruleArticles)</string>
<string name="r_next">列表下一頁規則(ruleArticles)</string>
<string name="r_title">標題規則(ruleTitle)</string>
<string name="r_guid">guid規則(ruleGuid)</string>
<string name="r_date">時間規則(rulePubDate)</string>
<string name="r_categories">類別規則(ruleCategories)</string>
<string name="r_description">描述規則(ruleDescription)</string>
<string name="r_image">圖片url規則(ruleImage)</string>
<string name="r_content">內容規則(ruleContent)</string>
<string name="r_style">樣式(style)</string>
<string name="r_link">連結規則(ruleLink)</string>
<!-- source end-->
<!--error string start-->
<string name="error_no_source">沒有書源</string>
<string name="error_get_book_info">書籍訊息獲取失敗</string>
<string name="error_get_content">內容獲取失敗</string>
<string name="error_get_chapter_list">目錄獲取失敗</string>
<string name="error_get_web_content">瀏覽網站失敗:%s</string>
<string name="error_read_file">文件讀取失敗</string>
<string name="error_load_toc">載入目錄失敗</string>
<string name="error_get_data">獲取資料失敗!</string>
<string name="error_load_msg">載入失敗\n%s</string>
<string name="net_error_10001">沒有網路</string>
<string name="net_error_10002">網路連接超時</string>
<string name="net_error_10003">資料解析失敗</string>
<!--error string end-->
<string name="source_http_header">請求頭(header)</string>
<string name="debug_source">除錯源</string>
<string name="import_by_qr_code">二維碼匯入</string>
<string name="scan_qr_code">掃描二維碼</string>
<string name="click_on_selected_show_menu">選中時點擊可彈出選單</string>
<string name="theme">主題</string>
<string name="theme_mode">主題模式</string>
<string name="theme_mode_desc">選擇主題模式</string>
<string name="default_theme">預設主題</string>
<string name="restore_default_theme">復原主題為預設配色</string>
<string name="join_qq_group">加入QQ群</string>
<string name="bg_image_per">獲取背景圖片需儲存權限</string>
<string name="input_book_source_url">輸入書源網址</string>
<string name="del_file">刪除文件</string>
<string name="del_file_success">刪除文件成功</string>
<string name="sure_del_file">確定刪除文件嗎?</string>
<string name="files_tree">手機目錄</string>
<string name="intelligent_import">智慧匯入</string>
<string name="find">發現</string>
<string name="switch_display_style">切換顯示樣式</string>
<string name="import_per">匯入本機書籍需儲存權限</string>
<string name="night_theme">夜間模式</string>
<string name="eink_theme">E-Ink 模式</string>
<string name="eink_theme_desc">電子墨水屏模式</string>
<string name="get_storage_per">本軟體需要儲存權限來儲存備份書籍訊息</string>
<string name="double_click_exit">再按一次退出程式</string>
<string name="import_book_per">匯入本機書籍需儲存權限</string>
<string name="network_connection_unavailable">網路連接不可用</string>
<string name="yes"></string>
<string name="no"></string>
<string name="sure">確認</string>
<string name="sure_del">是否確認刪除?</string>
<string name="sure_del_all_book">是否刪除全部書籍?</string>
<string name="sure_del_download_book">是否同時刪除已下載的書籍目錄?</string>
<string name="qr_per">掃描二維碼需相機權限</string>
<string name="aloud_can_not_auto_page">朗讀正在執行,不能自動翻頁</string>
<string name="input_charset">輸入編碼</string>
<string name="text_chapter_list_rule">TXT目錄規則</string>
<string name="open_local_book_per">打開外部書籍需獲取儲存權限</string>
<string name="no_book_name">未獲取到書名</string>
<string name="input_replace_url">輸入取代規則網址</string>
<string name="get_book_list_success">搜尋列表獲取成功%d</string>
<string name="non_null_source_name_url">書源名稱和URL不能為空</string>
<string name="gallery">圖庫</string>
<string name="get_ali_pay_hb">領支付寶紅包</string>
<string name="non_update_url">沒有獲取到更新地址</string>
<string name="check_host_cookie">正在打開首頁,成功自動返回主介面</string>
<string name="click_check_after_success">登入成功後請點擊右上角圖示進行首頁訪問測試</string>
<string name="chapter"></string>
<string name="to"></string>
<string name="use_regex">使用正規表示式</string>
<string name="text_indent">縮排</string>
<string name="indent_0">無縮排</string>
<string name="indent_1">一字元縮排</string>
<string name="indent_2">二字元縮排</string>
<string name="indent_3">三字元縮排</string>
<string name="indent_4">四字元縮排</string>
<string name="select_folder">選擇資料夾</string>
<string name="select_file">選擇文件</string>
<string name="no_find">沒有發現,可以在書源裡添加。</string>
<string name="restore_default">復原預設</string>
<string name="set_download_per">自訂快取路徑需要儲存權限</string>
<string name="black">黑色</string>
<string name="content_empty">文章內容為空</string>
<string name="on_change_source">正在換源請等待…</string>
<string name="chapter_list_empty">目錄列表為空</string>
<string name="text_letter_spacing">字距</string>
<string name="source_tab_base">基本</string>
<string name="source_tab_search">搜尋</string>
<string name="source_tab_find">發現</string>
<string name="source_tab_info">詳情</string>
<string name="source_tab_toc">目錄</string>
<string name="source_tab_content">正文</string>
<string name="e_ink_mode">E-Ink 模式</string>
<string name="e_ink_mode_detail">去除動畫,最佳化電紙書使用體驗</string>
<string name="web_menu">Web服務</string>
<string name="web_port_title">web埠</string>
<string name="web_port_summary">目前埠 %s</string>
<string name="qr_share">二維碼分享</string>
<string name="str_share">字串分享</string>
<string name="wifi_share">wifi分享</string>
<string name="please_grant_storage_permission">請給於儲存權限</string>
<string name="fast_rewind">減速</string>
<string name="fast_forward">加速</string>
<string name="skip_previous">上一個</string>
<string name="skip_next">下一個</string>
<string name="music">音樂</string>
<string name="audio">音訊</string>
<string name="is_enable">啟用</string>
<string name="enable_js">啟用JS</string>
<string name="load_with_base_url">載入BaseUrl</string>
<string name="all_source">全部書源</string>
<string name="cannot_empty">輸入不能為空</string>
<string name="clear_find_cache">清空發現快取</string>
<string name="edit_find">編輯發現</string>
<string name="change_icon_summary">切換軟體顯示在桌面的圖示</string>
<string name="help">幫助</string>
<string name="my">我的</string>
<string name="reading">閱讀</string>
<string name="battery_show">%d%%</string>
<string name="timer_m">%d分鐘</string>
<string name="brightness_auto">自動亮度%s</string>
<string name="read_aloud_by_page">按頁朗讀</string>
<string name="read_aloud_on_line">線上朗讀</string>
<string name="bg_image">背景圖片</string>
<string name="bg_color">背景顏色</string>
<string name="text_color">文字顏色</string>
<string name="select_image">選擇圖片</string>
<string name="group_manage">分組管理</string>
<string name="group_select">分組選擇</string>
<string name="group_edit">編輯分組</string>
<string name="move_to_group">移入分組</string>
<string name="add_group">添加分組</string>
<string name="add_replace_rule">建立取代</string>
<string name="group">分組</string>
<string name="group_s">分組: %s</string>
<string name="toc_s">目錄: %s</string>
<string name="enable_explore">啟用發現</string>
<string name="disable_explore">禁用發現</string>
<string name="enable_selection">啟用所選</string>
<string name="disable_selection">禁用所選</string>
<string name="export_selection">匯出所選</string>
<string name="export">匯出</string>
<string name="load_toc">載入目錄</string>
<string name="tts">TTS</string>
<string name="web_dav_pw">WebDav 密碼</string>
<string name="web_dav_pw_s">輸入你的WebDav授權密碼</string>
<string name="web_dav_url_s">輸入你的伺服器地址</string>
<string name="web_dav_url">WebDav 伺服器地址</string>
<string name="web_dav_account">WebDav 帳號</string>
<string name="web_dav_account_s">輸入你的WebDav帳號</string>
<string name="rss_source">訂閱源</string>
<string name="rss_source_edit">編輯訂閱源</string>
<string name="screen">篩選</string>
<string name="screen_find">篩選發現</string>
<string name="dur_pos">目前位置:</string>
<string name="precision_search">精準搜尋</string>
<string name="service_starting">正在啟動服務</string>
<string name="empty"></string>
<string name="file_chooser">文件選擇</string>
<string name="folder_chooser">資料夾選擇</string>
<string name="bottom_line">我是有底線的</string>
<string name="uri_to_path_fail">Uri轉Path失敗</string>
<string name="refresh_cover">重新整理封面</string>
<string name="change_cover_source">封面換源</string>
<string name="select_local_image">選擇本機圖片</string>
<string name="book_type">類型:</string>
<string name="book_type_text">文字</string>
<string name="book_type_audio">音訊</string>
<string name="to_backstage">後台</string>
<string name="importing">正在匯入</string>
<string name="exporting">正在匯出</string>
<string name="custom_page_key">自訂翻頁按鍵</string>
<string name="prev_page_key">上一頁按鍵</string>
<string name="next_page_key">下一頁按鍵</string>
<string name="after_add_bookshelf">先將書籍加入書架</string>
<string name="no_group">未分組</string>
<string name="prev_sentence">上一句</string>
<string name="next_sentence">下一句</string>
<string name="other_folder">其它目錄</string>
<string name="text_too_long_qr_error">文字太多,生成二維碼失敗</string>
<string name="share_rss_source">分享RSS源</string>
<string name="share_book_source">分享書源</string>
<string name="auto_dark_mode">自動切換夜間模式</string>
<string name="auto_dark_mode_s">夜間模式跟隨系統</string>
<string name="go_back">上級</string>
<string name="tone_colour">線上朗讀音色</string>
<string name="select_count">(%1$d/%2$d)</string>
<string name="show_rss">顯示訂閱</string>
<string name="service_stop">服務已停止</string>
<string name="service_start">正在啟動服務\n具體訊息查看通知欄</string>
<string name="default_path">預設路徑</string>
<string name="sys_folder_picker">系統資料夾選擇器</string>
<string name="app_folder_picker">自帶選擇器\n(Android10以上因權限限制可能無法使用)</string>
<string name="a10_permission_toast">Android10以上因權限限制可能無法讀寫文件</string>
<string name="add_to_text_context_menu_s">長按文字在操作選單中顯示閱讀·搜尋</string>
<string name="add_to_text_context_menu_t">文字操作顯示搜尋</string>
<string name="record_log">記錄日誌</string>
<string name="chinese_converter">中文簡繁體轉換</string>
<string name="chage_icon_error">圖示為向量圖示,Android8.0以前不支援</string>
<string name="aloud_config">朗讀設定</string>
<string name="main_activity">主介面</string>
<string name="selectText">長按選擇文字</string>
<string name="header">頁首</string>
<string name="main_body">正文</string>
<string name="footer">頁尾</string>
<string name="select_end">文字選擇結束位置</string>
<string name="select_start">文字選擇開始位置</string>
<string name="share_layout">共用布局</string>
<string name="browser">瀏覽器</string>
<string name="import_default_rule">匯入預設規則</string>
<string name="name">名稱</string>
<string name="regex">正則</string>
<string name="more_menu">更多選單</string>
<string name="reduce"></string>
<string name="plus"></string>
<string name="system_typeface">系統內建字體樣式</string>
<string name="delete_book_file">刪除來源文件</string>
<string name="default1">預設一</string>
<string name="default2">預設二</string>
<string name="default3">預設三</string>
<string name="title">標題</string>
<string name="title_left">靠左</string>
<string name="title_center">居中</string>
<string name="title_hide">隱藏</string>
<string name="add_to_group">加入分組</string>
<string name="save_image">儲存圖片</string>
<string name="no_default_path">沒有預設路徑</string>
<string name="change_group">設定分組</string>
<string name="view_toc">查看目錄</string>
<string name="bar_elevation">導航欄陰影</string>
<string name="bar_elevation_s">目前陰影大小(elevation): %s</string>
<string name="btn_default_s">預設</string>
<string name="main_menu">主選單</string>
<string name="request_permission">點擊授予權限</string>
<string name="tip_local_perm_request_storage">閱讀需要存取記憶卡權限,請點擊下方的"授予權限"按鈕,或前往“設定”—“應用權限”—打開所需權限。如果授予權限後仍然不正常,請點擊右上角的“選擇資料夾”,使用系統資料夾選擇器。</string>
<string name="alouding_disable">全文朗讀中不能朗讀選中文字</string>
<string name="read_body_to_lh">擴展到瀏海</string>
<string name="toc_updateing">更新目錄中</string>
<string name="media_button_on_exit_title">全程響應耳機按鍵</string>
<string name="media_button_on_exit_summary">即使退出軟體也響應耳機按鍵</string>
<string name="contributors">開發人員</string>
<string name="contact">聯繫我們</string>
<string name="license">開源許可</string>
<string name="other" translatable="false">其它</string>
<string name="official_account" translatable="false">legado-top</string>
<string name="follow_official_account">關注公眾號</string>
<string name="wechat">微信</string>
<string name="thanks">您的支援是我更新的動力</string>
<string name="about_official_account">公眾號[開源閱讀]</string>
<string name="source_auto_changing">正在自動換源</string>
<string name="click_to_apply">點擊加入</string>
<string name="middle"></string>
<string name="information">訊息</string>
<string name="hideHeader">隱藏頁首</string>
<string name="hideFooter">隱藏頁尾</string>
<string name="switchLayout">切換布局</string>
<!--color-->
<string name="primary">主色調</string>
<string name="accent">強調色</string>
<string name="background_color">背景色</string>
<string name="navbar_color">底部操作欄顏色</string>
<string name="day">白天</string>
<string name="day_color_primary">白天,主色調</string>
<string name="day_color_accent">白天,強調色</string>
<string name="day_background_color">白天,背景色</string>
<string name="day_navbar_color">白天,底欄色</string>
<string name="night">夜間</string>
<string name="night_primary">夜間,主色調</string>
<string name="night_accent">夜間,強調色</string>
<string name="night_background_color">夜間,背景色</string>
<string name="night_navbar_color">夜間,底欄色</string>
<string name="auto_change_source">自動換源</string>
<string name="text_full_justify">文字兩端對齊</string>
<string name="auto_page_speed">自動翻頁速度</string>
<string name="sort_by_url">地址排序</string>
<string name="text_font_weight_converter">文章字體轉換</string>
<string name="select_backup_path">請選擇備份路徑</string>
<string name="backup_summary">本地和WebDav壹起備份</string>
<string name="restore_summary">優先從WebDav恢復,失敗時從本地恢復</string>
<string name="import_old_summary">選擇舊版備份文件夾</string>
</resources>

@ -106,4 +106,10 @@
<item>页数及进度</item>
</string-array>
<string-array name="text_font_weight">
<item>正常</item>
<item>粗体</item>
<item>细体</item>
</string-array>
</resources>

@ -13,7 +13,7 @@
<string name="pk_check_update" translatable="false">checkUpdate</string>
<string name="legado_gzh" translatable="false">开源阅读</string>
<string name="source_rule_url" translatable="false">https://gitee.com/alanskycn/yuedu/blob/master/Rule/README.md</string>
<string name="source_rule_url" translatable="false">https://celeter.github.io/</string>
<string name="this_github_url" translatable="false">https://github.com/gedoor/legado</string>
<string name="contributors_url" translatable="false">https://github.com/gedoor/legado/graphs/contributors</string>
<string name="disclaimer_url" translatable="false">https://gedoor.github.io/MyBookshelf/disclaimer.html</string>

@ -11,6 +11,7 @@
<string name="mkdirs">创建子文件夹</string>
<string name="mkdirs_description">创建legado文件夹作为备份文件夹</string>
<string name="backup_path">备份路径</string>
<string name="select_backup_path">请选择备份路径</string>
<string name="menu_import_old_version">导入旧版数据</string>
<string name="menu_import_github">导入Github数据</string>
<string name="menu_replace_rule">净化替换</string>
@ -663,8 +664,8 @@
<string name="contributors">开发人员</string>
<string name="contact">联系我们</string>
<string name="license">开源许可</string>
<string name="other" translatable="false">其它</string>
<string name="official_account" translatable="false">开源阅读</string>
<string name="other">其它</string>
<string name="official_account">开源阅读</string>
<string name="follow_official_account">关注公众号</string>
<string name="wechat">微信</string>
<string name="thanks">您的支持是我更新的动力</string>
@ -676,6 +677,7 @@
<string name="hideHeader">隐藏页眉</string>
<string name="hideFooter">隐藏页脚</string>
<string name="switchLayout">切换布局</string>
<string name="text_font_weight_converter">文章字重切换</string>
<!--color-->
<string name="primary">主色调</string>
@ -693,5 +695,10 @@
<string name="night_background_color">夜间,背景色</string>
<string name="night_navbar_color">夜间,底栏色</string>
<string name="auto_change_source">自动换源</string>
<string name="text_full_justify">文字两端对齐</string>
<string name="auto_page_speed">自动翻页速度</string>
<string name="sort_by_url">地址排序</string>
<string name="backup_summary">本地和WebDav一起备份</string>
<string name="restore_summary">优先从WebDav恢复,失败时从本地恢复</string>
<string name="import_old_summary">选择旧版备份文件夹</string>
</resources>

@ -51,6 +51,12 @@
android:summary="@string/this_github_url"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.Preference
android:key="sourceRuleSummary"
android:title="@string/source_rule_s"
android:summary="@string/source_rule_url"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.Preference
android:key="home_page"
android:title="@string/home_page"

@ -4,64 +4,68 @@
<io.legado.app.ui.widget.prefs.PreferenceCategory
android:title="@string/web_dav_set"
app:layout="@layout/view_preference_category"
app:allowDividerAbove="false"
app:allowDividerBelow="false"
app:iconSpaceReserved="false">
app:iconSpaceReserved="false"
app:layout="@layout/view_preference_category">
<io.legado.app.ui.widget.prefs.EditTextPreference
android:key="web_dav_url"
android:title="@string/web_dav_url"
android:summary="@string/web_dav_url_s"
android:title="@string/web_dav_url"
android:summary="@string/web_dav_url_s"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.EditTextPreference
android:key="web_dav_account"
android:title="@string/web_dav_account"
android:summary="@string/web_dav_account_s"
android:key="web_dav_account"
android:title="@string/web_dav_account"
android:summary="@string/web_dav_account_s"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.EditTextPreference
android:key="web_dav_password"
android:title="@string/web_dav_pw"
android:summary="@string/web_dav_pw_s"
android:key="web_dav_password"
android:title="@string/web_dav_pw"
android:summary="@string/web_dav_pw_s"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.SwitchPreference
android:key="webDavCreateDir"
android:defaultValue="true"
android:title="@string/mkdirs"
android:summary="@string/mkdirs_description"
app:allowDividerAbove="false"
app:allowDividerBelow="false"
app:iconSpaceReserved="false" />
android:key="webDavCreateDir"
android:defaultValue="true"
android:title="@string/mkdirs"
android:summary="@string/mkdirs_description"
app:allowDividerAbove="false"
app:allowDividerBelow="false"
app:iconSpaceReserved="false" />
</io.legado.app.ui.widget.prefs.PreferenceCategory>
<io.legado.app.ui.widget.prefs.PreferenceCategory
app:layout="@layout/view_preference_category"
app:allowDividerBelow="false"
app:iconSpaceReserved="false">
app:iconSpaceReserved="false"
app:layout="@layout/view_preference_category">
<io.legado.app.ui.widget.prefs.Preference
android:key="backupUri"
android:title="@string/backup_path"
app:iconSpaceReserved="false" />
android:key="backupUri"
android:title="@string/backup_path"
android:summary="@string/select_backup_path"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.Preference
android:key="web_dav_backup"
android:title="@string/backup"
app:iconSpaceReserved="false" />
android:key="web_dav_backup"
android:title="@string/backup"
android:summary="@string/backup_summary"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.Preference
android:key="web_dav_restore"
android:title="@string/restore"
app:iconSpaceReserved="false" />
android:key="web_dav_restore"
android:title="@string/restore"
android:summary="@string/restore_summary"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.Preference
android:key="import_old"
android:title="@string/menu_import_old_version"
app:iconSpaceReserved="false" />
android:key="import_old"
android:title="@string/menu_import_old_version"
android:summary="@string/import_old_summary"
app:iconSpaceReserved="false" />
</io.legado.app.ui.widget.prefs.PreferenceCategory>

@ -36,6 +36,12 @@
android:key="hideNavigationBar"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.SwitchPreference
android:defaultValue="true"
android:title="@string/text_full_justify"
android:key="textFullJustify"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.SwitchPreference
android:defaultValue="true"
android:title="@string/volume_key_page"

@ -10,7 +10,7 @@ buildscript {
maven { url 'https://plugins.gradle.org/m2/' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.3'
classpath 'com.android.tools.build:gradle:4.0.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'de.timfreiheit.resourceplaceholders:placeholders:0.3'
classpath 'com.google.gms:google-services:4.3.3'

@ -1,6 +1,6 @@
#Tue Feb 25 08:10:32 CST 2020
#Sat May 30 10:00:31 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip

Loading…
Cancel
Save