diff --git a/app/build.gradle b/app/build.gradle
index 633b8ba1b..7dadb803b 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -136,13 +136,12 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
//room
- def room_version = '2.2.4'
+ def room_version = '2.2.5'
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
- kapt "android.arch.persistence.room:compiler:$room_version"
//paging
- implementation 'androidx.paging:paging-runtime:2.1.1'
+ implementation 'androidx.paging:paging-runtime:2.1.2'
//anko
def anko_version = '0.10.8'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 71fa00c62..a8ae915ff 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -23,6 +23,7 @@
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:supportsRtl="true"
+ android:requestLegacyExternalStorage="true"
android:theme="@style/AppTheme.Light"
tools:ignore="AllowBackup,GoogleAppIndexingWarning,UnusedAttribute">
@@ -78,6 +79,48 @@
android:resource="@xml/shortcuts"
android:launchMode="singleTask" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:launchMode="singleTop" />
【备份与恢复】,选择【导入旧版本数据】。
-* 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。
+* 请关注[开源阅读]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。
+* 弄了个企业公众号[开源阅读](),后面弄好后会把原来的[开源阅读软件]()迁移过来
+
+**2020/03/21**
+* 详情页点击书名搜索
+
+**2020/03/20**
+* 自动备份文件和手动备份文件分开
+* 修复一些rss收藏取消不了的bug
+* 修复rss请求头无效bug
+
+**2020/03/19**
+* 美化界面我的 by yangyxd
+* 优化搜索
+
+**2020/03/18**
+* 尝试修复搜索时崩溃
+* 解决看过书籍的移到顶部需要向上滚动才能看到的bug
+* 只有再书源被删除找不到书源时才会自动换源
+* 美化界面 by yangyxd
+* 订阅后台播放
+
+**2020/03/16**
+* 修复滚动模式切换章节位置不归0的bug
+* 修复文字选择更多菜单在部分手机上报错的bug
+* 修复文字选择菜单问题
+
+**2020/03/15**
+* 加载正文无书源时自动换源
+
+**2020/03/14**
+* 修改导航栏图标
+
+**2020/03/13**
+* 更改书架控件,ViewPager2替换回2.0使用的ViewPager,解决下拉不流畅问题
+* 修复点击作者搜索后,打开的详情页还是原来的书籍的bug
+* 修改朗读菜单
+* 优化rss朗读
+
+**2020/03/12**
+* 导入本地添加需要权限模式
**2020/03/11**
* 修复调节上边距时下边距一起动的bug
* 适配沚水的web阅读 by 六月
* 分组管理页面调整 by yangyxd
-* 导入本地添加需要权限模式
**2020/03/10**
* 优化文字选择菜单弹出位置
diff --git a/app/src/main/java/io/legado/app/constant/EventBus.kt b/app/src/main/java/io/legado/app/constant/EventBus.kt
index 88031b4fc..e24ba8000 100644
--- a/app/src/main/java/io/legado/app/constant/EventBus.kt
+++ b/app/src/main/java/io/legado/app/constant/EventBus.kt
@@ -11,7 +11,6 @@ object EventBus {
const val TIME_CHANGED = "timeChanged"
const val UP_CONFIG = "upConfig"
const val OPEN_CHAPTER = "openChapter"
- const val REPLACE = "replace"
const val AUDIO_SUB_TITLE = "audioSubTitle"
const val AUDIO_STATE = "audioState"
const val AUDIO_PROGRESS = "audioProgress"
diff --git a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
index 9ce5c65be..55d5ada4c 100644
--- a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
+++ b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
@@ -50,9 +50,12 @@ interface BookSourceDao {
@get:Query("select * from book_sources order by customOrder asc")
val all: List
- @get:Query("select * from book_sources where enabled = 1 order by customOrder asc")
+ @get:Query("select * from book_sources where enabled = 1 order by customOrder")
val allEnabled: List
+ @get:Query("select * from book_sources where enabled = 1 and bookSourceType = 0 order by customOrder")
+ val allTextEnabled: List
+
@Query("select * from book_sources where bookSourceUrl = :key")
fun getBookSource(key: String): BookSource?
diff --git a/app/src/main/java/io/legado/app/data/entities/Book.kt b/app/src/main/java/io/legado/app/data/entities/Book.kt
index 833561f1b..0dd5cab9e 100644
--- a/app/src/main/java/io/legado/app/data/entities/Book.kt
+++ b/app/src/main/java/io/legado/app/data/entities/Book.kt
@@ -31,7 +31,7 @@ data class Book(
var intro: String? = null, // 简介内容(书源获取)
var customIntro: String? = null, // 简介内容(用户修改)
var charset: String? = null, // 自定义字符集名称(仅适用于本地书籍)
- var type: Int = 0, // @BookType
+ var type: Int = 0, // 0:text 1:audio
var group: Int = 0, // 自定义分组索引号
var latestChapterTitle: String? = null, // 最新章节标题
var latestChapterTime: Long = System.currentTimeMillis(), // 最新章节标题更新时间
diff --git a/app/src/main/java/io/legado/app/data/entities/RssStar.kt b/app/src/main/java/io/legado/app/data/entities/RssStar.kt
index 5199b586d..1d0c266f8 100644
--- a/app/src/main/java/io/legado/app/data/entities/RssStar.kt
+++ b/app/src/main/java/io/legado/app/data/entities/RssStar.kt
@@ -16,4 +16,16 @@ data class RssStar(
var description: String? = null,
var content: String? = null,
var image: String? = null
-)
\ No newline at end of file
+) {
+ fun toRssArticle(): RssArticle {
+ return RssArticle(
+ origin = origin,
+ title = title,
+ link = link,
+ pubDate = pubDate,
+ description = description,
+ content = content,
+ image = image
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/help/LauncherIconHelp.kt b/app/src/main/java/io/legado/app/help/LauncherIconHelp.kt
index 76bcd6116..b859336e4 100644
--- a/app/src/main/java/io/legado/app/help/LauncherIconHelp.kt
+++ b/app/src/main/java/io/legado/app/help/LauncherIconHelp.kt
@@ -5,10 +5,7 @@ import android.content.pm.PackageManager
import android.os.Build
import io.legado.app.App
import io.legado.app.R
-import io.legado.app.ui.welcome.Launcher1
-import io.legado.app.ui.welcome.Launcher2
-import io.legado.app.ui.welcome.Launcher3
-import io.legado.app.ui.welcome.WelcomeActivity
+import io.legado.app.ui.welcome.*
import org.jetbrains.anko.toast
/**
@@ -20,7 +17,10 @@ object LauncherIconHelp {
private val componentNames = arrayListOf(
ComponentName(App.INSTANCE, Launcher1::class.java.name),
ComponentName(App.INSTANCE, Launcher2::class.java.name),
- ComponentName(App.INSTANCE, Launcher3::class.java.name)
+ ComponentName(App.INSTANCE, Launcher3::class.java.name),
+ ComponentName(App.INSTANCE, Launcher4::class.java.name),
+ ComponentName(App.INSTANCE, Launcher5::class.java.name),
+ ComponentName(App.INSTANCE, Launcher6::class.java.name)
)
fun changeIcon(icon: String?) {
diff --git a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt
index e9fd0df86..0811eee10 100644
--- a/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt
+++ b/app/src/main/java/io/legado/app/help/coroutine/Coroutine.kt
@@ -6,7 +6,7 @@ import kotlin.coroutines.CoroutineContext
class Coroutine(
- scope: CoroutineScope,
+ val scope: CoroutineScope,
context: CoroutineContext = Dispatchers.IO,
block: suspend CoroutineScope.() -> T
) {
@@ -28,9 +28,10 @@ class Coroutine(
private val job: Job
private var start: VoidCallback? = null
- private var success: Callback? = null
+ private var success: Callback? = null
private var error: Callback? = null
private var finally: VoidCallback? = null
+ private var cancel: VoidCallback? = null
private var timeMillis: Long? = null
private var errorReturn: Result? = null
@@ -45,7 +46,7 @@ class Coroutine(
get() = job.isCompleted
init {
- this.job = executeInternal(scope, context, block)
+ this.job = executeInternal(context, block)
}
fun timeout(timeMillis: () -> Long): Coroutine {
@@ -78,7 +79,7 @@ class Coroutine(
fun onSuccess(
context: CoroutineContext? = null,
- block: suspend CoroutineScope.(T?) -> Unit
+ block: suspend CoroutineScope.(T) -> Unit
): Coroutine {
this.success = Callback(context, block)
return this@Coroutine
@@ -100,9 +101,28 @@ class Coroutine(
return this@Coroutine
}
+ fun onCancel(
+ context: CoroutineContext? = null,
+ block: suspend CoroutineScope.() -> Unit
+ ): Coroutine {
+ this.cancel = VoidCallback(context, block)
+ return this@Coroutine
+ }
+
//取消当前任务
fun cancel(cause: CancellationException? = null) {
job.cancel(cause)
+ cancel?.let {
+ MainScope().launch {
+ if (null == it.context) {
+ it.block.invoke(scope)
+ } else {
+ withContext(scope.coroutineContext.plus(it.context)) {
+ it.block.invoke(this)
+ }
+ }
+ }
+ }
}
fun invokeOnCompletion(handler: CompletionHandler): DisposableHandle {
@@ -110,7 +130,6 @@ class Coroutine(
}
private fun executeInternal(
- scope: CoroutineScope,
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): Job {
@@ -118,21 +137,27 @@ class Coroutine(
try {
start?.let { dispatchVoidCallback(this, it) }
val value = executeBlock(scope, context, timeMillis ?: 0L, block)
- success?.let { dispatchCallback(this, value, it) }
+ if (isActive) {
+ success?.let { dispatchCallback(this, value, it) }
+ }
} catch (e: Throwable) {
if (BuildConfig.DEBUG) {
e.printStackTrace()
}
val consume: Boolean = errorReturn?.value?.let { value ->
- success?.let { dispatchCallback(this, value, it) }
+ if (isActive) {
+ success?.let { dispatchCallback(this, value, it) }
+ }
true
} ?: false
- if (!consume) {
+ if (!consume && isActive) {
error?.let { dispatchCallback(this, e, it) }
}
} finally {
- finally?.let { dispatchVoidCallback(this, it) }
+ if (isActive) {
+ finally?.let { dispatchVoidCallback(this, it) }
+ }
}
}
}
@@ -152,6 +177,7 @@ class Coroutine(
value: R,
callback: Callback
) {
+ if (!scope.isActive) return
if (null == callback.context) {
callback.block.invoke(scope, value)
} else {
@@ -166,7 +192,7 @@ class Coroutine(
context: CoroutineContext,
timeMillis: Long,
noinline block: suspend CoroutineScope.() -> T
- ): T? {
+ ): T {
return withContext(scope.coroutineContext.plus(context)) {
if (timeMillis > 0L) withTimeout(timeMillis) {
block()
diff --git a/app/src/main/java/io/legado/app/help/permission/PermissionsCompat.kt b/app/src/main/java/io/legado/app/help/permission/PermissionsCompat.kt
index a5ccdb266..4bbed5991 100644
--- a/app/src/main/java/io/legado/app/help/permission/PermissionsCompat.kt
+++ b/app/src/main/java/io/legado/app/help/permission/PermissionsCompat.kt
@@ -1,8 +1,11 @@
package io.legado.app.help.permission
+import android.os.Build
import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
+import org.jetbrains.anko.startActivity
+import java.util.ArrayList
class PermissionsCompat private constructor() {
@@ -12,6 +15,17 @@ class PermissionsCompat private constructor() {
RequestManager.pushRequest(request)
}
+ companion object {
+ // 检查权限, 如果已经拥有返回 true
+ fun check(activity: AppCompatActivity, vararg permissions: String): Boolean {
+ var request: Request = Request(activity)
+ var pers = ArrayList()
+ pers.addAll(listOf(*permissions))
+ var data = request.getDeniedPermissions(pers.toTypedArray())
+ return data == null;
+ }
+ }
+
class Builder {
private val request: Request
diff --git a/app/src/main/java/io/legado/app/help/permission/Request.kt b/app/src/main/java/io/legado/app/help/permission/Request.kt
index 600cbe93c..f7ed0293a 100644
--- a/app/src/main/java/io/legado/app/help/permission/Request.kt
+++ b/app/src/main/java/io/legado/app/help/permission/Request.kt
@@ -102,7 +102,7 @@ internal class Request : OnRequestPermissionsResultCallback {
deniedCallback = null
}
- private fun getDeniedPermissions(permissions: Array?): Array? {
+ fun getDeniedPermissions(permissions: Array?): Array? {
if (permissions != null) {
val deniedPermissionList = ArrayList()
for (permission in permissions) {
diff --git a/app/src/main/java/io/legado/app/help/storage/Backup.kt b/app/src/main/java/io/legado/app/help/storage/Backup.kt
index 8c4fa8fc2..60d86d6a3 100644
--- a/app/src/main/java/io/legado/app/help/storage/Backup.kt
+++ b/app/src/main/java/io/legado/app/help/storage/Backup.kt
@@ -21,14 +21,6 @@ object Backup {
FileUtils.getDirFile(App.INSTANCE.filesDir, "backup").absolutePath
}
- val legadoPath by lazy {
- FileUtils.getSdCardPath() + File.separator + "YueDu3.0"
- }
-
- val exportPath by lazy {
- legadoPath + File.separator + "Export"
- }
-
val backupFileNames by lazy {
arrayOf(
"bookshelf.json", "bookGroup.json", "bookSource.json", "rssSource.json",
@@ -42,47 +34,46 @@ object Backup {
return
}
Coroutine.async {
- val backupPath = context.getPrefString(PreferKey.backupPath)
- if (backupPath.isNullOrEmpty()) {
- backup(context)
- } else {
- backup(context, backupPath)
+ context.getPrefString(PreferKey.backupPath)?.let {
+ backup(context, it, true)
}
}
}
- suspend fun backup(context: Context, path: String = legadoPath) {
+ suspend fun backup(context: Context, path: String, isAuto: Boolean = false) {
context.putPrefLong(PreferKey.lastBackup, System.currentTimeMillis())
withContext(IO) {
- writeListToJson(App.db.bookDao().all, "bookshelf.json", backupPath)
- writeListToJson(App.db.bookGroupDao().all, "bookGroup.json", backupPath)
- writeListToJson(App.db.bookSourceDao().all, "bookSource.json", backupPath)
- writeListToJson(App.db.rssSourceDao().all, "rssSource.json", backupPath)
- writeListToJson(App.db.rssStarDao().all, "rssStar.json", backupPath)
- writeListToJson(App.db.replaceRuleDao().all, "replaceRule.json", backupPath)
- GSON.toJson(ReadBookConfig.configList)?.let {
- FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.readConfigFileName)
- .writeText(it)
- }
- Preferences.getSharedPreferences(App.INSTANCE, backupPath, "config")?.let { sp ->
- val edit = sp.edit()
- App.INSTANCE.defaultSharedPreferences.all.map {
- when (val value = it.value) {
- is Int -> edit.putInt(it.key, value)
- is Boolean -> edit.putBoolean(it.key, value)
- is Long -> edit.putLong(it.key, value)
- is Float -> edit.putFloat(it.key, value)
- is String -> edit.putString(it.key, value)
- else -> Unit
+ synchronized(this@Backup) {
+ writeListToJson(App.db.bookDao().all, "bookshelf.json", backupPath)
+ writeListToJson(App.db.bookGroupDao().all, "bookGroup.json", backupPath)
+ writeListToJson(App.db.bookSourceDao().all, "bookSource.json", backupPath)
+ writeListToJson(App.db.rssSourceDao().all, "rssSource.json", backupPath)
+ writeListToJson(App.db.rssStarDao().all, "rssStar.json", backupPath)
+ writeListToJson(App.db.replaceRuleDao().all, "replaceRule.json", backupPath)
+ GSON.toJson(ReadBookConfig.configList)?.let {
+ FileUtils.createFileIfNotExist(backupPath + File.separator + ReadBookConfig.readConfigFileName)
+ .writeText(it)
+ }
+ Preferences.getSharedPreferences(App.INSTANCE, backupPath, "config")?.let { sp ->
+ val edit = sp.edit()
+ App.INSTANCE.defaultSharedPreferences.all.map {
+ when (val value = it.value) {
+ is Int -> edit.putInt(it.key, value)
+ is Boolean -> edit.putBoolean(it.key, value)
+ is Long -> edit.putLong(it.key, value)
+ is Float -> edit.putFloat(it.key, value)
+ is String -> edit.putString(it.key, value)
+ else -> Unit
+ }
}
+ edit.commit()
+ }
+ WebDavHelp.backUpWebDav(backupPath)
+ if (path.isContentPath()) {
+ copyBackup(context, Uri.parse(path), isAuto)
+ } else {
+ copyBackup(File(path), isAuto)
}
- edit.commit()
- }
- WebDavHelp.backUpWebDav(backupPath)
- if (path.isContentPath()) {
- copyBackup(context, Uri.parse(path))
- } else {
- copyBackup(File(path))
}
}
}
@@ -95,27 +86,39 @@ object Backup {
}
@Throws(java.lang.Exception::class)
- private fun copyBackup(context: Context, uri: Uri) {
+ private fun copyBackup(context: Context, uri: Uri, isAuto: Boolean) {
DocumentFile.fromTreeUri(context, uri)?.let { treeDoc ->
for (fileName in backupFileNames) {
val file = File(backupPath + File.separator + fileName)
if (file.exists()) {
- treeDoc.findFile(fileName)?.delete()
- treeDoc.createFile("", fileName)
- ?.writeBytes(context, file.readBytes())
+ if (isAuto) {
+ treeDoc.findFile("auto")?.findFile(fileName)?.delete()
+ DocumentUtils.createFileIfNotExist(
+ treeDoc,
+ fileName,
+ subDirs = *arrayOf("auto")
+ )?.writeBytes(context, file.readBytes())
+ } else {
+ treeDoc.findFile(fileName)?.delete()
+ treeDoc.createFile("", fileName)
+ ?.writeBytes(context, file.readBytes())
+ }
}
}
}
}
@Throws(java.lang.Exception::class)
- private fun copyBackup(rootFile: File) {
+ private fun copyBackup(rootFile: File, isAuto: Boolean) {
for (fileName in backupFileNames) {
val file = File(backupPath + File.separator + fileName)
if (file.exists()) {
file.copyTo(
- FileUtils.createFileIfNotExist(rootFile, fileName),
- true
+ if (isAuto) {
+ FileUtils.createFileIfNotExist(rootFile, fileName, "auto")
+ } else {
+ FileUtils.createFileIfNotExist(rootFile, fileName)
+ }, true
)
}
}
diff --git a/app/src/main/java/io/legado/app/help/storage/OldBook.kt b/app/src/main/java/io/legado/app/help/storage/OldBook.kt
index f8321973c..a782cf5f5 100644
--- a/app/src/main/java/io/legado/app/help/storage/OldBook.kt
+++ b/app/src/main/java/io/legado/app/help/storage/OldBook.kt
@@ -40,7 +40,6 @@ object OldBook {
book.durChapterTitle = jsonItem.readString("$.durChapterName")
book.durChapterPos = jsonItem.readInt("$.durChapterPage") ?: 0
book.durChapterTime = jsonItem.readLong("$.finalDate") ?: 0
- book.group = jsonItem.readInt("$.group") ?: 0
book.intro = jsonItem.readString("$.bookInfoBean.introduce")
book.latestChapterTitle = jsonItem.readString("$.lastChapterName")
book.lastCheckCount = jsonItem.readInt("$.newChapters") ?: 0
diff --git a/app/src/main/java/io/legado/app/help/storage/Restore.kt b/app/src/main/java/io/legado/app/help/storage/Restore.kt
index 5393249c3..f174a6f5e 100644
--- a/app/src/main/java/io/legado/app/help/storage/Restore.kt
+++ b/app/src/main/java/io/legado/app/help/storage/Restore.kt
@@ -128,7 +128,7 @@ object Restore {
bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2)
}
ChapterProvider.upStyle()
- ReadBook.loadContent()
+ ReadBook.loadContent(resetPageOffset = false)
}
withContext(Main) {
if (AppConfig.isNightTheme && AppCompatDelegate.getDefaultNightMode() != AppCompatDelegate.MODE_NIGHT_YES) {
diff --git a/app/src/main/java/io/legado/app/lib/theme/ATH.kt b/app/src/main/java/io/legado/app/lib/theme/ATH.kt
index 0bcbb114b..19304862c 100644
--- a/app/src/main/java/io/legado/app/lib/theme/ATH.kt
+++ b/app/src/main/java/io/legado/app/lib/theme/ATH.kt
@@ -199,13 +199,12 @@ object ATH {
fun applyBottomNavigationColor(bottomBar: BottomNavigationView?) {
bottomBar?.apply {
- setBackgroundColor(ThemeStore.backgroundColor(context))
+ setBackgroundColor(ThemeStore.bottomBackground(context))
val colorStateList = Selector.colorBuild()
.setDefaultColor(context.getCompatColor(R.color.btn_bg_press_tp))
.setSelectedColor(ThemeStore.accentColor(bottom_navigation_view.context)).create()
itemIconTintList = colorStateList
itemTextColor = colorStateList
- setBackgroundColor(ThemeStore.bottomBackground(context))
}
}
diff --git a/app/src/main/java/io/legado/app/model/Debug.kt b/app/src/main/java/io/legado/app/model/Debug.kt
index 7d7ec3627..51ae12afd 100644
--- a/app/src/main/java/io/legado/app/model/Debug.kt
+++ b/app/src/main/java/io/legado/app/model/Debug.kt
@@ -56,9 +56,7 @@ object Debug {
log(debugSource, "︾开始解析")
Rss.getArticles(rssSource, null)
.onSuccess {
- if (it == null) {
- log(debugSource, "︽解析失败", state = -1)
- } else if(it.articles.isEmpty()) {
+ if (it.articles.isEmpty()) {
log(debugSource, "⇒列表页解析成功,为空")
log(debugSource, "︽解析完成", state = 1000)
} else {
@@ -69,7 +67,7 @@ object Debug {
if (ruleContent.isNullOrEmpty()) {
log(debugSource, "⇒内容规则为空,默认获取整个网页", state = 1000)
} else {
- rssContentDebug(it.articles[0], ruleContent)
+ rssContentDebug(it.articles[0], ruleContent, rssSource)
}
} else {
log(debugSource, "⇒存在描述规则,不解析内容页")
@@ -82,9 +80,9 @@ object Debug {
}
}
- private fun rssContentDebug(rssArticle: RssArticle, ruleContent: String) {
+ private fun rssContentDebug(rssArticle: RssArticle, ruleContent: String, rssSource: RssSource) {
log(debugSource, "︾开始解析内容页")
- Rss.getContent(rssArticle, ruleContent)
+ Rss.getContent(rssArticle, ruleContent, rssSource)
.onSuccess {
log(debugSource, it)
log(debugSource, "︽内容页解析完成", state = 1000)
@@ -140,14 +138,12 @@ object Debug {
log(debugSource, "︾开始解析发现页")
val explore = webBook.exploreBook(url, 1)
.onSuccess { exploreBooks ->
- exploreBooks?.let {
- if (exploreBooks.isNotEmpty()) {
- log(debugSource, "︽发现页解析完成")
- log(debugSource, showTime = false)
- infoDebug(webBook, exploreBooks[0].toBook())
- } else {
- log(debugSource, "︽未获取到书籍", state = -1)
- }
+ if (exploreBooks.isNotEmpty()) {
+ log(debugSource, "︽发现页解析完成")
+ log(debugSource, showTime = false)
+ infoDebug(webBook, exploreBooks[0].toBook())
+ } else {
+ log(debugSource, "︽未获取到书籍", state = -1)
}
}
.onError {
@@ -164,14 +160,12 @@ object Debug {
log(debugSource, "︾开始解析搜索页")
val search = webBook.searchBook(key, 1)
.onSuccess { searchBooks ->
- searchBooks?.let {
- if (searchBooks.isNotEmpty()) {
- log(debugSource, "︽搜索页解析完成")
- log(debugSource, showTime = false)
- infoDebug(webBook, searchBooks[0].toBook())
- } else {
- log(debugSource, "︽未获取到书籍", state = -1)
- }
+ if (searchBooks.isNotEmpty()) {
+ log(debugSource, "︽搜索页解析完成")
+ log(debugSource, showTime = false)
+ infoDebug(webBook, searchBooks[0].toBook())
+ } else {
+ log(debugSource, "︽未获取到书籍", state = -1)
}
}
.onError {
@@ -197,16 +191,14 @@ object Debug {
private fun tocDebug(webBook: WebBook, book: Book) {
log(debugSource, "︾开始解析目录页")
val chapterList = webBook.getChapterList(book)
- .onSuccess { chapterList ->
- chapterList?.let {
- if (it.isNotEmpty()) {
- log(debugSource, "︽目录页解析完成")
- log(debugSource, showTime = false)
- val nextChapterUrl = if (it.size > 1) it[1].url else null
- contentDebug(webBook, book, it[0], nextChapterUrl)
- } else {
- log(debugSource, "︽目录列表为空", state = -1)
- }
+ .onSuccess {
+ if (it.isNotEmpty()) {
+ log(debugSource, "︽目录页解析完成")
+ log(debugSource, showTime = false)
+ val nextChapterUrl = if (it.size > 1) it[1].url else null
+ contentDebug(webBook, book, it[0], nextChapterUrl)
+ } else {
+ log(debugSource, "︽目录列表为空", state = -1)
}
}
.onError {
diff --git a/app/src/main/java/io/legado/app/model/Rss.kt b/app/src/main/java/io/legado/app/model/Rss.kt
index b3622b40b..24dadade0 100644
--- a/app/src/main/java/io/legado/app/model/Rss.kt
+++ b/app/src/main/java/io/legado/app/model/Rss.kt
@@ -21,7 +21,10 @@ object Rss {
context: CoroutineContext = Dispatchers.IO
): Coroutine {
return Coroutine.async(scope, context) {
- val analyzeUrl = AnalyzeUrl(pageUrl ?: rssSource.sourceUrl)
+ val analyzeUrl = AnalyzeUrl(
+ pageUrl ?: rssSource.sourceUrl,
+ headerMapF = rssSource.getHeaderMap()
+ )
val body = analyzeUrl.getResponseAwait(rssSource.sourceUrl).body
RssParserByRule.parseXML(body, rssSource)
}
@@ -30,12 +33,15 @@ object Rss {
fun getContent(
rssArticle: RssArticle,
ruleContent: String,
+ rssSource: RssSource?,
scope: CoroutineScope = Coroutine.DEFAULT,
context: CoroutineContext = Dispatchers.IO
): Coroutine {
return Coroutine.async(scope, context) {
- val body = AnalyzeUrl(rssArticle.link, baseUrl = rssArticle.origin)
- .getResponseAwait(rssArticle.origin)
+ val body = AnalyzeUrl(
+ rssArticle.link, baseUrl = rssArticle.origin,
+ headerMapF = rssSource?.getHeaderMap()
+ ).getResponseAwait(rssArticle.origin)
.body
val analyzeRule = AnalyzeRule()
analyzeRule.setContent(
diff --git a/app/src/main/java/io/legado/app/model/SearchBookModel.kt b/app/src/main/java/io/legado/app/model/SearchBookModel.kt
new file mode 100644
index 000000000..1f6cac065
--- /dev/null
+++ b/app/src/main/java/io/legado/app/model/SearchBookModel.kt
@@ -0,0 +1,93 @@
+package io.legado.app.model
+
+import io.legado.app.App
+import io.legado.app.data.entities.SearchBook
+import io.legado.app.help.AppConfig
+import io.legado.app.help.coroutine.Coroutine
+import io.legado.app.utils.getPrefString
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers.IO
+import kotlinx.coroutines.asCoroutineDispatcher
+import java.util.concurrent.Executors
+
+class SearchBookModel(private val scope: CoroutineScope, private val callBack: CallBack) {
+ private var searchPool =
+ Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher()
+ private var mSearchId = System.currentTimeMillis()
+ private var searchPage = 1
+ private var searchKey: String = ""
+ private var task: Coroutine<*>? = null
+
+ private fun initSearchPool() {
+ searchPool =
+ Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher()
+ }
+
+ fun search(searchId: Long, key: String) {
+ if (searchId != mSearchId) {
+ task?.cancel()
+ searchPool.close()
+ initSearchPool()
+ mSearchId = searchId
+ searchPage = 1
+ if (key.isEmpty()) {
+ return
+ } else {
+ this.searchKey = key
+ }
+ } else {
+ searchPage++
+ }
+ task = Coroutine.async(scope, searchPool) {
+ val searchGroup = App.INSTANCE.getPrefString("searchGroup") ?: ""
+ val bookSourceList = if (searchGroup.isBlank()) {
+ App.db.bookSourceDao().allEnabled
+ } else {
+ App.db.bookSourceDao().getEnabledByGroup(searchGroup)
+ }
+ for (item in bookSourceList) {
+ //task取消时自动取消 by (scope = this@execute)
+ WebBook(item).searchBook(
+ searchKey,
+ searchPage,
+ scope = this,
+ context = searchPool
+ )
+ .timeout(30000L)
+ .onSuccess(IO) {
+ if (searchId == mSearchId) {
+ callBack.onSearchSuccess(it)
+ }
+ }
+ }
+ }.onStart {
+ callBack.onSearchStart()
+ }
+
+ task?.invokeOnCompletion {
+ if (searchId == mSearchId) {
+ callBack.onSearchFinish()
+ }
+ }
+ }
+
+ fun cancelSearch() {
+ task?.cancel()
+ mSearchId = 0
+ callBack.onSearchCancel()
+ }
+
+ fun close() {
+ task?.cancel()
+ mSearchId = 0
+ searchPool.close()
+ }
+
+ interface CallBack {
+ fun onSearchStart()
+ fun onSearchSuccess(searchBooks: ArrayList)
+ fun onSearchFinish()
+ fun onSearchCancel()
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/model/WebBook.kt b/app/src/main/java/io/legado/app/model/WebBook.kt
index 69dfb943c..cb1742132 100644
--- a/app/src/main/java/io/legado/app/model/WebBook.kt
+++ b/app/src/main/java/io/legado/app/model/WebBook.kt
@@ -27,26 +27,36 @@ class WebBook(val bookSource: BookSource) {
page: Int? = 1,
scope: CoroutineScope = Coroutine.DEFAULT,
context: CoroutineContext = Dispatchers.IO
- ): Coroutine> {
+ ): Coroutine> {
return Coroutine.async(scope, context) {
- bookSource.searchUrl?.let { searchUrl ->
- val analyzeUrl = AnalyzeUrl(
- ruleUrl = searchUrl,
- key = key,
- page = page,
- baseUrl = sourceUrl,
- headerMapF = bookSource.getHeaderMap()
- )
- val res = analyzeUrl.getResponseAwait(bookSource.bookSourceUrl)
- BookList.analyzeBookList(
- res.body,
- bookSource,
- analyzeUrl,
- res.url,
- true
- )
- } ?: arrayListOf()
+ searchBookSuspend(scope, key, page)
+ }
+ }
+
+ suspend fun searchBookSuspend(
+ scope: CoroutineScope,
+ key: String,
+ page: Int? = 1
+ ): ArrayList {
+ bookSource.searchUrl?.let { searchUrl ->
+ val analyzeUrl = AnalyzeUrl(
+ ruleUrl = searchUrl,
+ key = key,
+ page = page,
+ baseUrl = sourceUrl,
+ headerMapF = bookSource.getHeaderMap()
+ )
+ val res = analyzeUrl.getResponseAwait(bookSource.bookSourceUrl)
+ return BookList.analyzeBookList(
+ scope,
+ res.body,
+ bookSource,
+ analyzeUrl,
+ res.url,
+ true
+ )
}
+ return arrayListOf()
}
/**
@@ -67,6 +77,7 @@ class WebBook(val bookSource: BookSource) {
)
val res = analyzeUrl.getResponseAwait(bookSource.bookSourceUrl)
BookList.analyzeBookList(
+ scope,
res.body,
bookSource,
analyzeUrl,
diff --git a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt
index 568615aa1..873b6b3a9 100644
--- a/app/src/main/java/io/legado/app/model/webBook/BookContent.kt
+++ b/app/src/main/java/io/legado/app/model/webBook/BookContent.kt
@@ -2,6 +2,7 @@ package io.legado.app.model.webBook
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.data.entities.BookSource
@@ -39,7 +40,12 @@ object BookContent {
var contentData = analyzeContent(
book, baseUrl, body, contentRule, bookChapter, bookSource
)
- content.append(contentData.content.replace(bookChapter.title, "")).append("\n")
+ if (bookSource.bookSourceType == BookType.default) {
+ content.append(contentData.content.replace(bookChapter.title, "")).append("\n")
+ } else {
+ content.append(contentData.content).append("\n")
+ }
+
if (contentData.nextUrl.size == 1) {
var nextUrl = contentData.nextUrl[0]
val nextChapterUrl = if (!nextChapterUrlF.isNullOrEmpty())
diff --git a/app/src/main/java/io/legado/app/model/webBook/BookList.kt b/app/src/main/java/io/legado/app/model/webBook/BookList.kt
index a205c950a..372a4eec6 100644
--- a/app/src/main/java/io/legado/app/model/webBook/BookList.kt
+++ b/app/src/main/java/io/legado/app/model/webBook/BookList.kt
@@ -9,11 +9,15 @@ import io.legado.app.model.Debug
import io.legado.app.model.analyzeRule.AnalyzeRule
import io.legado.app.model.analyzeRule.AnalyzeUrl
import io.legado.app.utils.NetworkUtils
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.isActive
object BookList {
@Throws(Exception::class)
fun analyzeBookList(
+ scope: CoroutineScope,
body: String?,
bookSource: BookSource,
analyzeUrl: AnalyzeUrl,
@@ -28,12 +32,13 @@ object BookList {
)
)
Debug.log(bookSource.bookSourceUrl, "≡获取成功:${analyzeUrl.ruleUrl}")
+ if (!scope.isActive) throw CancellationException()
val analyzeRule = AnalyzeRule(null)
analyzeRule.setContent(body, baseUrl)
bookSource.bookUrlPattern?.let {
if (baseUrl.matches(it.toRegex())) {
Debug.log(bookSource.bookSourceUrl, "≡链接为详情页")
- getInfoItem(analyzeRule, bookSource, baseUrl)?.let { searchBook ->
+ getInfoItem(scope, analyzeRule, bookSource, baseUrl)?.let { searchBook ->
searchBook.infoHtml = body
bookList.add(searchBook)
}
@@ -59,7 +64,7 @@ object BookList {
collections = analyzeRule.getElements(ruleList)
if (collections.isEmpty() && bookSource.bookUrlPattern.isNullOrEmpty()) {
Debug.log(bookSource.bookSourceUrl, "└列表为空,按详情页解析")
- getInfoItem(analyzeRule, bookSource, baseUrl)?.let { searchBook ->
+ getInfoItem(scope, analyzeRule, bookSource, baseUrl)?.let { searchBook ->
searchBook.infoHtml = body
bookList.add(searchBook)
}
@@ -74,8 +79,9 @@ object BookList {
val ruleWordCount = analyzeRule.splitSourceRule(bookListRule.wordCount)
Debug.log(bookSource.bookSourceUrl, "└列表大小:${collections.size}")
for ((index, item) in collections.withIndex()) {
+ if (!scope.isActive) throw CancellationException()
getSearchItem(
- item, analyzeRule, bookSource, baseUrl, index == 0,
+ scope, item, analyzeRule, bookSource, baseUrl, index == 0,
ruleName = ruleName, ruleBookUrl = ruleBookUrl, ruleAuthor = ruleAuthor,
ruleCoverUrl = ruleCoverUrl, ruleIntro = ruleIntro, ruleKind = ruleKind,
ruleLastChapter = ruleLastChapter, ruleWordCount = ruleWordCount
@@ -93,7 +99,9 @@ object BookList {
return bookList
}
+ @Throws(Exception::class)
private fun getInfoItem(
+ scope: CoroutineScope,
analyzeRule: AnalyzeRule,
bookSource: BookSource,
baseUrl: String
@@ -108,29 +116,37 @@ object BookList {
with(bookSource.getBookInfoRule()) {
init?.let {
if (it.isNotEmpty()) {
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "≡执行详情页初始化规则")
analyzeRule.setContent(analyzeRule.getElement(it))
}
}
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取书名")
searchBook.name = analyzeRule.getString(name)
Debug.log(bookSource.bookSourceUrl, "└${searchBook.name}")
if (searchBook.name.isNotEmpty()) {
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取作者")
searchBook.author = BookHelp.formatAuthor(analyzeRule.getString(author))
Debug.log(bookSource.bookSourceUrl, "└${searchBook.author}")
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取分类")
searchBook.kind = analyzeRule.getStringList(kind)?.joinToString(",")
Debug.log(bookSource.bookSourceUrl, "└${searchBook.kind}")
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取字数")
searchBook.wordCount = analyzeRule.getString(wordCount)
Debug.log(bookSource.bookSourceUrl, "└${searchBook.wordCount}")
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取最新章节")
searchBook.latestChapterTitle = analyzeRule.getString(lastChapter)
Debug.log(bookSource.bookSourceUrl, "└${searchBook.latestChapterTitle}")
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取简介")
searchBook.intro = analyzeRule.getString(intro)
Debug.log(bookSource.bookSourceUrl, "└${searchBook.intro}", true)
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取封面链接")
searchBook.coverUrl = analyzeRule.getString(coverUrl, true)
Debug.log(bookSource.bookSourceUrl, "└${searchBook.coverUrl}")
@@ -140,7 +156,9 @@ object BookList {
return null
}
+ @Throws(Exception::class)
private fun getSearchItem(
+ scope: CoroutineScope,
item: Any,
analyzeRule: AnalyzeRule,
bookSource: BookSource,
@@ -162,30 +180,38 @@ object BookList {
searchBook.originOrder = bookSource.customOrder
analyzeRule.book = searchBook
analyzeRule.setContent(item)
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取书名", log)
searchBook.name = analyzeRule.getString(ruleName)
Debug.log(bookSource.bookSourceUrl, "└${searchBook.name}", log)
if (searchBook.name.isNotEmpty()) {
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取作者", log)
searchBook.author = BookHelp.formatAuthor(analyzeRule.getString(ruleAuthor))
Debug.log(bookSource.bookSourceUrl, "└${searchBook.author}", log)
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取分类", log)
searchBook.kind = analyzeRule.getStringList(ruleKind)?.joinToString(",")
Debug.log(bookSource.bookSourceUrl, "└${searchBook.kind}", log)
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取字数", log)
searchBook.wordCount = analyzeRule.getString(ruleWordCount)
Debug.log(bookSource.bookSourceUrl, "└${searchBook.wordCount}", log)
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取最新章节", log)
searchBook.latestChapterTitle = analyzeRule.getString(ruleLastChapter)
Debug.log(bookSource.bookSourceUrl, "└${searchBook.latestChapterTitle}", log)
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取简介", log)
searchBook.intro = analyzeRule.getString(ruleIntro)
Debug.log(bookSource.bookSourceUrl, "└${searchBook.intro}", log, true)
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取封面链接", log)
analyzeRule.getString(ruleCoverUrl).let {
if (it.isNotEmpty()) searchBook.coverUrl = NetworkUtils.getAbsoluteURL(baseUrl, it)
}
Debug.log(bookSource.bookSourceUrl, "└${searchBook.coverUrl}", log)
+ if (!scope.isActive) throw CancellationException()
Debug.log(bookSource.bookSourceUrl, "┌获取详情页链接", log)
searchBook.bookUrl = analyzeRule.getString(ruleBookUrl, true)
if (searchBook.bookUrl.isEmpty()) {
diff --git a/app/src/main/java/io/legado/app/service/AudioPlayService.kt b/app/src/main/java/io/legado/app/service/AudioPlayService.kt
index c165f3142..986e7c4ed 100644
--- a/app/src/main/java/io/legado/app/service/AudioPlayService.kt
+++ b/app/src/main/java/io/legado/app/service/AudioPlayService.kt
@@ -18,9 +18,9 @@ import androidx.core.app.NotificationCompat
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseService
-import io.legado.app.constant.IntentAction
import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus
+import io.legado.app.constant.IntentAction
import io.legado.app.constant.Status
import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
@@ -275,7 +275,7 @@ class AudioPlayService : BaseService(),
AudioPlay.book?.let { book ->
AudioPlay.webBook?.getContent(book, chapter, scope = this)
?.onSuccess(IO) { content ->
- if (content.isNullOrEmpty()) {
+ if (content.isEmpty()) {
withContext(Main) {
toast("未获取到资源链接")
}
diff --git a/app/src/main/java/io/legado/app/service/DownloadService.kt b/app/src/main/java/io/legado/app/service/DownloadService.kt
index 732506449..dd48933e8 100644
--- a/app/src/main/java/io/legado/app/service/DownloadService.kt
+++ b/app/src/main/java/io/legado/app/service/DownloadService.kt
@@ -130,10 +130,8 @@ class DownloadService : BaseService() {
// notificationContent = "启动:" + chapter.title
//}
.onSuccess(IO) { content ->
- content?.let {
- downloadCount[entry.key]?.increaseSuccess()
- BookHelp.saveContent(book, chapter, content)
- }
+ downloadCount[entry.key]?.increaseSuccess()
+ BookHelp.saveContent(book, chapter, content)
}
.onFinally(IO) {
synchronized(this@DownloadService) {
diff --git a/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt b/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt
index 61507bf7c..1f63dac89 100644
--- a/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt
+++ b/app/src/main/java/io/legado/app/service/TTSReadAloudService.kt
@@ -29,7 +29,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
}
}
- private var ttsIsSuccess: Boolean = false
+ private var ttsInitFinish = false
override fun onCreate() {
super.onCreate()
@@ -42,22 +42,23 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
clearTTS()
}
+ @Synchronized
override fun onInit(status: Int) {
- launch {
- if (status == TextToSpeech.SUCCESS) {
- textToSpeech?.language = Locale.CHINA
- textToSpeech?.setOnUtteranceProgressListener(TTSUtteranceListener())
- ttsIsSuccess = true
- play()
- } else {
+ if (status == TextToSpeech.SUCCESS) {
+ textToSpeech?.language = Locale.CHINA
+ textToSpeech?.setOnUtteranceProgressListener(TTSUtteranceListener())
+ ttsInitFinish = true
+ play()
+ } else {
+ launch {
toast(R.string.tts_init_failed)
}
}
}
- @Suppress("DEPRECATION")
+ @Synchronized
override fun play() {
- if (contentList.isEmpty() || !ttsIsSuccess) {
+ if (contentList.isEmpty() || !ttsInitFinish) {
return
}
if (requestFocus()) {
diff --git a/app/src/main/java/io/legado/app/service/help/ReadBook.kt b/app/src/main/java/io/legado/app/service/help/ReadBook.kt
index 85f2fff45..f48f13d11 100644
--- a/app/src/main/java/io/legado/app/service/help/ReadBook.kt
+++ b/app/src/main/java/io/legado/app/service/help/ReadBook.kt
@@ -21,7 +21,6 @@ import org.jetbrains.anko.toast
object ReadBook {
-
var titleDate = MutableLiveData()
var book: Book? = null
var inBookshelf = false
@@ -34,9 +33,10 @@ object ReadBook {
var curTextChapter: TextChapter? = null
var nextTextChapter: TextChapter? = null
var webBook: WebBook? = null
+ var msg: String? = null
private val loadingChapters = arrayListOf()
- fun resetData(book: Book) {
+ fun resetData(book: Book, noSource: (name: String, author: String) -> Unit) {
this.book = book
titleDate.postValue(book.name)
durChapterIndex = book.durChapterIndex
@@ -46,12 +46,27 @@ object ReadBook {
prevTextChapter = null
curTextChapter = null
nextTextChapter = null
- upWebBook(book.origin)
+ upWebBook(book, noSource)
}
- fun upWebBook(origin: String) {
- val bookSource = App.db.bookSourceDao().getBookSource(origin)
- webBook = if (bookSource != null) WebBook(bookSource) else null
+ fun upWebBook(book: Book?, noSource: (name: String, author: String) -> Unit) {
+ book ?: return
+ if (book.origin == BookType.local) {
+ webBook = null
+ } else {
+ val bookSource = App.db.bookSourceDao().getBookSource(book.origin)
+ if (bookSource != null) {
+ webBook = WebBook(bookSource)
+ } else {
+ webBook = null
+ noSource.invoke(book.name, book.author)
+ }
+ }
+ }
+
+ fun upMsg(msg: String?) {
+ this.msg = msg
+ callBack?.upContent()
}
fun moveToNextPage() {
@@ -69,11 +84,11 @@ object ReadBook {
nextTextChapter = null
book?.let {
if (curTextChapter == null) {
- loadContent(durChapterIndex, upContent)
+ loadContent(durChapterIndex, upContent, false)
} else if (upContent) {
callBack?.upContent()
}
- loadContent(durChapterIndex.plus(1), upContent)
+ loadContent(durChapterIndex.plus(1), upContent, false)
GlobalScope.launch(Dispatchers.IO) {
for (i in 2..10) {
delay(100)
@@ -99,11 +114,11 @@ object ReadBook {
prevTextChapter = null
book?.let {
if (curTextChapter == null) {
- loadContent(durChapterIndex, upContent)
+ loadContent(durChapterIndex, upContent, false)
} else if (upContent) {
callBack?.upContent()
}
- loadContent(durChapterIndex.minus(1), upContent)
+ loadContent(durChapterIndex.minus(1), upContent, false)
GlobalScope.launch(Dispatchers.IO) {
for (i in -5..-2) {
delay(100)
@@ -184,21 +199,21 @@ object ReadBook {
/**
* 加载章节内容
*/
- fun loadContent() {
- loadContent(durChapterIndex)
- loadContent(durChapterIndex + 1)
- loadContent(durChapterIndex - 1)
+ fun loadContent(resetPageOffset: Boolean) {
+ loadContent(durChapterIndex, resetPageOffset = resetPageOffset)
+ loadContent(durChapterIndex + 1, resetPageOffset = resetPageOffset)
+ loadContent(durChapterIndex - 1, resetPageOffset = resetPageOffset)
}
- fun loadContent(index: Int, upContent: Boolean = true) {
+ fun loadContent(index: Int, upContent: Boolean = true, resetPageOffset: Boolean) {
book?.let { book ->
if (addLoading(index)) {
Coroutine.async {
App.db.bookChapterDao().getChapter(book.bookUrl, index)?.let { chapter ->
BookHelp.getContent(book, chapter)?.let {
- contentLoadFinish(chapter, it, upContent)
+ contentLoadFinish(chapter, it, upContent, resetPageOffset)
removeLoading(chapter.index)
- } ?: download(chapter)
+ } ?: download(chapter, resetPageOffset = resetPageOffset)
} ?: removeLoading(index)
}.onError {
removeLoading(index)
@@ -216,7 +231,7 @@ object ReadBook {
if (BookHelp.hasContent(book, chapter)) {
removeLoading(chapter.index)
} else {
- download(chapter)
+ download(chapter, false)
}
} ?: removeLoading(index)
}.onError {
@@ -226,20 +241,28 @@ object ReadBook {
}
}
- private fun download(chapter: BookChapter) {
+ private fun download(chapter: BookChapter, resetPageOffset: Boolean) {
book?.let { book ->
webBook?.getContent(book, chapter)
?.onSuccess(Dispatchers.IO) { content ->
- if (content.isNullOrEmpty()) {
- contentLoadFinish(chapter, App.INSTANCE.getString(R.string.content_empty))
+ if (content.isEmpty()) {
+ contentLoadFinish(
+ chapter,
+ App.INSTANCE.getString(R.string.content_empty),
+ resetPageOffset = resetPageOffset
+ )
removeLoading(chapter.index)
} else {
BookHelp.saveContent(book, chapter, content)
- contentLoadFinish(chapter, content)
+ contentLoadFinish(chapter, content, resetPageOffset = resetPageOffset)
removeLoading(chapter.index)
}
}?.onError {
- contentLoadFinish(chapter, it.localizedMessage ?: "未知错误")
+ contentLoadFinish(
+ chapter,
+ it.localizedMessage ?: "未知错误",
+ resetPageOffset = resetPageOffset
+ )
removeLoading(chapter.index)
}
}
@@ -265,7 +288,8 @@ object ReadBook {
private fun contentLoadFinish(
chapter: BookChapter,
content: String,
- upContent: Boolean = true
+ upContent: Boolean = true,
+ resetPageOffset: Boolean
) {
Coroutine.async {
if (chapter.index in durChapterIndex - 1..durChapterIndex + 1) {
@@ -279,18 +303,18 @@ object ReadBook {
when (chapter.index) {
durChapterIndex -> {
curTextChapter = ChapterProvider.getTextChapter(chapter, c, chapterSize)
- if (upContent) callBack?.upContent()
+ if (upContent) callBack?.upContent(resetPageOffset = resetPageOffset)
callBack?.upView()
curPageChanged()
callBack?.contentLoadFinish()
}
durChapterIndex - 1 -> {
prevTextChapter = ChapterProvider.getTextChapter(chapter, c, chapterSize)
- if (upContent) callBack?.upContent(-1)
+ if (upContent) callBack?.upContent(-1, resetPageOffset)
}
durChapterIndex + 1 -> {
nextTextChapter = ChapterProvider.getTextChapter(chapter, c, chapterSize)
- if (upContent) callBack?.upContent(1)
+ if (upContent) callBack?.upContent(1, resetPageOffset)
}
}
}
@@ -316,7 +340,7 @@ object ReadBook {
}
interface CallBack {
- fun upContent(relativePosition: Int = 0)
+ fun upContent(relativePosition: Int = 0, resetPageOffset: Boolean = true)
fun upView()
fun upPageProgress()
fun contentLoadFinish()
diff --git a/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt b/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt
index bece64c93..3c960093b 100644
--- a/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt
@@ -46,9 +46,8 @@ class AudioPlayActivity :
private var adjustProgress = false
override fun onActivityCreated(savedInstanceState: Bundle?) {
- setSupportActionBar(toolbar)
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
- AudioPlay.titleData.observe(this, Observer { toolbar.title = it })
+ title_bar.transparent()
+ AudioPlay.titleData.observe(this, Observer { title_bar.title = it })
AudioPlay.coverData.observe(this, Observer { upCover(it) })
viewModel.initData(intent)
initView()
diff --git a/app/src/main/java/io/legado/app/ui/audio/AudioPlayViewModel.kt b/app/src/main/java/io/legado/app/ui/audio/AudioPlayViewModel.kt
index b5dd5bfba..892a99f41 100644
--- a/app/src/main/java/io/legado/app/ui/audio/AudioPlayViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/audio/AudioPlayViewModel.kt
@@ -72,7 +72,7 @@ class AudioPlayViewModel(application: Application) : BaseViewModel(application)
execute {
AudioPlay.webBook?.getChapterList(book, this)
?.onSuccess(Dispatchers.IO) { cList ->
- if (!cList.isNullOrEmpty()) {
+ if (cList.isNotEmpty()) {
if (changeDruChapterIndex == null) {
App.db.bookChapterDao().insert(*cList.toTypedArray())
AudioPlay.chapterSize = cList.size
diff --git a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt
index f3b0d5ee5..15217f3bc 100644
--- a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookActivity.kt
@@ -6,12 +6,14 @@ import android.view.MenuItem
import androidx.appcompat.widget.PopupMenu
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup
+import io.legado.app.help.ItemTouchCallback
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.okButton
@@ -64,6 +66,10 @@ class ArrangeBookActivity : VMBaseActivity(R.layout.activi
recycler_view.addItemDecoration(VerticalDivider(this))
adapter = ArrangeBookAdapter(this, this)
recycler_view.adapter = adapter
+ val itemTouchCallback = ItemTouchCallback()
+ itemTouchCallback.onItemTouchCallbackListener = adapter
+ itemTouchCallback.isCanDrag = true
+ ItemTouchHelper(itemTouchCallback).attachToRecyclerView(recycler_view)
select_action_bar.setMainActionText(R.string.move_to_group)
select_action_bar.inflateMenu(R.menu.arrange_book_sel)
select_action_bar.setOnMenuItemClickListener(this)
diff --git a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookAdapter.kt b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookAdapter.kt
index fd51e3a3d..abc72b905 100644
--- a/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/book/arrange/ArrangeBookAdapter.kt
@@ -7,14 +7,13 @@ import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookGroup
-import io.legado.app.utils.gone
-import io.legado.app.utils.visible
+import io.legado.app.help.ItemTouchCallback
import kotlinx.android.synthetic.main.item_arrange_book.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
-
class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
- SimpleRecyclerAdapter(context, R.layout.item_arrange_book) {
+ SimpleRecyclerAdapter(context, R.layout.item_arrange_book),
+ ItemTouchCallback.OnItemTouchCallbackListener {
val groupRequestCode = 12
private val selectedBooks: HashSet = hashSetOf()
var actionItem: Book? = null
@@ -56,12 +55,7 @@ class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
override fun convert(holder: ItemViewHolder, item: Book, payloads: MutableList) {
with(holder.itemView) {
tv_name.text = item.name
-// tv_name.text = if (item.author.isEmpty()) {
-// item.name
-// } else {
-// "${item.name}(${item.author})"
-// }
- tv_author.text = item.author // resources.getString(R.string.author_show, item.author)
+ tv_author.text = item.author
tv_author.visibility = if (item.author.isEmpty()) View.GONE else View.VISIBLE
tv_group_s.text = getGroupName(item.group)
checkbox.isChecked = selectedBooks.contains(item)
@@ -115,7 +109,7 @@ class ArrangeBookAdapter(context: Context, val callBack: CallBack) :
groupNames.add(it.groupName)
}
}
- return groupNames;
+ return groupNames
}
private fun getGroupName(groupId: Int): String {
diff --git a/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt
index 627bacc09..0c91fe0f9 100644
--- a/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt
@@ -51,14 +51,13 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application
fun search() {
task = execute {
- searchStateData.postValue(true)
val bookSourceList = App.db.bookSourceDao().allEnabled
for (item in bookSourceList) {
//task取消时自动取消 by (scope = this@execute)
WebBook(item).searchBook(name, scope = this@execute, context = searchPool)
.timeout(30000L)
.onSuccess(Dispatchers.IO) {
- if (it != null && it.isNotEmpty()) {
+ if (it.isNotEmpty()) {
val searchBook = it[0]
if (searchBook.name == name && searchBook.author == author
&& !searchBook.coverUrl.isNullOrEmpty()
@@ -72,6 +71,10 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application
}
}
}
+ }.onStart {
+ searchStateData.postValue(true)
+ }.onCancel {
+ searchStateData.postValue(false)
}
task?.invokeOnCompletion {
diff --git a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt
index e0800c0a1..b375aed11 100644
--- a/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt
@@ -72,14 +72,13 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
fun search() {
task = execute {
- searchStateData.postValue(true)
val bookSourceList = App.db.bookSourceDao().allEnabled
for (item in bookSourceList) {
//task取消时自动取消 by (scope = this@execute)
WebBook(item).searchBook(name, scope = this@execute, context = searchPool)
.timeout(30000L)
.onSuccess(IO) {
- it?.forEach { searchBook ->
+ it.forEach { searchBook ->
if (searchBook.name == name && searchBook.author == author) {
if (context.getPrefBoolean(PreferKey.changeSourceLoadToc)) {
if (searchBook.tocUrl.isEmpty()) {
@@ -95,6 +94,10 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
}
}
}
+ }.onStart {
+ searchStateData.postValue(true)
+ }.onCancel {
+ searchStateData.postValue(false)
}
task?.invokeOnCompletion {
@@ -107,7 +110,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
App.db.bookSourceDao().getBookSource(book.origin)?.let { bookSource ->
WebBook(bookSource).getBookInfo(book, this)
.onSuccess {
- it?.let { loadChapter(it) }
+ loadChapter(it)
}.onError {
debug { context.getString(R.string.error_get_book_info) }
}
@@ -119,13 +122,11 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
execute {
App.db.bookSourceDao().getBookSource(book.origin)?.let { bookSource ->
WebBook(bookSource).getChapterList(book, this)
- .onSuccess(IO) {
- it?.let { chapters ->
- if (chapters.isNotEmpty()) {
- book.latestChapterTitle = chapters.last().title
- val searchBook: SearchBook = book.toSearchBook()
- searchFinish(searchBook)
- }
+ .onSuccess(IO) { chapters ->
+ if (chapters.isNotEmpty()) {
+ book.latestChapterTitle = chapters.last().title
+ val searchBook: SearchBook = book.toSearchBook()
+ searchFinish(searchBook)
}
}.onError {
debug { context.getString(R.string.error_get_chapter_list) }
diff --git a/app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListFragment.kt b/app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListFragment.kt
index 51c8b8096..d717cb25f 100644
--- a/app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/book/chapterlist/ChapterListFragment.kt
@@ -36,6 +36,7 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragme
private var durChapterIndex = 0
private lateinit var mLayoutManager: UpLinearLayoutManager
private var tocLiveData: LiveData>? = null
+ private var scrollToDurChapter = false
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
viewModel.chapterCallBack = this
@@ -74,7 +75,6 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragme
book?.let {
durChapterIndex = it.durChapterIndex
tv_current_chapter_info.text = it.durChapterTitle
- mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0)
initCacheFileNames(it)
}
}
@@ -85,7 +85,10 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragme
tocLiveData = App.db.bookChapterDao().observeByBook(viewModel.bookUrl)
tocLiveData?.observe(viewLifecycleOwner, Observer {
adapter.setItems(it)
- mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0)
+ if (!scrollToDurChapter) {
+ mLayoutManager.scrollToPositionWithOffset(durChapterIndex, 0)
+ scrollToDurChapter = true
+ }
})
}
diff --git a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt
index d34e21916..bd22b3968 100644
--- a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt
@@ -35,11 +35,9 @@ class ExploreShowViewModel(application: Application) : BaseViewModel(application
WebBook(source).exploreBook(url, page, this)
.timeout(30000L)
.onSuccess(IO) { searchBooks ->
- searchBooks?.let {
- booksData.postValue(searchBooks)
- App.db.searchBookDao().insert(*searchBooks.toTypedArray())
- page++
- }
+ booksData.postValue(searchBooks)
+ App.db.searchBookDao().insert(*searchBooks.toTypedArray())
+ page++
}
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
index 1086f0416..8be9ed34f 100644
--- a/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
@@ -33,6 +33,7 @@ import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel
import io.legado.app.utils.requestInputMethod
+import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_group_manage.view.*
@@ -74,6 +75,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
tv_ok.setTextColor(requireContext().accentColor)
+ tv_ok.visible()
tv_ok.onClick { dismiss() }
App.db.bookGroupDao().liveDataAll().observe(viewLifecycleOwner, Observer {
val diffResult =
diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
index 39cca5312..e0b6f275a 100644
--- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
@@ -1,11 +1,14 @@
package io.legado.app.ui.book.info
+import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
+import android.widget.CheckBox
+import android.widget.LinearLayout
import androidx.lifecycle.Observer
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
@@ -29,6 +32,7 @@ import io.legado.app.ui.book.info.edit.BookInfoEditActivity
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.book.search.SearchActivity
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
+import io.legado.app.utils.dp
import io.legado.app.utils.getViewModel
import io.legado.app.utils.gone
import io.legado.app.utils.visible
@@ -53,8 +57,7 @@ class BookInfoActivity :
get() = getViewModel(BookInfoViewModel::class.java)
override fun onActivityCreated(savedInstanceState: Bundle?) {
- setSupportActionBar(toolbar)
- supportActionBar?.setDisplayHomeAsUpEnabled(true)
+ title_bar.transparent()
viewModel.bookData.observe(this, Observer { showBook(it) })
viewModel.chapterListData.observe(this, Observer { upLoading(false, it) })
viewModel.initData(intent)
@@ -227,25 +230,33 @@ class BookInfoActivity :
tv_author.onClick {
startActivity(Pair("key", viewModel.bookData.value?.author))
}
+ tv_name.onClick {
+ startActivity(Pair("key", viewModel.bookData.value?.name))
+ }
}
+ @SuppressLint("InflateParams")
private fun deleteBook() {
viewModel.bookData.value?.let {
if (it.isLocalBook()) {
alert(
titleResource = R.string.sure,
- messageResource = R.string.sure_delete_book_file
+ messageResource = R.string.sure_del
) {
- positiveButton(R.string.yes) {
- viewModel.delBook(true) {
- finish()
- }
+ val checkBox = CheckBox(this@BookInfoActivity).apply {
+ setText(R.string.delete_book_file)
+ }
+ val view = LinearLayout(this@BookInfoActivity).apply {
+ setPadding(16.dp, 0, 16.dp, 0)
+ addView(checkBox)
}
- negativeButton(R.string.no) {
- viewModel.delBook(false) {
+ customView = view
+ positiveButton(R.string.yes) {
+ viewModel.delBook(checkBox.isChecked) {
finish()
}
}
+ negativeButton(R.string.no)
}.show()
} else {
viewModel.delBook {
diff --git a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
index 485d704ce..a8d8f59ef 100644
--- a/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/info/BookInfoViewModel.kt
@@ -59,13 +59,11 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
App.db.bookSourceDao().getBookSource(book.origin)?.let { bookSource ->
WebBook(bookSource).getBookInfo(book, this)
.onSuccess(IO) {
- it?.let {
- bookData.postValue(book)
- if (inBookshelf) {
- App.db.bookDao().update(book)
- }
- loadChapter(it, changeDruChapterIndex)
+ bookData.postValue(book)
+ if (inBookshelf) {
+ App.db.bookDao().update(book)
}
+ loadChapter(it, changeDruChapterIndex)
}.onError {
toast(R.string.error_get_book_info)
}
@@ -92,20 +90,18 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
App.db.bookSourceDao().getBookSource(book.origin)?.let { bookSource ->
WebBook(bookSource).getChapterList(book, this)
.onSuccess(IO) {
- it?.let {
- if (it.isNotEmpty()) {
- if (inBookshelf) {
- App.db.bookDao().update(book)
- App.db.bookChapterDao().insert(*it.toTypedArray())
- }
- if (changeDruChapterIndex == null) {
- chapterListData.postValue(it)
- } else {
- changeDruChapterIndex(it)
- }
+ if (it.isNotEmpty()) {
+ if (inBookshelf) {
+ App.db.bookDao().update(book)
+ App.db.bookChapterDao().insert(*it.toTypedArray())
+ }
+ if (changeDruChapterIndex == null) {
+ chapterListData.postValue(it)
} else {
- toast(R.string.chapter_list_empty)
+ changeDruChapterIndex(it)
}
+ } else {
+ toast(R.string.chapter_list_empty)
}
}.onError {
chapterListData.postValue(null)
diff --git a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt
index 2958325fe..3ce47ee99 100644
--- a/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/local/ImportBookActivity.kt
@@ -8,6 +8,7 @@ import android.os.Bundle
import android.provider.DocumentsContract
import android.view.Menu
import android.view.MenuItem
+import android.view.View
import androidx.appcompat.widget.PopupMenu
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.LiveData
@@ -17,6 +18,8 @@ import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.help.AppConfig
+import io.legado.app.help.permission.Permissions
+import io.legado.app.help.permission.PermissionsCompat
import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.filechooser.FilePicker
import io.legado.app.ui.widget.SelectActionBar
@@ -40,7 +43,8 @@ class ImportBookActivity : VMBaseActivity(R.layout.activity
private val subDocs = arrayListOf()
private lateinit var adapter: ImportBookAdapter
private var localUriLiveData: LiveData>? = null
- private var path = FileUtils.getSdCardPath()
+ private var sdPath = FileUtils.getSdCardPath()
+ private var path = sdPath
override val viewModel: ImportBookViewModel
get() = getViewModel(ImportBookViewModel::class.java)
@@ -79,6 +83,7 @@ class ImportBookActivity : VMBaseActivity(R.layout.activity
}
}
})
+
}
private fun initEvent() {
@@ -123,6 +128,24 @@ class ImportBookActivity : VMBaseActivity(R.layout.activity
subDocs.clear()
path = it
}
+ } ?: let {
+ // 没有权限就显示一个授权提示和按钮
+ if (PermissionsCompat.check(this, *Permissions.Group.STORAGE)) {
+ hint_per.visibility = View.GONE
+ } else {
+ hint_per.visibility = View.VISIBLE
+ tv_request_per.onClick {
+ PermissionsCompat.Builder(this)
+ .addPermissions(*Permissions.Group.STORAGE)
+ .rationale(R.string.tip_perm_request_storage)
+ .onGranted {
+ hint_per.visibility = View.GONE
+ initData()
+ upRootDoc()
+ }
+ .request()
+ }
+ }
}
upPath()
}
@@ -159,7 +182,7 @@ class ImportBookActivity : VMBaseActivity(R.layout.activity
}
}
} ?: let {
- tv_path.text = path
+ tv_path.text = path.replace(sdPath, "SD")
val docList = arrayListOf()
File(path).listFiles()?.forEach {
if (it.isDirectory) {
@@ -228,7 +251,7 @@ class ImportBookActivity : VMBaseActivity(R.layout.activity
@Synchronized
private fun goBackDir(): Boolean {
if (rootDoc == null) {
- if (path != FileUtils.getSdCardPath()) {
+ if (path != sdPath) {
File(path).parent?.let {
path = it
upPath()
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
index 3de366ec3..e81d7cad9 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
@@ -25,6 +25,7 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
import io.legado.app.help.ReadBookConfig
import io.legado.app.help.coroutine.Coroutine
+import io.legado.app.help.storage.Backup
import io.legado.app.help.storage.SyncBookProgress
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.noButton
@@ -91,6 +92,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
override val headerHeight: Int get() = page_view.curPage.headerHeight
override fun onCreate(savedInstanceState: Bundle?) {
+ ReadBook.msg = null
Help.setOrientation(this)
super.onCreate(savedInstanceState)
}
@@ -115,7 +117,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
- ReadBook.loadContent()
+ ReadBook.loadContent(resetPageOffset = false)
}
override fun onResume() {
@@ -402,11 +404,10 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
*/
override fun showTextActionMenu() {
textActionMenu ?: let {
- textActionMenu = TextActionMenu(this, this).apply {
- contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
- }
+ textActionMenu = TextActionMenu(this, this)
}
textActionMenu?.let { popup ->
+ popup.contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
val popupHeight = popup.contentView.measuredHeight
val x = text_menu_position.x.toInt()
var y = text_menu_position.y.toInt() - popupHeight
@@ -481,9 +482,9 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
/**
* 更新内容
*/
- override fun upContent(relativePosition: Int) {
+ override fun upContent(relativePosition: Int, resetPageOffset: Boolean) {
launch {
- page_view.upContent(relativePosition)
+ page_view.upContent(relativePosition, resetPageOffset)
}
}
@@ -579,7 +580,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
override fun onReplaceRuleSave() {
Coroutine.async {
BookHelp.upReplaceRules()
- ReadBook.loadContent()
+ ReadBook.loadContent(resetPageOffset = false)
}
}
@@ -682,8 +683,10 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
mHandler.removeCallbacks(keepScreenRunnable)
textActionMenu?.dismiss()
page_view.onDestroy()
+ ReadBook.msg = null
if (!BuildConfig.DEBUG) {
SyncBookProgress.uploadBookProgress()
+ Backup.autoBack(this)
}
}
@@ -707,9 +710,9 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
page_view.upBg()
page_view.upStyle()
if (it) {
- ReadBook.loadContent()
+ ReadBook.loadContent(resetPageOffset = false)
} else {
- page_view.upContent()
+ page_view.upContent(resetPageOffset = false)
}
}
observeEvent(EventBus.ALOUD_STATE) {
@@ -718,7 +721,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
val page = textChapter.page(ReadBook.durPageIndex)
if (page != null) {
page.removePageAloudSpan()
- page_view.upContent()
+ page_view.upContent(resetPageOffset = false)
}
}
}
@@ -735,9 +738,6 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
}
}
}
- observeEvent(EventBus.REPLACE) {
- ReplaceEditDialog().show(supportFragmentManager, "replaceEditDialog")
- }
observeEvent(PreferKey.keepLight) {
upScreenTimeOut()
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
index 2ebab7875..231f83e11 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
@@ -39,7 +39,9 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
private fun initBook(book: Book) {
if (ReadBook.book?.bookUrl != book.bookUrl) {
- ReadBook.resetData(book)
+ ReadBook.resetData(book) { name, author ->
+ autoChangeSource(name, author)
+ }
isInitFinish = true
ReadBook.chapterSize = App.db.bookChapterDao().getChapterCount(book.bookUrl)
if (ReadBook.chapterSize == 0) {
@@ -52,7 +54,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
if (ReadBook.durChapterIndex > ReadBook.chapterSize - 1) {
ReadBook.durChapterIndex = ReadBook.chapterSize - 1
}
- ReadBook.loadContent()
+ ReadBook.loadContent(resetPageOffset = true)
}
if (ReadBook.inBookshelf) {
ReadBook.saveRead()
@@ -60,7 +62,9 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
} else {
isInitFinish = true
ReadBook.titleDate.postValue(book.name)
- ReadBook.upWebBook(book.origin)
+ ReadBook.upWebBook(book) { name, author ->
+ autoChangeSource(name, author)
+ }
ReadBook.chapterSize = App.db.bookChapterDao().getChapterCount(book.bookUrl)
if (ReadBook.chapterSize == 0) {
if (book.tocUrl.isEmpty()) {
@@ -70,7 +74,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
}
} else {
if (ReadBook.curTextChapter != null) {
- ReadBook.callBack?.upContent()
+ ReadBook.callBack?.upContent(resetPageOffset = false)
}
}
}
@@ -103,32 +107,34 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
App.db.bookChapterDao().insert(*it.toTypedArray())
App.db.bookDao().update(book)
ReadBook.chapterSize = it.size
- ReadBook.loadContent()
+ ReadBook.loadContent(resetPageOffset = true)
}
} else {
ReadBook.webBook?.getChapterList(book, this)
?.onSuccess(IO) { cList ->
- if (!cList.isNullOrEmpty()) {
+ if (cList.isNotEmpty()) {
if (changeDruChapterIndex == null) {
App.db.bookChapterDao().insert(*cList.toTypedArray())
App.db.bookDao().update(book)
ReadBook.chapterSize = cList.size
- ReadBook.loadContent()
+ ReadBook.upMsg(null)
+ ReadBook.loadContent(resetPageOffset = true)
} else {
changeDruChapterIndex(cList)
}
} else {
- toast(R.string.error_load_toc)
+ ReadBook.upMsg(context.getString(R.string.error_load_toc))
}
}?.onError {
- toast(R.string.error_load_toc)
- } ?: autoChangeSource()
+ ReadBook.upMsg(context.getString(R.string.error_load_toc))
+ }
}
}
}
fun changeTo(book1: Book) {
execute {
+ ReadBook.upMsg(null)
ReadBook.book?.let {
App.db.bookDao().delete(it)
}
@@ -151,8 +157,26 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
}
}
- private fun autoChangeSource() {
-
+ private fun autoChangeSource(name: String, author: String) {
+ execute {
+ App.db.bookSourceDao().allTextEnabled.forEach { source ->
+ try {
+ val searchBooks = WebBook(source).searchBookSuspend(this, name)
+ searchBooks.getOrNull(0)?.let {
+ if (it.name == name && (it.author == author || author == "")) {
+ changeTo(it.toBook())
+ return@forEach
+ }
+ }
+ } catch (e: Exception) {
+ //nothing
+ }
+ }
+ }.onStart {
+ ReadBook.upMsg("正在自动换源")
+ }.onFinally {
+ ReadBook.upMsg(null)
+ }
}
private fun upChangeDurChapterIndex(book: Book, chapters: List) {
@@ -167,7 +191,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
App.db.bookDao().update(book)
App.db.bookChapterDao().insert(*chapters.toTypedArray())
ReadBook.chapterSize = chapters.size
- ReadBook.loadContent()
+ ReadBook.loadContent(resetPageOffset = true)
}
}
@@ -181,7 +205,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
ReadBook.durPageIndex = pageIndex
}
ReadBook.saveRead()
- ReadBook.loadContent()
+ ReadBook.loadContent(resetPageOffset = true)
}
fun removeFromBookshelf(success: (() -> Unit)?) {
@@ -208,9 +232,9 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
execute {
App.db.bookChapterDao().getChapter(book.bookUrl, ReadBook.durChapterIndex)
?.let { chapter ->
- BookHelp.delContent(book, chapter)
- ReadBook.loadContent(ReadBook.durChapterIndex)
- }
+ BookHelp.delContent(book, chapter)
+ ReadBook.loadContent(ReadBook.durChapterIndex, resetPageOffset = false)
+ }
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt
index 9d7046f6e..5c8d1d214 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt
@@ -7,6 +7,7 @@ import android.content.Intent
import android.content.pm.ResolveInfo
import android.net.Uri
import android.os.Build
+import android.speech.tts.TextToSpeech
import android.view.LayoutInflater
import android.view.Menu
import android.view.ViewGroup
@@ -15,13 +16,12 @@ import androidx.annotation.RequiresApi
import androidx.appcompat.view.SupportMenuInflater
import androidx.appcompat.view.menu.MenuBuilder
import androidx.appcompat.view.menu.MenuItemImpl
-import androidx.appcompat.widget.PopupMenu
-import androidx.core.view.size
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
+import androidx.core.view.isVisible
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
+import io.legado.app.service.BaseReadAloudService
+import io.legado.app.utils.gone
import io.legado.app.utils.isAbsUrl
import io.legado.app.utils.sendToClip
import io.legado.app.utils.visible
@@ -30,10 +30,16 @@ import kotlinx.android.synthetic.main.popup_action_menu.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.share
import org.jetbrains.anko.toast
+import java.util.*
@SuppressLint("RestrictedApi")
class TextActionMenu(private val context: Context, private val callBack: CallBack) :
- PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) {
+ PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT),
+ TextToSpeech.OnInitListener {
+
+ private val adapter = Adapter(context)
+ private val menu = MenuBuilder(context)
+ private val moreMenu = MenuBuilder(context)
init {
@SuppressLint("InflateParams")
@@ -44,33 +50,38 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
isFocusable = false
initRecyclerView()
+ setOnDismissListener {
+ contentView.apply {
+ iv_menu_more.setImageResource(R.drawable.ic_more_vert)
+ recycler_view_more.gone()
+ adapter.setItems(menu.visibleItems)
+ recycler_view.visible()
+ }
+ }
}
private fun initRecyclerView() = with(contentView) {
- val adapter = Adapter(context)
- recycler_view.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
recycler_view.adapter = adapter
- val menu = MenuBuilder(context)
+ recycler_view_more.adapter = adapter
SupportMenuInflater(context).inflate(R.menu.content_select_action, menu)
adapter.setItems(menu.visibleItems)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- val popupMenu = PopupMenu(context, iv_menu_more)
- onInitializeMenu(popupMenu.menu)
- if (popupMenu.menu.size > 0) {
- iv_menu_more.visible()
- popupMenu.setOnMenuItemClickListener { item ->
- item.intent?.let {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- it.putExtra(Intent.EXTRA_PROCESS_TEXT, callBack.selectedText)
- context.startActivity(it)
- }
- }
- this@TextActionMenu.dismiss()
- true
- }
- }
- iv_menu_more.onClick {
- popupMenu.show()
+ onInitializeMenu(moreMenu)
+ }
+ if (moreMenu.size() > 0) {
+ iv_menu_more.visible()
+ }
+ iv_menu_more.onClick {
+ if (recycler_view.isVisible) {
+ iv_menu_more.setImageResource(R.drawable.ic_arrow_back)
+ adapter.setItems(moreMenu.visibleItems)
+ recycler_view.gone()
+ recycler_view_more.visible()
+ } else {
+ iv_menu_more.setImageResource(R.drawable.ic_more_vert)
+ recycler_view_more.gone()
+ adapter.setItems(menu.visibleItems)
+ recycler_view.visible()
}
}
}
@@ -104,6 +115,13 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
when (item.itemId) {
R.id.menu_copy -> context.sendToClip(callBack.selectedText)
R.id.menu_share_str -> context.share(callBack.selectedText)
+ R.id.menu_aloud -> {
+ if (BaseReadAloudService.isRun) {
+ context.toast(R.string.alouding_disable)
+ return
+ }
+ readAloud(callBack.selectedText)
+ }
R.id.menu_browser -> {
try {
val intent = if (callBack.selectedText.isAbsUrl()) {
@@ -121,9 +139,41 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
context.toast(e.localizedMessage ?: "ERROR")
}
}
+ else -> item.intent?.let {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ it.putExtra(Intent.EXTRA_PROCESS_TEXT, callBack.selectedText)
+ context.startActivity(it)
+ }
+ }
}
}
+ private var textToSpeech: TextToSpeech? = null
+ private var ttsInitFinish = false
+ private var lastText: String = ""
+
+ @SuppressLint("SetJavaScriptEnabled")
+ private fun readAloud(text: String) {
+ lastText = text
+ if (textToSpeech == null) {
+ textToSpeech = TextToSpeech(context, this)
+ return
+ }
+ if (!ttsInitFinish) return
+ if (text == "") return
+ if (textToSpeech?.isSpeaking == true)
+ textToSpeech?.stop()
+ textToSpeech?.speak(text, TextToSpeech.QUEUE_ADD, null, "select_text")
+ lastText = ""
+ }
+
+ @Synchronized
+ override fun onInit(status: Int) {
+ textToSpeech?.language = Locale.CHINA
+ ttsInitFinish = true
+ readAloud(lastText)
+ }
+
@RequiresApi(Build.VERSION_CODES.M)
private fun createProcessTextIntent(): Intent {
return Intent()
diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt
index 5540d0386..9c118c068 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt
@@ -21,7 +21,6 @@ import io.legado.app.utils.observeEvent
import io.legado.app.utils.putPrefBoolean
import kotlinx.android.synthetic.main.dialog_read_aloud.*
import org.jetbrains.anko.sdk27.listeners.onClick
-import org.jetbrains.anko.sdk27.listeners.onLongClick
class ReadAloudDialog : BaseDialogFragment() {
var callBack: CallBack? = null
@@ -34,7 +33,7 @@ class ReadAloudDialog : BaseDialogFragment() {
it.windowManager?.defaultDisplay?.getMetrics(dm)
}
dialog?.window?.let {
- it.setBackgroundDrawableResource(R.color.transparent)
+ it.setBackgroundDrawableResource(R.color.background)
it.decorView.setPadding(0, 0, 0, 0)
val attr = it.attributes
attr.dimAmount = 0.0f
@@ -54,10 +53,10 @@ class ReadAloudDialog : BaseDialogFragment() {
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
- ll_bottom_bg.setBackgroundColor(requireContext().bottomBackground)
+ root_view.setBackgroundColor(requireContext().bottomBackground)
initOnChange()
initData()
- initOnClick()
+ initEvent()
}
private fun initData() {
@@ -101,22 +100,19 @@ class ReadAloudDialog : BaseDialogFragment() {
})
}
- private fun initOnClick() {
- iv_menu.onClick { callBack?.showMenuBar(); dismiss() }
- iv_other_config.onClick {
+ private fun initEvent() {
+ ll_main_menu.onClick { callBack?.showMenuBar(); dismiss() }
+ ll_setting.onClick {
ReadAloudConfigDialog().show(childFragmentManager, "readAloudConfigDialog")
}
+ tv_pre.onClick { ReadBook.moveToPrevChapter(upContent = true, toLast = false) }
+ tv_next.onClick { ReadBook.moveToNextChapter(true) }
iv_stop.onClick { ReadAloud.stop(requireContext()); dismiss() }
iv_play_pause.onClick { callBack?.onClickReadAloud() }
iv_play_prev.onClick { ReadAloud.prevParagraph(requireContext()) }
- iv_play_prev.onLongClick {
- ReadBook.moveToPrevChapter(upContent = true, toLast = false)
- true
- }
iv_play_next.onClick { ReadAloud.nextParagraph(requireContext()) }
- iv_play_next.onLongClick { ReadBook.moveToNextChapter(true); true }
- fabToc.onClick { callBack?.openChapterList() }
- fabBack.onClick { callBack?.finish() }
+ ll_catalog.onClick { callBack?.openChapterList() }
+ ll_to_backstage.onClick { callBack?.finish() }
}
private fun upPlayState() {
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
index 6dc89f716..1cc006512 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
@@ -108,8 +108,10 @@ class ContentView(context: Context) : FrameLayout(context) {
}
}
- fun setContent(textPage: TextPage) {
+ fun setContent(textPage: TextPage, resetPageOffset: Boolean = true) {
setProgress(textPage)
+ if (resetPageOffset)
+ resetPageOffset()
content_text_view.setContent(textPage)
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/DataSource.kt b/app/src/main/java/io/legado/app/ui/book/read/page/DataSource.kt
index 4f7afa04f..3a128f969 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/DataSource.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/DataSource.kt
@@ -17,5 +17,5 @@ interface DataSource {
fun hasPrevChapter(): Boolean
- fun upContent(relativePosition: Int = 0)
+ fun upContent(relativePosition: Int = 0, resetPageOffset: Boolean = true)
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
index 1b7b71b07..dd19fdc0e 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
@@ -38,7 +38,7 @@ class PageView(context: Context, attrs: AttributeSet) :
prevPage.x = -w.toFloat()
pageDelegate?.setViewSize(w, h)
if (oldw != 0 && oldh != 0) {
- ReadBook.loadContent()
+ ReadBook.loadContent(resetPageOffset = false)
}
}
@@ -93,9 +93,9 @@ class PageView(context: Context, attrs: AttributeSet) :
upContent()
}
- override fun upContent(relativePosition: Int) {
+ override fun upContent(relativePosition: Int, resetPageOffset: Boolean) {
if (ReadBookConfig.isScroll) {
- curPage.setContent(pageFactory.currentPage)
+ curPage.setContent(pageFactory.currentPage, resetPageOffset)
} else {
when (relativePosition) {
-1 -> prevPage.setContent(pageFactory.prevPage)
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt b/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt
index a867daf3d..a38ed6e51 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt
@@ -38,7 +38,7 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource
} else {
ReadBook.setPageIndex(pageIndex.plus(1))
}
- if (upContent) upContent()
+ if (upContent) upContent(resetPageOffset = false)
true
} else
false
@@ -51,7 +51,7 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource
} else {
ReadBook.setPageIndex(pageIndex.minus(1))
}
- if (upContent) upContent()
+ if (upContent) upContent(resetPageOffset = false)
true
} else
false
@@ -59,6 +59,9 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource
override val currentPage: TextPage
get() = with(dataSource) {
+ ReadBook.msg?.let {
+ return@with TextPage(text = it).format()
+ }
currentChapter?.let {
return@with it.page(pageIndex)
?: TextPage(title = it.title).format()
@@ -68,6 +71,9 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource
override val nextPage: TextPage
get() = with(dataSource) {
+ ReadBook.msg?.let {
+ return@with TextPage(text = it).format()
+ }
currentChapter?.let {
if (pageIndex < it.pageSize() - 1) {
return@with it.page(pageIndex + 1)?.removePageAloudSpan()
@@ -86,6 +92,9 @@ class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource
override val prevPage: TextPage
get() = with(dataSource) {
+ ReadBook.msg?.let {
+ return@with TextPage(text = it).format()
+ }
if (pageIndex > 0) {
currentChapter?.let {
return@with it.page(pageIndex - 1)?.removePageAloudSpan()
diff --git a/app/src/main/java/io/legado/app/ui/book/search/DiffCallBack.kt b/app/src/main/java/io/legado/app/ui/book/search/DiffCallBack.kt
index 07a3b16c5..b03e54fb7 100644
--- a/app/src/main/java/io/legado/app/ui/book/search/DiffCallBack.kt
+++ b/app/src/main/java/io/legado/app/ui/book/search/DiffCallBack.kt
@@ -49,9 +49,9 @@ class DiffCallBack(private val oldItems: List, private val newItems:
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
- val oldItem = oldItems[oldItemPosition]
- val newItem = newItems[newItemPosition]
val payload = Bundle()
+ val newItem = newItems[newItemPosition]
+ val oldItem = oldItems[oldItemPosition]
if (oldItem.name != newItem.name) {
payload.putString("name", newItem.name)
}
@@ -73,6 +73,9 @@ class DiffCallBack(private val oldItems: List, private val newItems:
if (oldItem.intro != newItem.intro) {
payload.putString("intro", newItem.intro)
}
+ if (payload.isEmpty) {
+ return null
+ }
return payload
}
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt
index 2d1054859..e81545adc 100644
--- a/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/search/SearchActivity.kt
@@ -112,6 +112,7 @@ class SearchActivity : VMBaseActivity(R.layout.activity_book_se
viewModel.saveSearchKey(query)
viewModel.search(it)
}
+ openOrCloseHistory(false)
return true
}
@@ -128,6 +129,7 @@ class SearchActivity : VMBaseActivity(R.layout.activity_book_se
openOrCloseHistory(hasFocus)
}
}
+ openOrCloseHistory(true)
}
private fun initRecyclerView() {
@@ -283,10 +285,12 @@ class SearchActivity : VMBaseActivity(R.layout.activity_book_se
*/
@Synchronized
private fun upSearchItems(items: List, isMandatoryUpdate: Boolean) {
+ val searchItems = ArrayList(items)
if (isMandatoryUpdate || System.currentTimeMillis() - refreshTime > 500) {
refreshTime = System.currentTimeMillis()
- val diffResult = DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), items))
- adapter.setItems(items, diffResult)
+ val diffResult =
+ DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), searchItems))
+ adapter.setItems(searchItems, diffResult)
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt
index 47671626d..ccfc5dd15 100644
--- a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt
@@ -7,82 +7,62 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.SearchBook
import io.legado.app.data.entities.SearchKeyword
-import io.legado.app.help.AppConfig
-import io.legado.app.help.coroutine.Coroutine
-import io.legado.app.model.WebBook
+import io.legado.app.model.SearchBookModel
import io.legado.app.utils.getPrefBoolean
-import io.legado.app.utils.getPrefString
-import kotlinx.coroutines.Dispatchers.IO
-import kotlinx.coroutines.asCoroutineDispatcher
-import java.util.concurrent.Executors
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.isActive
-class SearchViewModel(application: Application) : BaseViewModel(application) {
- private var searchPool =
- Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher()
- private var task: Coroutine<*>? = null
+class SearchViewModel(application: Application) : BaseViewModel(application)
+ , SearchBookModel.CallBack {
+ private val searchBookModel = SearchBookModel(this, this)
var isSearchLiveData = MutableLiveData()
var searchBookLiveData = MutableLiveData>()
var searchKey: String = ""
- var searchPage = 1
var isLoading = false
var searchBooks = arrayListOf()
+ private var searchID = 0L
/**
* 开始搜索
*/
fun search(key: String) {
- task?.cancel()
- if (key.isEmpty() && searchKey.isEmpty()) {
- return
- } else if (key.isEmpty()) {
- isLoading = true
- searchPage++
- } else if (key.isNotEmpty()) {
- isLoading = true
- searchPage = 1
- searchKey = key
+ if (searchKey != key && key.isNotEmpty()) {
+ searchBookModel.cancelSearch()
searchBooks.clear()
+ searchBookLiveData.postValue(searchBooks)
+ searchID = System.currentTimeMillis()
+ searchKey = key
}
+ searchBookModel.search(searchID, searchKey)
+ }
+
+ override fun onSearchStart() {
isSearchLiveData.postValue(true)
- task = execute {
- val searchGroup = context.getPrefString("searchGroup") ?: ""
- val bookSourceList = if (searchGroup.isBlank()) {
- App.db.bookSourceDao().allEnabled
- } else {
- App.db.bookSourceDao().getEnabledByGroup(searchGroup)
- }
- for (item in bookSourceList) {
- //task取消时自动取消 by (scope = this@execute)
- WebBook(item).searchBook(
- searchKey,
- searchPage,
- scope = this@execute,
- context = searchPool
- )
- .timeout(30000L)
- .onSuccess(IO) {
- it?.let { list ->
- if (context.getPrefBoolean(PreferKey.precisionSearch)) {
- precisionSearch(list)
- } else {
- App.db.searchBookDao().insert(*list.toTypedArray())
- mergeItems(list)
- }
- }
- }
- }
- }
+ }
- task?.invokeOnCompletion {
- isSearchLiveData.postValue(false)
- isLoading = false
+ override fun onSearchSuccess(searchBooks: ArrayList) {
+ if (context.getPrefBoolean(PreferKey.precisionSearch)) {
+ precisionSearch(this, searchBooks)
+ } else {
+ App.db.searchBookDao().insert(*searchBooks.toTypedArray())
+ mergeItems(this, searchBooks)
}
}
+ override fun onSearchFinish() {
+ isSearchLiveData.postValue(false)
+ isLoading = false
+ }
+
+ override fun onSearchCancel() {
+ isSearchLiveData.postValue(false)
+ isLoading = false
+ }
+
/**
* 精确搜索处理
*/
- private fun precisionSearch(searchBooks: List) {
+ private fun precisionSearch(scope: CoroutineScope, searchBooks: List) {
val books = arrayListOf()
searchBooks.forEach { searchBook ->
if (searchBook.name.contains(searchKey, true)
@@ -90,14 +70,16 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
) books.add(searchBook)
}
App.db.searchBookDao().insert(*books.toTypedArray())
- mergeItems(books)
+ if (scope.isActive) {
+ mergeItems(scope, books)
+ }
}
/**
* 合并搜索结果并排序
*/
@Synchronized
- private fun mergeItems(newDataS: List) {
+ private fun mergeItems(scope: CoroutineScope, newDataS: List) {
if (newDataS.isNotEmpty()) {
val copyDataS = ArrayList(searchBooks)
val searchBooksAdd = ArrayList()
@@ -141,6 +123,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
}
}
}
+ if (!scope.isActive) return
searchBooks.sortWith(Comparator { o1, o2 ->
if (o1.name == searchKey && o2.name != searchKey) {
1
@@ -166,6 +149,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
0
}
})
+ if (!scope.isActive) return
searchBooks = copyDataS
searchBookLiveData.postValue(copyDataS)
}
@@ -175,7 +159,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
* 停止搜索
*/
fun stop() {
- task?.cancel()
+ searchBookModel.cancelSearch()
}
/**
@@ -211,7 +195,7 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
override fun onCleared() {
super.onCleared()
- searchPool.close()
+ searchBookModel.close()
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
index 85e3239bc..c84497536 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
@@ -8,7 +8,6 @@ import io.legado.app.App
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.BookSource
import io.legado.app.help.http.HttpHelper
-import io.legado.app.help.storage.Backup
import io.legado.app.help.storage.OldRule
import io.legado.app.help.storage.Restore.jsonPath
import io.legado.app.utils.*
@@ -96,7 +95,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
FileUtils.createFileIfNotExist(file, "exportBookSource.json")
.writeText(json)
}.onSuccess {
- context.toast("成功导出至\n${Backup.exportPath}")
+ context.toast("成功导出至\n${file.absolutePath}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}
@@ -109,7 +108,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
doc.createFile("", "exportBookSource.json")
?.writeText(context, json)
}.onSuccess {
- context.toast("成功导出至\n${Backup.exportPath}")
+ context.toast("成功导出至\n${doc.uri.path}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}
@@ -211,7 +210,7 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
}.onError {
finally(it.localizedMessage ?: "")
}.onSuccess {
- finally(it ?: "导入完成")
+ finally(it)
}
}
diff --git a/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt b/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt
index 409638a4c..c849ae1de 100644
--- a/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt
+++ b/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt
@@ -48,30 +48,24 @@ object BackupRestoreUi {
selectBackupFolder(fragment, backupSelectRequestCode)
}
} else {
- backupUsePermission(fragment, requestCode = backupSelectRequestCode)
+ backupUsePermission(fragment, backupPath)
}
}
}
private fun backupUsePermission(
fragment: Fragment,
- path: String = Backup.legadoPath,
- requestCode: Int = selectFolderRequestCode
+ path: String
) {
PermissionsCompat.Builder(fragment)
.addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage)
.onGranted {
- when (requestCode) {
- selectFolderRequestCode -> AppConfig.backupPath = Backup.legadoPath
- else -> {
- Coroutine.async {
- AppConfig.backupPath = path
- Backup.backup(fragment.requireContext(), path)
- }.onSuccess {
- fragment.toast(R.string.backup_success)
- }
- }
+ Coroutine.async {
+ AppConfig.backupPath = path
+ Backup.backup(fragment.requireContext(), path)
+ }.onSuccess {
+ fragment.toast(R.string.backup_success)
}
}
.request()
@@ -107,7 +101,7 @@ object BackupRestoreUi {
}
}
- private fun restoreUsePermission(fragment: Fragment, path: String = Backup.legadoPath) {
+ private fun restoreUsePermission(fragment: Fragment, path: String) {
PermissionsCompat.Builder(fragment)
.addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage)
diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt
index 8f5549709..717ad8f11 100644
--- a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt
+++ b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt
@@ -31,7 +31,7 @@ object FilePicker {
0 -> default?.invoke()
1 -> {
try {
- val intent = getSelectDirIntent()
+ val intent = createSelectDirIntent()
activity.startActivityForResult(intent, requestCode)
} catch (e: java.lang.Exception) {
e.printStackTrace()
@@ -68,7 +68,7 @@ object FilePicker {
0 -> default?.invoke()
1 -> {
try {
- val intent = getSelectDirIntent()
+ val intent = createSelectDirIntent()
fragment.startActivityForResult(intent, requestCode)
} catch (e: java.lang.Exception) {
e.printStackTrace()
@@ -106,7 +106,7 @@ object FilePicker {
0 -> default?.invoke()
1 -> {
try {
- val intent = getSelectFileIntent()
+ val intent = createSelectFileIntent()
intent.type = type//设置类型
activity.startActivityForResult(intent, requestCode)
} catch (e: java.lang.Exception) {
@@ -147,7 +147,7 @@ object FilePicker {
0 -> default?.invoke()
1 -> {
try {
- val intent = getSelectFileIntent()
+ val intent = createSelectFileIntent()
intent.type = type//设置类型
fragment.startActivityForResult(intent, requestCode)
} catch (e: java.lang.Exception) {
@@ -168,14 +168,14 @@ object FilePicker {
}.show()
}
- private fun getSelectFileIntent(): Intent {
+ private fun createSelectFileIntent(): Intent {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
return intent
}
- private fun getSelectDirIntent(): Intent {
+ private fun createSelectDirIntent(): Intent {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
return intent
diff --git a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt
index 0d43b28c2..0aa49ede7 100644
--- a/app/src/main/java/io/legado/app/ui/main/MainActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/main/MainActivity.kt
@@ -15,7 +15,6 @@ import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig
-import io.legado.app.help.storage.Backup
import io.legado.app.lib.theme.ATH
import io.legado.app.service.BaseReadAloudService
import io.legado.app.service.help.ReadAloud
@@ -78,7 +77,7 @@ class MainActivity : VMBaseActivity(R.layout.activity_main),
putPrefInt(PreferKey.versionCode, App.INSTANCE.versionCode)
if (!BuildConfig.DEBUG) {
val log = String(assets.open("updateLog.md").readBytes())
- TextDialog.show(supportFragmentManager, log, TextDialog.MD)
+ TextDialog.show(supportFragmentManager, log, TextDialog.MD, 5000)
}
}
}
@@ -114,13 +113,6 @@ class MainActivity : VMBaseActivity(R.layout.activity_main),
return super.onKeyUp(keyCode, event)
}
- override fun finish() {
- if (!BuildConfig.DEBUG) {
- Backup.autoBack(this)
- }
- super.finish()
- }
-
override fun onDestroy() {
super.onDestroy()
ReadAloud.stop(this)
diff --git a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
index 4fe57d758..61c951ae5 100644
--- a/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/main/MainViewModel.kt
@@ -33,11 +33,9 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
updateList.remove(book.bookUrl)
postEvent(EventBus.UP_BOOK, book.bookUrl)
}
- it?.let {
- App.db.bookDao().update(book)
- App.db.bookChapterDao().delByBook(book.bookUrl)
- App.db.bookChapterDao().insert(*it.toTypedArray())
- }
+ App.db.bookDao().update(book)
+ App.db.bookChapterDao().delByBook(book.bookUrl)
+ App.db.bookChapterDao().insert(*it.toTypedArray())
}
.onError {
synchronized(this) {
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfAdapter.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfAdapter.kt
deleted file mode 100644
index 55115ad94..000000000
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfAdapter.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package io.legado.app.ui.main.bookshelf
-
-import androidx.fragment.app.Fragment
-import androidx.viewpager2.adapter.FragmentStateAdapter
-import io.legado.app.data.entities.BookGroup
-import io.legado.app.ui.main.bookshelf.books.BooksFragment
-
-
-class BookshelfAdapter(fragment: Fragment, val callBack: CallBack) :
- FragmentStateAdapter(fragment) {
-
- private val ids = hashSetOf()
-
- override fun getItemCount(): Int {
- return callBack.groupSize
- }
-
- override fun getItemId(position: Int): Long {
- return callBack.getGroup(position).groupId.toLong()
- }
-
- override fun containsItem(itemId: Long): Boolean {
- return ids.contains(itemId)
- }
-
- override fun createFragment(position: Int): Fragment {
- val groupId = callBack.getGroup(position).groupId
- ids.add(groupId.toLong())
- return BooksFragment.newInstance(position, groupId)
- }
-
- interface CallBack {
- val groupSize: Int
- fun getGroup(position: Int): BookGroup
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
index 3608a79ed..55dca8d77 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfFragment.kt
@@ -7,10 +7,12 @@ import android.view.Menu
import android.view.MenuItem
import android.view.View
import androidx.appcompat.widget.SearchView
+import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentManager
+import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import com.google.android.material.tabs.TabLayout
-import com.google.android.material.tabs.TabLayoutMediator
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.VMBaseFragment
@@ -29,6 +31,7 @@ import io.legado.app.ui.book.download.DownloadActivity
import io.legado.app.ui.book.group.GroupManageDialog
import io.legado.app.ui.book.local.ImportBookActivity
import io.legado.app.ui.book.search.SearchActivity
+import io.legado.app.ui.main.bookshelf.books.BooksFragment
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.dialog_bookshelf_config.view.*
@@ -42,15 +45,14 @@ import org.jetbrains.anko.startActivity
class BookshelfFragment : VMBaseFragment(R.layout.fragment_bookshelf),
TabLayout.OnTabSelectedListener,
SearchView.OnQueryTextListener,
- GroupManageDialog.CallBack,
- BookshelfAdapter.CallBack {
+ GroupManageDialog.CallBack {
override val viewModel: BookshelfViewModel
get() = getViewModel(BookshelfViewModel::class.java)
- private lateinit var bookshelfAdapter: BookshelfAdapter
private var bookGroupLiveData: LiveData>? = null
private val bookGroups = mutableListOf()
+ private val fragmentMap = hashMapOf()
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
setSupportToolbar(toolbar)
@@ -79,26 +81,17 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b
}
}
- override val groupSize: Int
- get() = bookGroups.size
-
- override fun getGroup(position: Int): BookGroup {
- return bookGroups[position]
- }
-
private val selectedGroup: BookGroup
get() = bookGroups[view_pager_bookshelf.currentItem]
private fun initView() {
+ ATH.applyEdgeEffectColor(view_pager_bookshelf)
tab_layout.isTabIndicatorFullWidth = false
tab_layout.tabMode = TabLayout.MODE_SCROLLABLE
tab_layout.setSelectedTabIndicatorColor(requireContext().accentColor)
- ATH.applyEdgeEffectColor(view_pager_bookshelf)
- bookshelfAdapter = BookshelfAdapter(this, this)
- view_pager_bookshelf.adapter = bookshelfAdapter
- TabLayoutMediator(tab_layout, view_pager_bookshelf) { tab, position ->
- tab.text = bookGroups[position].groupName
- }.attach()
+ tab_layout.setupWithViewPager(view_pager_bookshelf)
+ view_pager_bookshelf.offscreenPageLimit = 1
+ view_pager_bookshelf.adapter = TabFragmentPageAdapter(childFragmentManager)
}
private fun initBookGroupData() {
@@ -119,7 +112,7 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b
bookGroups.add(AppConst.bookGroupAudio)
}
bookGroups.addAll(it)
- bookshelfAdapter.notifyDataSetChanged()
+ view_pager_bookshelf.adapter?.notifyDataSetChanged()
tab_layout.getTabAt(getPrefInt(PreferKey.saveTabPosition, 0))?.select()
tab_layout.addOnTabSelectedListener(this)
}
@@ -149,7 +142,7 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b
if (AppConfig.bookGroupAllShow) {
bookGroups.add(0, AppConst.bookGroupAll)
}
- bookshelfAdapter.notifyDataSetChanged()
+ view_pager_bookshelf.adapter?.notifyDataSetChanged()
}
}
@@ -212,4 +205,31 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b
putPrefInt(PreferKey.saveTabPosition, it)
}
}
+
+ private inner class TabFragmentPageAdapter internal constructor(fm: FragmentManager) :
+ FragmentStatePagerAdapter(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
+
+ override fun getPageTitle(position: Int): CharSequence? {
+ return bookGroups[position].groupName
+ }
+
+ override fun getItemPosition(`object`: Any): Int {
+ return POSITION_NONE
+ }
+
+ override fun getItem(position: Int): Fragment {
+ val group = bookGroups[position]
+ var fragment = fragmentMap[group.groupId]
+ if (fragment == null) {
+ fragment = BooksFragment.newInstance(position, group.groupId)
+ fragmentMap[group.groupId] = fragment
+ }
+ return fragment
+ }
+
+ override fun getCount(): Int {
+ return bookGroups.size
+ }
+
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
index 87d2b3e06..2415db1be 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt
@@ -43,10 +43,8 @@ class BookshelfViewModel(application: Application) : BaseViewModel(application)
)
WebBook(bookSource).getBookInfo(book, this)
.onSuccess(IO) {
- it?.let { book ->
- App.db.bookDao().insert(book)
- successCount++
- }
+ App.db.bookDao().insert(it)
+ successCount++
}.onError {
throw Exception(it.localizedMessage)
}
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt
index d647afd3b..1438c4507 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksAdapterGrid.kt
@@ -21,7 +21,6 @@ class BooksAdapterGrid(context: Context, private val callBack: CallBack) :
if (bundle == null) {
ATH.applyBackgroundTint(this)
tv_name.text = item.name
- bv_author.text = item.author
iv_cover.load(item.getDisplayCover(), item.name, item.author)
if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) {
bv_unread.invisible()
@@ -35,7 +34,6 @@ class BooksAdapterGrid(context: Context, private val callBack: CallBack) :
bundle.keySet().map {
when (it) {
"name" -> tv_name.text = item.name
- "author" -> bv_author.text = item.author
"cover" -> iv_cover.load(item.getDisplayCover(), item.name, item.author)
"refresh" -> if (item.origin != BookType.local && callBack.isUpdate(item.bookUrl)) {
bv_unread.invisible()
diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt
index 43375288e..d82480ca2 100644
--- a/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/books/BooksFragment.kt
@@ -7,6 +7,7 @@ import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseFragment
@@ -26,6 +27,7 @@ import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.observeEvent
import kotlinx.android.synthetic.main.fragment_books.*
import org.jetbrains.anko.startActivity
+import kotlin.math.max
class BooksFragment : BaseFragment(R.layout.fragment_books),
@@ -71,9 +73,26 @@ class BooksFragment : BaseFragment(R.layout.fragment_books),
booksAdapter = BooksAdapterList(requireContext(), this)
} else {
rv_bookshelf.layoutManager = GridLayoutManager(context, bookshelfLayout + 2)
- booksAdapter = BooksAdapterGrid(requireContext(),this)
+ booksAdapter = BooksAdapterGrid(requireContext(), this)
}
rv_bookshelf.adapter = booksAdapter
+ booksAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
+ override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
+ val layoutManager = rv_bookshelf.layoutManager
+ if (positionStart == 0 && layoutManager is LinearLayoutManager) {
+ val scrollTo = layoutManager.findFirstVisibleItemPosition() - itemCount
+ rv_bookshelf.scrollToPosition(max(0, scrollTo))
+ }
+ }
+
+ override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
+ val layoutManager = rv_bookshelf.layoutManager
+ if (toPosition == 0 && layoutManager is LinearLayoutManager) {
+ val scrollTo = layoutManager.findFirstVisibleItemPosition() - itemCount
+ rv_bookshelf.scrollToPosition(max(0, scrollTo))
+ }
+ }
+ })
}
private fun upRecyclerData() {
diff --git a/app/src/main/java/io/legado/app/ui/main/explore/ExploreAdapter.kt b/app/src/main/java/io/legado/app/ui/main/explore/ExploreAdapter.kt
index 3ceeca8b6..ccfa5bf82 100644
--- a/app/src/main/java/io/legado/app/ui/main/explore/ExploreAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/main/explore/ExploreAdapter.kt
@@ -12,6 +12,7 @@ import io.legado.app.data.entities.BookSource
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.lib.theme.accentColor
import io.legado.app.utils.ACache
+import io.legado.app.utils.dp
import io.legado.app.utils.gone
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.item_fillet_text.view.*
@@ -23,16 +24,21 @@ import org.jetbrains.anko.sdk27.listeners.onLongClick
class ExploreAdapter(context: Context, private val scope: CoroutineScope, val callBack: CallBack) :
SimpleRecyclerAdapter(context, R.layout.item_find_book) {
- private var exIndex = 0
+ private var exIndex = -1
private var scrollTo = -1
override fun convert(holder: ItemViewHolder, item: BookSource, payloads: MutableList) {
with(holder.itemView) {
+ if (holder.layoutPosition == getActualItemCount() - 1) {
+ setPadding(16.dp, 12.dp, 16.dp, 12.dp)
+ } else {
+ setPadding(16.dp, 12.dp, 16.dp, 0)
+ }
if (payloads.isEmpty()) {
tv_name.text = item.bookSourceName
}
if (exIndex == holder.layoutPosition) {
- iv_status.setImageResource(R.drawable.ic_remove)
+ iv_status.setImageResource(R.drawable.ic_arrow_down)
rotate_loading.loadingColor = context.accentColor
rotate_loading.show()
if (scrollTo >= 0) {
@@ -68,7 +74,7 @@ class ExploreAdapter(context: Context, private val scope: CoroutineScope, val ca
}
}
} else {
- iv_status.setImageResource(R.drawable.ic_add)
+ iv_status.setImageResource(R.drawable.ic_arrow_right)
rotate_loading.hide()
gl_child.gone()
}
diff --git a/app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt b/app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt
index 7dcecaf11..f3087f5e7 100644
--- a/app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/main/my/MyFragment.kt
@@ -24,6 +24,7 @@ import io.legado.app.ui.config.ConfigActivity
import io.legado.app.ui.config.ConfigViewModel
import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.replacerule.ReplaceRuleActivity
+import io.legado.app.ui.widget.dialog.TextDialog
import io.legado.app.ui.widget.prefs.NameListPreference
import io.legado.app.ui.widget.prefs.PreferenceCategory
import io.legado.app.ui.widget.prefs.SwitchPreference
@@ -48,9 +49,10 @@ class MyFragment : BaseFragment(R.layout.fragment_my_config), FileChooserDialog.
override fun onCompatOptionsItemSelected(item: MenuItem) {
when (item.itemId) {
- R.id.menu_help -> startActivity()
- R.id.menu_backup -> BackupRestoreUi.backup(this)
- R.id.menu_restore -> BackupRestoreUi.restore(this)
+ R.id.menu_help -> {
+ val text = String(requireContext().assets.open("help.md").readBytes())
+ TextDialog.show(childFragmentManager, text, TextDialog.MD)
+ }
}
}
diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt
index 8f0e848f0..ec75e78ed 100644
--- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleViewModel.kt
@@ -8,7 +8,6 @@ import io.legado.app.R
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.ReplaceRule
import io.legado.app.help.http.HttpHelper
-import io.legado.app.help.storage.Backup
import io.legado.app.help.storage.ImportOldData
import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers
@@ -108,7 +107,7 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
FileUtils.createFileIfNotExist(file, "exportReplaceRule.json")
.writeText(json)
}.onSuccess {
- context.toast("成功导出至\n${Backup.exportPath}")
+ context.toast("成功导出至\n${file.absolutePath}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}
@@ -121,7 +120,7 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
doc.createFile("", "exportReplaceRule.json")
?.writeText(context, json)
}.onSuccess {
- context.toast("成功导出至\n${Backup.exportPath}")
+ context.toast("成功导出至\n${doc.uri.path}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}
diff --git a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt
index ccfff44df..61a2b14be 100644
--- a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesViewModel.kt
@@ -44,8 +44,8 @@ class RssArticlesViewModel(application: Application) : BaseViewModel(application
rssSource?.let { rssSource ->
Rss.getArticles(rssSource, null)
.onSuccess(IO) {
- nextPageUrl = it?.nextPageUrl
- it?.articles?.let { list ->
+ nextPageUrl = it.nextPageUrl
+ it.articles.let { list ->
list.forEach { rssArticle ->
rssArticle.order = order--
}
@@ -76,8 +76,8 @@ class RssArticlesViewModel(application: Application) : BaseViewModel(application
if (source != null && !pageUrl.isNullOrEmpty()) {
Rss.getArticles(source, pageUrl)
.onSuccess(IO) {
- nextPageUrl = it?.nextPageUrl
- it?.articles?.let { list ->
+ nextPageUrl = it.nextPageUrl
+ it.articles.let { list ->
if (list.isEmpty()) {
callBack?.loadFinally(false)
return@let
diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
index 79ea599d1..73c55961b 100644
--- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
@@ -22,7 +22,6 @@ import org.apache.commons.text.StringEscapeUtils
import org.jetbrains.anko.share
import org.jetbrains.anko.toast
import org.jsoup.Jsoup
-import org.jsoup.safety.Whitelist
class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_read),
@@ -194,7 +193,7 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
}
override fun upStarMenu() {
- if (viewModel.star) {
+ if (viewModel.rssStar != null) {
starMenuItem?.setIcon(R.drawable.ic_star)
starMenuItem?.setTitle(R.string.in_favorites)
} else {
@@ -246,18 +245,15 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
@SuppressLint("SetJavaScriptEnabled")
private fun readAloud() {
- if (viewModel.textToSpeech.isSpeaking) {
- viewModel.textToSpeech.stop()
+ if (viewModel.textToSpeech?.isSpeaking == true) {
+ viewModel.textToSpeech?.stop()
upTtsMenu(false)
} else {
web_view.settings.javaScriptEnabled = true
web_view.evaluateJavascript("document.documentElement.outerHTML") {
val html = StringEscapeUtils.unescapeJson(it)
- val text = Jsoup.clean(html, Whitelist.none())
- .replace(Regex("""&\w+;"""), "")
- .trim()//朗读过程中总是听到一些杂音,清理一下
- //longToast(需读内容)调试一下
- viewModel.readAloud(text)
+ Jsoup.parse(html).text()
+ viewModel.readAloud(Jsoup.parse(html).textArray())
}
}
}
diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
index 6d90fd181..f5b84642f 100644
--- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssViewModel.kt
@@ -15,6 +15,7 @@ import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppConst
import io.legado.app.data.entities.RssArticle
import io.legado.app.data.entities.RssSource
+import io.legado.app.data.entities.RssStar
import io.legado.app.help.http.HttpHelper
import io.legado.app.model.Rss
import io.legado.app.model.analyzeRule.AnalyzeUrl
@@ -22,6 +23,7 @@ import io.legado.app.utils.DocumentUtils
import io.legado.app.utils.FileUtils
import io.legado.app.utils.isContentPath
import io.legado.app.utils.writeBytes
+import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch
import java.io.File
import java.util.*
@@ -34,8 +36,10 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application),
var rssArticle: RssArticle? = null
val contentLiveData = MutableLiveData()
val urlLiveData = MutableLiveData()
- var star = false
- var textToSpeech: TextToSpeech = TextToSpeech(context, this)
+ var rssStar: RssStar? = null
+ var textToSpeech: TextToSpeech? = null
+ private var ttsInitFinish = false
+ private var ttsTextList = arrayListOf()
fun initData(intent: Intent) {
execute {
@@ -43,8 +47,8 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application),
val link = intent.getStringExtra("link")
if (origin != null && link != null) {
rssSource = App.db.rssSourceDao().getByKey(origin)
- star = App.db.rssStarDao().get(origin, link) != null
- rssArticle = App.db.rssArticleDao().get(origin, link)
+ rssStar = App.db.rssStarDao().get(origin, link)
+ rssArticle = rssStar?.toRssArticle() ?: App.db.rssArticleDao().get(origin, link)
rssArticle?.let { rssArticle ->
if (!rssArticle.description.isNullOrBlank()) {
contentLiveData.postValue(rssArticle.description)
@@ -76,21 +80,26 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application),
}
private fun loadContent(rssArticle: RssArticle, ruleContent: String) {
- Rss.getContent(rssArticle, ruleContent, this)
- .onSuccess {
- contentLiveData.postValue(it)
+ Rss.getContent(rssArticle, ruleContent, rssSource, this)
+ .onSuccess(IO) { body ->
+ rssArticle.description = body
+ App.db.rssArticleDao().insert(rssArticle)
+ rssStar?.let {
+ it.description = body
+ App.db.rssStarDao().insert(it)
+ }
+ contentLiveData.postValue(body)
}
}
fun favorite() {
execute {
- rssArticle?.let {
- if (star) {
- App.db.rssStarDao().delete(it.origin, it.link)
- } else {
- App.db.rssStarDao().insert(it.toStar())
- }
- star = !star
+ rssStar?.let {
+ App.db.rssStarDao().delete(it.origin, it.link)
+ rssStar = null
+ } ?: rssArticle?.toStar()?.let {
+ App.db.rssStarDao().insert(it)
+ rssStar = it
}
}.onSuccess {
callBack?.upStarMenu()
@@ -143,28 +152,43 @@ class ReadRssViewModel(application: Application) : BaseViewModel(application),
}
}
+ @Synchronized
override fun onInit(status: Int) {
- launch {
- if (status == TextToSpeech.SUCCESS) {
- textToSpeech.language = Locale.CHINA
- textToSpeech.setOnUtteranceProgressListener(TTSUtteranceListener())
- } else {
+ if (status == TextToSpeech.SUCCESS) {
+ textToSpeech?.language = Locale.CHINA
+ textToSpeech?.setOnUtteranceProgressListener(TTSUtteranceListener())
+ ttsInitFinish = true
+ play()
+ } else {
+ launch {
toast(R.string.tts_init_failed)
}
}
}
- fun readAloud(text: String) {
- textToSpeech.stop()
- text.split("\n", " ", " ").forEach {
- textToSpeech.speak(it, TextToSpeech.QUEUE_ADD, null, "rss")
+ @Synchronized
+ private fun play() {
+ if (!ttsInitFinish) return
+ textToSpeech?.stop()
+ ttsTextList.forEach {
+ textToSpeech?.speak(it, TextToSpeech.QUEUE_ADD, null, "rss")
+ }
+ }
+
+ fun readAloud(textArray: Array) {
+ ttsTextList.clear()
+ ttsTextList.addAll(textArray)
+ textToSpeech?.let {
+ play()
+ } ?: let {
+ textToSpeech = TextToSpeech(context, this)
}
}
override fun onCleared() {
super.onCleared()
- textToSpeech.stop()
- textToSpeech.shutdown()
+ textToSpeech?.stop()
+ textToSpeech?.shutdown()
}
/**
diff --git a/app/src/main/java/io/legado/app/ui/rss/read/VisibleWebView.kt b/app/src/main/java/io/legado/app/ui/rss/read/VisibleWebView.kt
new file mode 100644
index 000000000..0e6e1b40b
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/rss/read/VisibleWebView.kt
@@ -0,0 +1,17 @@
+package io.legado.app.ui.rss.read
+
+import android.content.Context
+import android.util.AttributeSet
+import android.view.View
+import android.webkit.WebView
+
+class VisibleWebView(
+ context: Context,
+ attrs: AttributeSet? = null
+) : WebView(context, attrs) {
+
+ override fun onWindowVisibilityChanged(visibility: Int) {
+ super.onWindowVisibilityChanged(View.VISIBLE)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt
index ed09cec93..ce2313f5a 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt
@@ -24,10 +24,7 @@ import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.widget.recycler.VerticalDivider
-import io.legado.app.utils.applyTint
-import io.legado.app.utils.getViewModelOfActivity
-import io.legado.app.utils.requestInputMethod
-import io.legado.app.utils.splitNotBlank
+import io.legado.app.utils.*
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_group_manage.view.*
@@ -68,6 +65,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
tv_ok.setTextColor(requireContext().accentColor)
+ tv_ok.visible()
tv_ok.onClick { dismiss() }
App.db.rssSourceDao().liveGroup().observe(viewLifecycleOwner, Observer {
val groups = linkedSetOf()
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt
index 9160155b3..300d7468c 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceViewModel.kt
@@ -9,7 +9,6 @@ import io.legado.app.R
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.RssSource
import io.legado.app.help.http.HttpHelper
-import io.legado.app.help.storage.Backup
import io.legado.app.help.storage.Restore.jsonPath
import io.legado.app.utils.*
import org.jetbrains.anko.toast
@@ -74,7 +73,7 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application)
FileUtils.createFileIfNotExist(file, "exportRssSource.json")
.writeText(json)
}.onSuccess {
- context.toast("成功导出至\n${Backup.exportPath}")
+ context.toast("成功导出至\n${file.absolutePath}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}
@@ -87,7 +86,7 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application)
doc.createFile("", "exportRssSource.json")
?.writeText(context, json)
}.onSuccess {
- context.toast("成功导出至\n${Backup.exportPath}")
+ context.toast("成功导出至\n${doc.uri.path}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}
@@ -187,7 +186,7 @@ class RssSourceViewModel(application: Application) : BaseViewModel(application)
}.onError {
finally(it.localizedMessage ?: "")
}.onSuccess {
- finally(it ?: "导入完成")
+ finally(it)
}
}
diff --git a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt
index 46e1e446e..fdf77bc85 100644
--- a/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt
@@ -58,4 +58,7 @@ open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) {
class Launcher1 : WelcomeActivity()
class Launcher2 : WelcomeActivity()
-class Launcher3 : WelcomeActivity()
\ No newline at end of file
+class Launcher3 : WelcomeActivity()
+class Launcher4 : WelcomeActivity()
+class Launcher5 : WelcomeActivity()
+class Launcher6 : WelcomeActivity()
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/widget/SearchView.kt b/app/src/main/java/io/legado/app/ui/widget/SearchView.kt
new file mode 100644
index 000000000..765a0a580
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/widget/SearchView.kt
@@ -0,0 +1,110 @@
+package io.legado.app.ui.widget
+
+import android.app.SearchableInfo
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.drawable.Drawable
+import android.text.Spannable
+import android.text.SpannableStringBuilder
+import android.text.style.ImageSpan
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.Gravity
+import android.widget.TextView
+import androidx.appcompat.widget.SearchView
+import io.legado.app.R
+
+class SearchView : SearchView {
+ private var mSearchHintIcon: Drawable? = null
+ private var textView: TextView? = null
+
+ constructor(
+ context: Context?,
+ attrs: AttributeSet? = null
+ ) : super(context, attrs)
+
+ constructor(
+ context: Context?,
+ attrs: AttributeSet?,
+ defStyleAttr: Int
+ ) : super(context, attrs, defStyleAttr)
+
+ override fun onLayout(
+ changed: Boolean,
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int
+ ) {
+ super.onLayout(changed, left, top, right, bottom)
+ try {
+ if (textView == null) {
+ textView = findViewById(androidx.appcompat.R.id.search_src_text)
+ mSearchHintIcon = this.context.getDrawable(R.drawable.ic_search_hint)
+ updateQueryHint()
+ }
+ // 改变字体
+ textView!!.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
+ textView!!.gravity = Gravity.CENTER_VERTICAL
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+
+ private fun getDecoratedHint(hintText: CharSequence): CharSequence {
+ // If the field is always expanded or we don't have a search hint icon,
+ // then don't add the search icon to the hint.
+ if (mSearchHintIcon == null) {
+ return hintText
+ }
+ val textSize = (textView!!.textSize * 0.8).toInt()
+ mSearchHintIcon!!.setBounds(0, 0, textSize, textSize)
+ val ssb = SpannableStringBuilder(" ")
+ ssb.setSpan(CenteredImageSpan(mSearchHintIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
+ ssb.append(hintText)
+ return ssb
+ }
+
+ private fun updateQueryHint() {
+ textView?.let {
+ it.hint = getDecoratedHint(queryHint ?: "")
+ }
+ }
+
+ override fun setIconifiedByDefault(iconified: Boolean) {
+ super.setIconifiedByDefault(iconified)
+ updateQueryHint()
+ }
+
+ override fun setSearchableInfo(searchable: SearchableInfo?) {
+ super.setSearchableInfo(searchable)
+ searchable?.let {
+ updateQueryHint()
+ }
+ }
+
+ override fun setQueryHint(hint: CharSequence?) {
+ super.setQueryHint(hint)
+ updateQueryHint()
+ }
+
+ internal class CenteredImageSpan(drawable: Drawable?) : ImageSpan(drawable!!) {
+ override fun draw(
+ canvas: Canvas, text: CharSequence,
+ start: Int, end: Int, x: Float,
+ top: Int, y: Int, bottom: Int, paint: Paint
+ ) {
+ // image to draw
+ val b = drawable
+ // font metrics of text to be replaced
+ val fm = paint.fontMetricsInt
+ val transY = ((y + fm.descent + y + fm.ascent) / 2
+ - b.bounds.bottom / 2)
+ canvas.save()
+ canvas.translate(x, transY.toFloat())
+ b.draw(canvas)
+ canvas.restore()
+ }
+ }
+}
diff --git a/app/src/main/java/io/legado/app/ui/widget/ShadowLayout.kt b/app/src/main/java/io/legado/app/ui/widget/ShadowLayout.kt
new file mode 100644
index 000000000..0f6ad3200
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/widget/ShadowLayout.kt
@@ -0,0 +1,185 @@
+package io.legado.app.ui.widget
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.RectF
+import android.util.AttributeSet
+import android.view.View
+import android.widget.RelativeLayout
+import io.legado.app.R
+import io.legado.app.utils.getCompatColor
+
+/**
+ * ShadowLayout.java
+ *
+ *
+ * Created by lijiankun on 17/8/11.
+ */
+class ShadowLayout(
+ context: Context,
+ attrs: AttributeSet? = null
+) : RelativeLayout(context, attrs) {
+ private val mPaint =
+ Paint(Paint.ANTI_ALIAS_FLAG)
+ private val mRectF = RectF()
+
+ /**
+ * 阴影的颜色
+ */
+ private var mShadowColor = Color.TRANSPARENT
+
+ /**
+ * 阴影的大小范围
+ */
+ private var mShadowRadius = 0f
+
+ /**
+ * 阴影 x 轴的偏移量
+ */
+ private var mShadowDx = 0f
+
+ /**
+ * 阴影 y 轴的偏移量
+ */
+ private var mShadowDy = 0f
+
+ /**
+ * 阴影显示的边界
+ */
+ private var mShadowSide = ALL
+
+ /**
+ * 阴影的形状,圆形/矩形
+ */
+ private var mShadowShape = SHAPE_RECTANGLE
+
+
+ init {
+ setLayerType(View.LAYER_TYPE_SOFTWARE, null) // 关闭硬件加速
+ setWillNotDraw(false) // 调用此方法后,才会执行 onDraw(Canvas) 方法
+ val typedArray =
+ context.obtainStyledAttributes(attrs, R.styleable.ShadowLayout)
+ mShadowColor = typedArray.getColor(
+ R.styleable.ShadowLayout_shadowColor,
+ context.getCompatColor(android.R.color.black)
+ )
+ mShadowRadius =
+ typedArray.getDimension(R.styleable.ShadowLayout_shadowRadius, dip2px(0f))
+ mShadowDx = typedArray.getDimension(R.styleable.ShadowLayout_shadowDx, dip2px(0f))
+ mShadowDy = typedArray.getDimension(R.styleable.ShadowLayout_shadowDy, dip2px(0f))
+ mShadowSide =
+ typedArray.getInt(R.styleable.ShadowLayout_shadowSide, ALL)
+ mShadowShape = typedArray.getInt(
+ R.styleable.ShadowLayout_shadowShape,
+ SHAPE_RECTANGLE
+ )
+ typedArray.recycle()
+
+ setUpShadowPaint()
+ }
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ val effect = mShadowRadius + dip2px(5f)
+ var rectLeft = 0f
+ var rectTop = 0f
+ var rectRight = this.measuredWidth.toFloat()
+ var rectBottom = this.measuredHeight.toFloat()
+ var paddingLeft = 0
+ var paddingTop = 0
+ var paddingRight = 0
+ var paddingBottom = 0
+ this.width
+ if (mShadowSide and LEFT == LEFT) {
+ rectLeft = effect
+ paddingLeft = effect.toInt()
+ }
+ if (mShadowSide and TOP == TOP) {
+ rectTop = effect
+ paddingTop = effect.toInt()
+ }
+ if (mShadowSide and RIGHT == RIGHT) {
+ rectRight = this.measuredWidth - effect
+ paddingRight = effect.toInt()
+ }
+ if (mShadowSide and BOTTOM == BOTTOM) {
+ rectBottom = this.measuredHeight - effect
+ paddingBottom = effect.toInt()
+ }
+ if (mShadowDy != 0.0f) {
+ rectBottom -= mShadowDy
+ paddingBottom += mShadowDy.toInt()
+ }
+ if (mShadowDx != 0.0f) {
+ rectRight -= mShadowDx
+ paddingRight += mShadowDx.toInt()
+ }
+ mRectF.left = rectLeft
+ mRectF.top = rectTop
+ mRectF.right = rectRight
+ mRectF.bottom = rectBottom
+ setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom)
+ }
+
+ /**
+ * 真正绘制阴影的方法
+ */
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+ setUpShadowPaint()
+ if (mShadowShape == SHAPE_RECTANGLE) {
+ canvas.drawRect(mRectF, mPaint)
+ } else if (mShadowShape == SHAPE_OVAL) {
+ canvas.drawCircle(
+ mRectF.centerX(),
+ mRectF.centerY(),
+ mRectF.width().coerceAtMost(mRectF.height()) / 2,
+ mPaint
+ )
+ }
+ }
+
+ fun setShadowColor(shadowColor: Int) {
+ mShadowColor = shadowColor
+ requestLayout()
+ postInvalidate()
+ }
+
+ fun setShadowRadius(shadowRadius: Float) {
+ mShadowRadius = shadowRadius
+ requestLayout()
+ postInvalidate()
+ }
+
+ private fun setUpShadowPaint() {
+ mPaint.reset()
+ mPaint.isAntiAlias = true
+ mPaint.color = Color.TRANSPARENT
+ mPaint.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor)
+ }
+
+ /**
+ * dip2px dp 值转 px 值
+ *
+ * @param dpValue dp 值
+ * @return px 值
+ */
+ private fun dip2px(dpValue: Float): Float {
+ val dm = context.resources.displayMetrics
+ val scale = dm.density
+ return dpValue * scale + 0.5f
+ }
+
+ companion object {
+ const val ALL = 0x1111
+ const val LEFT = 0x0001
+ const val TOP = 0x0010
+ const val RIGHT = 0x0100
+ const val BOTTOM = 0x1000
+ const val SHAPE_RECTANGLE = 0x0001
+ const val SHAPE_OVAL = 0x0010
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt b/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt
index 663bb55a8..94d3b7be6 100644
--- a/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt
@@ -2,6 +2,7 @@ package io.legado.app.ui.widget
import android.content.Context
import android.content.res.ColorStateList
+import android.graphics.Color
import android.util.AttributeSet
import android.view.Menu
import android.view.View
@@ -186,6 +187,11 @@ class TitleBar(context: Context, attrs: AttributeSet?) : AppBarLayout(context, a
toolbar.setSubtitleTextAppearance(context, resId)
}
+ fun transparent() {
+ elevation = 0f
+ backgroundColor = Color.TRANSPARENT
+ }
+
private fun attachToActivity() {
if (attachToActivity) {
activity?.let {
diff --git a/app/src/main/java/io/legado/app/ui/widget/dialog/TextDialog.kt b/app/src/main/java/io/legado/app/ui/widget/dialog/TextDialog.kt
index 01a55f2da..9c1c75d5e 100644
--- a/app/src/main/java/io/legado/app/ui/widget/dialog/TextDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/dialog/TextDialog.kt
@@ -5,29 +5,40 @@ import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import io.legado.app.R
+import io.legado.app.base.BaseDialogFragment
import kotlinx.android.synthetic.main.dialog_text_view.*
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
import ru.noties.markwon.Markwon
-class TextDialog : DialogFragment() {
+class TextDialog : BaseDialogFragment() {
companion object {
const val MD = 1
- fun show(fragmentManager: FragmentManager, content: String?, mode: Int = 0) {
+ fun show(
+ fragmentManager: FragmentManager,
+ content: String?,
+ mode: Int = 0,
+ time: Long = 0
+ ) {
TextDialog().apply {
val bundle = Bundle()
bundle.putString("content", content)
bundle.putInt("mode", mode)
+ bundle.putLong("time", time)
arguments = bundle
+ isCancelable = false
}.show(fragmentManager, "textDialog")
}
}
+ private var time = 0L
+
override fun onStart() {
super.onStart()
val dm = DisplayMetrics()
@@ -43,8 +54,7 @@ class TextDialog : DialogFragment() {
return inflater.inflate(R.layout.dialog_text_view, container)
}
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
+ override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
arguments?.let {
val content = it.getString("content") ?: ""
when (it.getInt("mode")) {
@@ -57,8 +67,27 @@ class TextDialog : DialogFragment() {
}
else -> text_view.text = content
}
+ time = it.getLong("time", 0L)
+ }
+ if (time > 0) {
+ badge_view.setBadgeCount((time / 1000).toInt())
+ launch {
+ while (time > 0) {
+ delay(1000)
+ time -= 1000
+ badge_view.setBadgeCount((time / 1000).toInt())
+ if (time <= 0) {
+ view.post {
+ dialog?.setCancelable(true)
+ }
+ }
+ }
+ }
+ } else {
+ view.post {
+ dialog?.setCancelable(true)
+ }
}
-
}
}
diff --git a/app/src/main/java/io/legado/app/ui/widget/prefs/ColorPreference.kt b/app/src/main/java/io/legado/app/ui/widget/prefs/ColorPreference.kt
index 6bea75a51..101b6678a 100644
--- a/app/src/main/java/io/legado/app/ui/widget/prefs/ColorPreference.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/prefs/ColorPreference.kt
@@ -37,6 +37,8 @@ class ColorPreference(context: Context, attrs: AttributeSet) : Preference(contex
init {
isPersistent = true
+ layoutResource = io.legado.app.R.layout.view_preference
+
val a = context.obtainStyledAttributes(attrs, R.styleable.ColorPreference)
showDialog = a.getBoolean(R.styleable.ColorPreference_cpv_showDialog, true)
@@ -109,9 +111,12 @@ class ColorPreference(context: Context, attrs: AttributeSet) : Preference(contex
}
override fun onBindViewHolder(holder: PreferenceViewHolder) {
+ val v = io.legado.app.ui.widget.prefs.Preference.bindView(context, holder, icon, title, summary, widgetLayoutResource,
+ io.legado.app.R.id.cpv_preference_preview_color_panel, 30, 30)
+ if (v is ColorPanelView) {
+ v.color = color
+ }
super.onBindViewHolder(holder)
- val preview = holder.itemView.findViewById(R.id.cpv_preference_preview_color_panel) as ColorPanelView
- preview.color = color
}
override fun onSetInitialValue(defaultValue: Any?) {
diff --git a/app/src/main/java/io/legado/app/ui/widget/prefs/EditTextPreference.kt b/app/src/main/java/io/legado/app/ui/widget/prefs/EditTextPreference.kt
new file mode 100644
index 000000000..8c0e39da3
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/widget/prefs/EditTextPreference.kt
@@ -0,0 +1,21 @@
+package io.legado.app.ui.widget.prefs
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.TextView
+import androidx.preference.PreferenceViewHolder
+import io.legado.app.R
+
+class EditTextPreference(context: Context, attrs: AttributeSet) : androidx.preference.EditTextPreference(context, attrs) {
+
+ init {
+ // isPersistent = true
+ layoutResource = R.layout.view_preference
+ }
+
+ override fun onBindViewHolder(holder: PreferenceViewHolder?) {
+ Preference.bindView(context, holder, icon, title, summary, null, null)
+ super.onBindViewHolder(holder)
+ }
+
+}
diff --git a/app/src/main/java/io/legado/app/ui/widget/prefs/IconListPreference.kt b/app/src/main/java/io/legado/app/ui/widget/prefs/IconListPreference.kt
index b295e5ad3..fa8b4eeb7 100644
--- a/app/src/main/java/io/legado/app/ui/widget/prefs/IconListPreference.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/prefs/IconListPreference.kt
@@ -29,6 +29,7 @@ class IconListPreference(context: Context, attrs: AttributeSet) : ListPreference
private val mEntryDrawables = arrayListOf()
init {
+ layoutResource = R.layout.view_preference
widgetLayoutResource = R.layout.view_icon
val a = context.theme.obtainStyledAttributes(attrs, R.styleable.IconListPreference, 0, 0)
@@ -52,11 +53,12 @@ class IconListPreference(context: Context, attrs: AttributeSet) : ListPreference
override fun onBindViewHolder(holder: PreferenceViewHolder?) {
super.onBindViewHolder(holder)
- holder?.itemView?.findViewById(R.id.preview)?.let {
+ val v = Preference.bindView(context, holder, icon, title, summary, widgetLayoutResource, R.id.preview, 50, 50)
+ if (v is ImageView) {
val selectedIndex = findIndexOfValue(value)
if (selectedIndex >= 0) {
val drawable = mEntryDrawables[selectedIndex]
- it.setImageDrawable(drawable)
+ v.setImageDrawable(drawable)
}
}
}
diff --git a/app/src/main/java/io/legado/app/ui/widget/prefs/NameListPreference.kt b/app/src/main/java/io/legado/app/ui/widget/prefs/NameListPreference.kt
index d8b51e90f..0763c8474 100644
--- a/app/src/main/java/io/legado/app/ui/widget/prefs/NameListPreference.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/prefs/NameListPreference.kt
@@ -11,12 +11,15 @@ import io.legado.app.R
class NameListPreference(context: Context, attrs: AttributeSet) : ListPreference(context, attrs) {
init {
+ layoutResource = R.layout.view_preference
widgetLayoutResource = R.layout.item_fillet_text
}
override fun onBindViewHolder(holder: PreferenceViewHolder?) {
+ val v = Preference.bindView(context, holder, icon, title, summary, widgetLayoutResource, R.id.text_view)
+ if (v is TextView) {
+ v.text = entry
+ }
super.onBindViewHolder(holder)
- val textView = holder?.itemView?.findViewById(R.id.text_view)
- textView?.text = entry
}
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/widget/prefs/Preference.kt b/app/src/main/java/io/legado/app/ui/widget/prefs/Preference.kt
new file mode 100644
index 000000000..0190dcd9f
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/widget/prefs/Preference.kt
@@ -0,0 +1,85 @@
+package io.legado.app.ui.widget.prefs
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.core.view.isVisible
+import androidx.preference.PreferenceViewHolder
+import io.legado.app.R
+import io.legado.app.lib.theme.accentColor
+import org.jetbrains.anko.layoutInflater
+import kotlin.math.roundToInt
+
+class Preference(context: Context, attrs: AttributeSet) : androidx.preference.Preference(context, attrs) {
+
+ init {
+ // isPersistent = true
+ layoutResource = R.layout.view_preference
+ }
+
+ companion object {
+
+ fun bindView(context: Context, it: PreferenceViewHolder?, icon: Drawable?, title: CharSequence?, summary: CharSequence?, weightLayoutRes: Int?, viewId: Int?,
+ weightWidth: Int = 0, weightHeight: Int = 0): T? {
+ if (it == null) return null
+ val view = it.findViewById(R.id.preference_title)
+ if (view is TextView) {
+ view.text = title
+ view.isVisible = title != null && title.isNotEmpty()
+
+ val tvSummary = it.findViewById(R.id.preference_desc)
+ if (tvSummary is TextView) {
+ tvSummary.text = summary
+ tvSummary.isVisible = summary != null && summary.isNotEmpty()
+ }
+
+ val iconView = it.findViewById(R.id.preference_icon)
+ if (iconView is ImageView) {
+ iconView.isVisible = icon != null && icon.isVisible
+ iconView.setImageDrawable(icon)
+ iconView.setColorFilter(context.accentColor)
+ }
+ }
+
+ if (weightLayoutRes != null && weightLayoutRes != 0 && viewId != null && viewId != 0) {
+ val lay = it.findViewById(R.id.preference_widget)
+ if (lay is FrameLayout) {
+ var v = it.itemView.findViewById(viewId)
+ if (v == null) {
+ val inflater: LayoutInflater = context.layoutInflater
+ val childView = inflater.inflate(weightLayoutRes, null)
+ lay.removeAllViews()
+ lay.addView(childView)
+ lay.isVisible = true
+ v = lay.findViewById(viewId)
+ }
+
+ if (weightWidth > 0 || weightHeight > 0) {
+ val lp = lay.layoutParams
+ if (weightHeight > 0)
+ lp.height = (context.resources.displayMetrics.density * weightHeight).roundToInt()
+ if (weightWidth > 0)
+ lp.width = (context.resources.displayMetrics.density * weightWidth).roundToInt()
+ lay.layoutParams = lp
+ }
+
+ return v
+ }
+ }
+
+ return null
+ }
+
+ }
+
+ override fun onBindViewHolder(holder: PreferenceViewHolder?) {
+ bindView(context, holder, icon, title, summary, null, null)
+ super.onBindViewHolder(holder)
+ }
+
+}
diff --git a/app/src/main/java/io/legado/app/ui/widget/prefs/PreferenceCategory.kt b/app/src/main/java/io/legado/app/ui/widget/prefs/PreferenceCategory.kt
index e1458b76e..a7dcd2e6c 100644
--- a/app/src/main/java/io/legado/app/ui/widget/prefs/PreferenceCategory.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/prefs/PreferenceCategory.kt
@@ -1,22 +1,41 @@
package io.legado.app.ui.widget.prefs
import android.content.Context
+import android.graphics.Color
import android.util.AttributeSet
+import android.view.View
import android.widget.TextView
+import androidx.core.view.isVisible
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceViewHolder
+import io.legado.app.R
import io.legado.app.lib.theme.accentColor
-class PreferenceCategory(context: Context, attrs: AttributeSet) :
- PreferenceCategory(context, attrs) {
+class PreferenceCategory(context: Context, attrs: AttributeSet) : PreferenceCategory(context, attrs) {
+
+ init {
+ isPersistent = true
+ layoutResource = R.layout.view_preference_category
+ }
override fun onBindViewHolder(holder: PreferenceViewHolder?) {
super.onBindViewHolder(holder)
holder?.let {
- val view = it.findViewById(android.R.id.title)
- if (view is TextView && !view.isInEditMode) {
- view.setTextColor(context.accentColor)//设置title文本的颜色
+ val view = it.findViewById(R.id.preference_title)
+ if (view is TextView) { // && !view.isInEditMode
+ view.text = title
+ view.setTextColor(context.accentColor) //设置title文本的颜色
+ view.isVisible = title != null && title.isNotEmpty()
+
+ val da = it.findViewById(R.id.preference_divider_above)
+ if (da is View) {
+ da.isVisible = it.isDividerAllowedAbove
+ }
+ val db = it.findViewById(R.id.preference_divider_below)
+ if (db is View) {
+ db.isVisible = it.isDividerAllowedBelow
+ }
}
}
}
diff --git a/app/src/main/java/io/legado/app/ui/widget/prefs/SwitchPreference.kt b/app/src/main/java/io/legado/app/ui/widget/prefs/SwitchPreference.kt
index 4fe66af7b..2f6becfc2 100644
--- a/app/src/main/java/io/legado/app/ui/widget/prefs/SwitchPreference.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/prefs/SwitchPreference.kt
@@ -12,14 +12,24 @@ import io.legado.app.lib.theme.accentColor
class SwitchPreference(context: Context, attrs: AttributeSet) :
SwitchPreferenceCompat(context, attrs) {
+ init {
+ layoutResource = R.layout.view_preference
+ }
+
override fun onBindViewHolder(holder: PreferenceViewHolder?) {
- super.onBindViewHolder(holder)
- holder?.let {
- val view = it.findViewById(R.id.switchWidget)
- if (view is SwitchCompat) {
- ATH.setTint(view, context.accentColor)
- }
+ val v = Preference.bindView(
+ context,
+ holder,
+ icon,
+ title,
+ summary,
+ widgetLayoutResource,
+ R.id.switchWidget
+ )
+ if (v is SwitchCompat) {
+ ATH.setTint(v, context.accentColor)
}
+ super.onBindViewHolder(holder)
}
}
diff --git a/app/src/main/java/io/legado/app/utils/DocumentUtils.kt b/app/src/main/java/io/legado/app/utils/DocumentUtils.kt
index f4fd0e8eb..b53cebb14 100644
--- a/app/src/main/java/io/legado/app/utils/DocumentUtils.kt
+++ b/app/src/main/java/io/legado/app/utils/DocumentUtils.kt
@@ -98,7 +98,7 @@ object DocumentUtils {
DocumentsContract.Document.COLUMN_LAST_MODIFIED,
DocumentsContract.Document.COLUMN_SIZE,
DocumentsContract.Document.COLUMN_MIME_TYPE
- ), null, null, null
+ ), null, null, DocumentsContract.Document.COLUMN_DISPLAY_NAME
)
c?.let {
val ici = c.getColumnIndex(DocumentsContract.Document.COLUMN_DOCUMENT_ID)
diff --git a/app/src/main/java/io/legado/app/utils/JsoupExtensions.kt b/app/src/main/java/io/legado/app/utils/JsoupExtensions.kt
new file mode 100644
index 000000000..3f347e4a1
--- /dev/null
+++ b/app/src/main/java/io/legado/app/utils/JsoupExtensions.kt
@@ -0,0 +1,66 @@
+package io.legado.app.utils
+
+import org.jsoup.internal.StringUtil
+import org.jsoup.nodes.CDataNode
+import org.jsoup.nodes.Element
+import org.jsoup.nodes.Node
+import org.jsoup.nodes.TextNode
+import org.jsoup.select.NodeTraversor
+import org.jsoup.select.NodeVisitor
+
+
+fun Element.textArray(): Array {
+ val accum = StringUtil.borrowBuilder()
+ NodeTraversor.traverse(object : NodeVisitor {
+ override fun head(node: Node, depth: Int) {
+ if (node is TextNode) {
+ appendNormalisedText(accum, node)
+ } else if (node is Element) {
+ if (accum.isNotEmpty() &&
+ (node.isBlock || node.tag().name == "br") &&
+ !lastCharIsWhitespace(accum)
+ ) accum.append("\n")
+ }
+ }
+
+ override fun tail(node: Node, depth: Int) {
+ if (node is Element) {
+ if (node.isBlock && node.nextSibling() is TextNode && !lastCharIsWhitespace(
+ accum
+ )
+ ) accum.append("\n")
+ }
+ }
+ }, this)
+ val text = StringUtil.releaseBuilder(accum).trim { it <= ' ' }
+ return text.splitNotBlank("\n")
+}
+
+private fun appendNormalisedText(accum: StringBuilder, textNode: TextNode) {
+ val text = textNode.wholeText
+ if (preserveWhitespace(textNode.parentNode()) || textNode is CDataNode)
+ accum.append(text)
+ else StringUtil.appendNormalisedWhitespace(
+ accum,
+ text,
+ lastCharIsWhitespace(accum)
+ )
+}
+
+private fun preserveWhitespace(node: Node?): Boolean {
+ if (node is Element) {
+ var el = node as Element?
+ var i = 0
+ do {
+ if (el!!.tag().preserveWhitespace()) return true
+ el = el.parent()
+ i++
+ } while (i < 6 && el != null)
+ }
+ return false
+}
+
+private fun lastCharIsWhitespace(sb: java.lang.StringBuilder): Boolean {
+ return sb.isNotEmpty() && sb[sb.length - 1] == ' '
+}
+
diff --git a/app/src/main/java/io/legado/app/utils/ViewExtensions.kt b/app/src/main/java/io/legado/app/utils/ViewExtensions.kt
index f0334504e..e37539851 100644
--- a/app/src/main/java/io/legado/app/utils/ViewExtensions.kt
+++ b/app/src/main/java/io/legado/app/utils/ViewExtensions.kt
@@ -6,6 +6,7 @@ import android.graphics.Canvas
import android.os.Build
import android.view.View
import android.view.View.*
+import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.RadioGroup
import android.widget.SeekBar
@@ -75,6 +76,13 @@ fun View.screenshot(): Bitmap? {
}.getOrNull()
}
+fun View.setMargin(left: Int, top: Int, right: Int, bottom: Int) {
+ if (layoutParams is ViewGroup.MarginLayoutParams) {
+ (layoutParams as ViewGroup.MarginLayoutParams).setMargins(left, top, right, bottom)
+ requestLayout()
+ }
+}
+
fun SeekBar.progressAdd(int: Int) {
progress += int
}
diff --git a/app/src/main/res/drawable-v24/ic_launcher_2.xml b/app/src/main/res/drawable-v24/ic_launcher_2.xml
deleted file mode 100644
index b4343492c..000000000
--- a/app/src/main/res/drawable-v24/ic_launcher_2.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable-v24/ic_launcher_2b.xml b/app/src/main/res/drawable-v24/ic_launcher_2b.xml
deleted file mode 100644
index be66fb2a7..000000000
--- a/app/src/main/res/drawable-v24/ic_launcher_2b.xml
+++ /dev/null
@@ -1,56 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/bg_find_book_group.xml b/app/src/main/res/drawable/bg_find_book_group.xml
new file mode 100644
index 000000000..b00a89b22
--- /dev/null
+++ b/app/src/main/res/drawable/bg_find_book_group.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_prefs_color.xml b/app/src/main/res/drawable/bg_prefs_color.xml
new file mode 100644
index 000000000..ff4a5de4e
--- /dev/null
+++ b/app/src/main/res/drawable/bg_prefs_color.xml
@@ -0,0 +1,10 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_searchview.xml b/app/src/main/res/drawable/bg_searchview.xml
new file mode 100644
index 000000000..beee4af1e
--- /dev/null
+++ b/app/src/main/res/drawable/bg_searchview.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_arrow_down.xml b/app/src/main/res/drawable/ic_arrow_down.xml
new file mode 100644
index 000000000..19a41e8db
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_down.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_arrow_right.xml b/app/src/main/res/drawable/ic_arrow_right.xml
new file mode 100644
index 000000000..72bcbdb79
--- /dev/null
+++ b/app/src/main/res/drawable/ic_arrow_right.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_bottom_books.xml b/app/src/main/res/drawable/ic_bottom_books.xml
new file mode 100644
index 000000000..807aaaf30
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bottom_books.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_bottom_books_e.xml b/app/src/main/res/drawable/ic_bottom_books_e.xml
new file mode 100644
index 000000000..04d36c375
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bottom_books_e.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_bottom_books_s.xml b/app/src/main/res/drawable/ic_bottom_books_s.xml
new file mode 100644
index 000000000..8f5f72542
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bottom_books_s.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_bottom_explore.xml b/app/src/main/res/drawable/ic_bottom_explore.xml
new file mode 100644
index 000000000..439f69ba9
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bottom_explore.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_bottom_explore_e.xml b/app/src/main/res/drawable/ic_bottom_explore_e.xml
new file mode 100644
index 000000000..47e4bde9d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bottom_explore_e.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_bottom_explore_s.xml b/app/src/main/res/drawable/ic_bottom_explore_s.xml
new file mode 100644
index 000000000..6dc6e4322
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bottom_explore_s.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_bottom_person.xml b/app/src/main/res/drawable/ic_bottom_person.xml
new file mode 100644
index 000000000..50053997a
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bottom_person.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_bottom_person_e.xml b/app/src/main/res/drawable/ic_bottom_person_e.xml
new file mode 100644
index 000000000..d01128391
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bottom_person_e.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_bottom_person_s.xml b/app/src/main/res/drawable/ic_bottom_person_s.xml
new file mode 100644
index 000000000..3ff7fc8c4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bottom_person_s.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_bottom_rss_feed.xml b/app/src/main/res/drawable/ic_bottom_rss_feed.xml
new file mode 100644
index 000000000..4cd080b7f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bottom_rss_feed.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_bottom_rss_feed_e.xml b/app/src/main/res/drawable/ic_bottom_rss_feed_e.xml
new file mode 100644
index 000000000..c1c4ba0e3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bottom_rss_feed_e.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_bottom_rss_feed_s.xml b/app/src/main/res/drawable/ic_bottom_rss_feed_s.xml
new file mode 100644
index 000000000..a73c01430
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bottom_rss_feed_s.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_cfg_about.xml b/app/src/main/res/drawable/ic_cfg_about.xml
new file mode 100644
index 000000000..42e3d30d3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cfg_about.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_cfg_backup.xml b/app/src/main/res/drawable/ic_cfg_backup.xml
new file mode 100644
index 000000000..9c647c009
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cfg_backup.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_cfg_e_lnk.xml b/app/src/main/res/drawable/ic_cfg_e_lnk.xml
new file mode 100644
index 000000000..b94a8eb4d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cfg_e_lnk.xml
@@ -0,0 +1,5 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_cfg_jz.xml b/app/src/main/res/drawable/ic_cfg_jz.xml
new file mode 100644
index 000000000..710bae7fd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cfg_jz.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_cfg_other.xml b/app/src/main/res/drawable/ic_cfg_other.xml
new file mode 100644
index 000000000..a3d26b642
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cfg_other.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_cfg_replace.xml b/app/src/main/res/drawable/ic_cfg_replace.xml
new file mode 100644
index 000000000..53c9d1d04
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cfg_replace.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_cfg_source.xml b/app/src/main/res/drawable/ic_cfg_source.xml
new file mode 100644
index 000000000..e71bb109d
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cfg_source.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_cfg_theme.xml b/app/src/main/res/drawable/ic_cfg_theme.xml
new file mode 100644
index 000000000..2fe949d19
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cfg_theme.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_cfg_web.xml b/app/src/main/res/drawable/ic_cfg_web.xml
new file mode 100644
index 000000000..7a34ae004
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cfg_web.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_explore_black_24dp.xml b/app/src/main/res/drawable/ic_explore_black_24dp.xml
deleted file mode 100644
index d841826b0..000000000
--- a/app/src/main/res/drawable/ic_explore_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_help.xml b/app/src/main/res/drawable/ic_help.xml
new file mode 100644
index 000000000..32838437b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_help.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launch.xml b/app/src/main/res/drawable/ic_launch.xml
deleted file mode 100644
index 6b423cdc7..000000000
--- a/app/src/main/res/drawable/ic_launch.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_launcher1.xml b/app/src/main/res/drawable/ic_launcher1.xml
new file mode 100644
index 000000000..18c829634
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher1.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher1_b.xml b/app/src/main/res/drawable/ic_launcher1_b.xml
new file mode 100644
index 000000000..c5b40eb91
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher1_b.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable-v24/ic_launcher_1.xml b/app/src/main/res/drawable/ic_launcher2.xml
similarity index 100%
rename from app/src/main/res/drawable-v24/ic_launcher_1.xml
rename to app/src/main/res/drawable/ic_launcher2.xml
diff --git a/app/src/main/res/drawable-v24/ic_launcher_3.xml b/app/src/main/res/drawable/ic_launcher3.xml
similarity index 100%
rename from app/src/main/res/drawable-v24/ic_launcher_3.xml
rename to app/src/main/res/drawable/ic_launcher3.xml
diff --git a/app/src/main/res/drawable-v24/ic_launcher_0.xml b/app/src/main/res/drawable/ic_launcher4.xml
similarity index 100%
rename from app/src/main/res/drawable-v24/ic_launcher_0.xml
rename to app/src/main/res/drawable/ic_launcher4.xml
diff --git a/app/src/main/res/drawable/ic_launcher5.xml b/app/src/main/res/drawable/ic_launcher5.xml
new file mode 100644
index 000000000..29435836b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher5.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher5_b.xml b/app/src/main/res/drawable/ic_launcher5_b.xml
new file mode 100644
index 000000000..9a5153369
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher5_b.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher6.xml b/app/src/main/res/drawable/ic_launcher6.xml
new file mode 100644
index 000000000..1bb711f8f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher6.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher7.xml b/app/src/main/res/drawable/ic_launcher7.xml
new file mode 100644
index 000000000..983784aef
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher7.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_launcher7_b.xml b/app/src/main/res/drawable/ic_launcher7_b.xml
new file mode 100644
index 000000000..76214f1e4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher7_b.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_library_books_black_24dp.xml b/app/src/main/res/drawable/ic_library_books_black_24dp.xml
deleted file mode 100644
index 06deda209..000000000
--- a/app/src/main/res/drawable/ic_library_books_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_live_help_black_24dp.xml b/app/src/main/res/drawable/ic_live_help_black_24dp.xml
deleted file mode 100644
index 8b9c1ac46..000000000
--- a/app/src/main/res/drawable/ic_live_help_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_person_black_24dp.xml b/app/src/main/res/drawable/ic_person_black_24dp.xml
deleted file mode 100644
index 9f1acc792..000000000
--- a/app/src/main/res/drawable/ic_person_black_24dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
diff --git a/app/src/main/res/drawable/ic_rss_feed.xml b/app/src/main/res/drawable/ic_rss_feed.xml
deleted file mode 100644
index ed6228cc2..000000000
--- a/app/src/main/res/drawable/ic_rss_feed.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/drawable/ic_search_hint.xml b/app/src/main/res/drawable/ic_search_hint.xml
new file mode 100644
index 000000000..42de64c26
--- /dev/null
+++ b/app/src/main/res/drawable/ic_search_hint.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/shape_fillet_btn.xml b/app/src/main/res/drawable/shape_fillet_btn.xml
index 10dac7541..83ccf2776 100644
--- a/app/src/main/res/drawable/shape_fillet_btn.xml
+++ b/app/src/main/res/drawable/shape_fillet_btn.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_fillet_btn_press.xml b/app/src/main/res/drawable/shape_fillet_btn_press.xml
index b569081a4..b0c46f6f5 100644
--- a/app/src/main/res/drawable/shape_fillet_btn_press.xml
+++ b/app/src/main/res/drawable/shape_fillet_btn_press.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout-land/activity_book_info.xml b/app/src/main/res/layout-land/activity_book_info.xml
index 27e0bec0d..1dc7d789c 100644
--- a/app/src/main/res/layout-land/activity_book_info.xml
+++ b/app/src/main/res/layout-land/activity_book_info.xml
@@ -14,13 +14,10 @@
android:contentDescription="@string/bg_image"
app:layout_constraintTop_toTopOf="parent" />
-
@@ -81,7 +78,8 @@
android:paddingLeft="10dp"
android:paddingTop="8dp"
android:paddingRight="16dp"
- android:paddingBottom="3dp">
+ android:paddingBottom="3dp"
+ tools:ignore="RtlHardcoded">
+ android:scrollbars="vertical"
+ tools:ignore="NestedWeights">
+ tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry" />
+ tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry" />
@@ -290,7 +288,7 @@
android:text="@string/group_s"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
- tools:ignore="NestedWeights" />
+ tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry" />
+ tools:ignore="NestedWeights,RtlHardcoded,RtlSymmetry" />
+ android:background="@color/background" />
-
+ app:layout_constraintTop_toBottomOf="@+id/title_bar" />
-
diff --git a/app/src/main/res/layout/activity_import_book.xml b/app/src/main/res/layout/activity_import_book.xml
index e93a5b6c6..38d0b4f55 100644
--- a/app/src/main/res/layout/activity_import_book.xml
+++ b/app/src/main/res/layout/activity_import_book.xml
@@ -1,20 +1,23 @@
-
+ android:layout_height="wrap_content"
+ app:layout_constraintBottom_toBottomOf="parent" />
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 9f2d2044e..701dea9b5 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -7,10 +7,10 @@
diff --git a/app/src/main/res/layout/activity_rss_read.xml b/app/src/main/res/layout/activity_rss_read.xml
index 76f630002..ea4131764 100644
--- a/app/src/main/res/layout/activity_rss_read.xml
+++ b/app/src/main/res/layout/activity_rss_read.xml
@@ -15,7 +15,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content" />
-
diff --git a/app/src/main/res/layout/dialog_read_aloud.xml b/app/src/main/res/layout/dialog_read_aloud.xml
index 627bd9414..504865171 100644
--- a/app/src/main/res/layout/dialog_read_aloud.xml
+++ b/app/src/main/res/layout/dialog_read_aloud.xml
@@ -1,245 +1,337 @@
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal">
-
+
+
-
-
+
+
+
+
+
+
-
+
+
+
+
+
+
-
+ android:layout_weight="1"
+ android:max="60" />
-
+
-
+
-
-
+ android:layout_gravity="center_vertical"
+ android:orientation="horizontal"
+ android:padding="8dp">
-
-
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_weight="1"
+ android:text="@string/read_aloud_speed"
+ android:textColor="@color/tv_text_default"
+ android:textSize="14sp" />
+
+
-
+ android:layout_marginStart="15dp"
+ android:layout_marginEnd="15dp"
+ android:max="45" />
+
+
+
+
+
+
+
-
-
+ android:contentDescription="@string/chapter_list"
+ android:src="@drawable/ic_toc"
+ app:tint="@color/tv_text_default"
+ tools:ignore="NestedWeights" />
-
+ 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" />
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="2" />
+
+
+
+
+
+
+
+
+
+ android:id="@+id/ll_to_backstage"
+ android:layout_width="50dp"
+ android:layout_height="50dp"
+ android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:clickable="true"
+ android:contentDescription="@string/interface_setting"
+ android:focusable="true"
+ android:orientation="vertical"
+ android:paddingBottom="7dp">
-
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+ 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" />
+
diff --git a/app/src/main/res/layout/dialog_recycler_view.xml b/app/src/main/res/layout/dialog_recycler_view.xml
index 98f67043f..5b1d900a9 100644
--- a/app/src/main/res/layout/dialog_recycler_view.xml
+++ b/app/src/main/res/layout/dialog_recycler_view.xml
@@ -15,7 +15,7 @@
android:id="@+id/recycler_view"
android:background="@color/background_card"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="0dp"
android:layout_weight="1" />
diff --git a/app/src/main/res/layout/dialog_text_view.xml b/app/src/main/res/layout/dialog_text_view.xml
index 3a2b347c3..c1a1a70a6 100644
--- a/app/src/main/res/layout/dialog_text_view.xml
+++ b/app/src/main/res/layout/dialog_text_view.xml
@@ -1,7 +1,25 @@
-
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_bookshelf.xml b/app/src/main/res/layout/fragment_bookshelf.xml
index 2eb390c9a..ea0a9ab40 100644
--- a/app/src/main/res/layout/fragment_bookshelf.xml
+++ b/app/src/main/res/layout/fragment_bookshelf.xml
@@ -14,7 +14,7 @@
app:contentLayout="@layout/view_tab_layout_min"
app:layout_constraintTop_toTopOf="parent" />
-
+ android:paddingTop="2dp"
+ android:paddingRight="10dp"
+ android:paddingLeft="10dp">
-
+ app:shadowColor="#16000000"
+ app:shadowShape="rectangle"
+ app:shadowDx="1dp"
+ app:shadowDy="3dp"
+ app:shadowRadius="4dp"
+ app:shadowSide="all">
-
-
-
+ android:scaleType="centerCrop"
+ android:src="@drawable/image_cover_default"
+ android:transitionName="img_cover"
+ tools:ignore="UnusedAttribute" />
-
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_fillet_text.xml b/app/src/main/res/layout/item_fillet_text.xml
index 331bd8a2c..dcdf75935 100644
--- a/app/src/main/res/layout/item_fillet_text.xml
+++ b/app/src/main/res/layout/item_fillet_text.xml
@@ -8,7 +8,10 @@
android:background="@drawable/selector_fillet_btn_bg"
android:ellipsize="end"
android:gravity="center"
- android:padding="5dp"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:paddingLeft="12dp"
+ android:paddingRight="12dp"
android:maxLines="1"
android:textColor="@color/tv_text_default"
android:textSize="14sp"
diff --git a/app/src/main/res/layout/item_find_book.xml b/app/src/main/res/layout/item_find_book.xml
index 8a95ed1e2..d689630d2 100644
--- a/app/src/main/res/layout/item_find_book.xml
+++ b/app/src/main/res/layout/item_find_book.xml
@@ -4,8 +4,6 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingLeft="10dp"
- android:paddingRight="10dp"
android:gravity="center"
android:orientation="vertical">
@@ -17,8 +15,7 @@
android:paddingRight="10dp"
android:paddingTop="6dp"
android:paddingBottom="6dp"
- android:layout_margin="4dp"
- android:background="@color/btn_bg_press"
+ android:background="@drawable/bg_find_book_group"
android:orientation="horizontal"
android:gravity="center_vertical">
@@ -28,11 +25,22 @@
android:layout_height="wrap_content"
android:layout_weight="1" />
+
+
@@ -41,6 +49,8 @@
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_text.xml b/app/src/main/res/layout/item_text.xml
index e37188e47..357efdde3 100644
--- a/app/src/main/res/layout/item_text.xml
+++ b/app/src/main/res/layout/item_text.xml
@@ -9,6 +9,7 @@
android:gravity="center"
android:padding="5dp"
android:maxLines="1"
+ android:scrollbars="none"
android:textColor="@color/tv_text_default"
android:textSize="14sp"
tools:ignore="UnusedAttribute" />
diff --git a/app/src/main/res/layout/popup_action_menu.xml b/app/src/main/res/layout/popup_action_menu.xml
index 98cceb33c..0f6b6e0e5 100644
--- a/app/src/main/res/layout/popup_action_menu.xml
+++ b/app/src/main/res/layout/popup_action_menu.xml
@@ -1,27 +1,46 @@
-
+ android:orientation="vertical">
-
+ android:orientation="horizontal"
+ android:gravity="center_vertical">
+
+
+
+
+
+
-
+ android:orientation="vertical"
+ app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
-
+
diff --git a/app/src/main/res/layout/view_book_page.xml b/app/src/main/res/layout/view_book_page.xml
index 922819b1e..3f09664a3 100644
--- a/app/src/main/res/layout/view_book_page.xml
+++ b/app/src/main/res/layout/view_book_page.xml
@@ -18,6 +18,8 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
+ android:maxLines="1"
+ android:ellipsize="end"
android:textSize="12sp" />
@@ -62,7 +66,8 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
- android:singleLine="true"
+ android:maxLines="1"
+ android:ellipsize="end"
android:textSize="12sp" />
diff --git a/app/src/main/res/layout/view_preference.xml b/app/src/main/res/layout/view_preference.xml
new file mode 100644
index 000000000..87b10ef5b
--- /dev/null
+++ b/app/src/main/res/layout/view_preference.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_preference_category.xml b/app/src/main/res/layout/view_preference_category.xml
new file mode 100644
index 000000000..28f4e73ff
--- /dev/null
+++ b/app/src/main/res/layout/view_preference_category.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/view_read_menu.xml b/app/src/main/res/layout/view_read_menu.xml
index 135f5b5d9..3754c0914 100644
--- a/app/src/main/res/layout/view_read_menu.xml
+++ b/app/src/main/res/layout/view_read_menu.xml
@@ -210,7 +210,7 @@
diff --git a/app/src/main/res/layout/view_search.xml b/app/src/main/res/layout/view_search.xml
index 5fd67be63..96ff311da 100644
--- a/app/src/main/res/layout/view_search.xml
+++ b/app/src/main/res/layout/view_search.xml
@@ -1,10 +1,17 @@
-
\ No newline at end of file
diff --git a/app/src/main/res/menu/content_select_action.xml b/app/src/main/res/menu/content_select_action.xml
index d11ee6154..ff83efd96 100644
--- a/app/src/main/res/menu/content_select_action.xml
+++ b/app/src/main/res/menu/content_select_action.xml
@@ -9,6 +9,10 @@
android:id="@+id/menu_copy"
android:title="@android:string/copy" />
+
+
diff --git a/app/src/main/res/menu/main_bnv.xml b/app/src/main/res/menu/main_bnv.xml
index fb34daff3..9197bec7b 100644
--- a/app/src/main/res/menu/main_bnv.xml
+++ b/app/src/main/res/menu/main_bnv.xml
@@ -4,20 +4,20 @@
tools:showIn="bottom_navigation_view">
+ android:id="@+id/menu_bookshelf"
+ android:icon="@drawable/ic_bottom_books"
+ android:title="@string/bookshelf" />
+ android:id="@+id/menu_find_book"
+ android:icon="@drawable/ic_bottom_explore"
+ android:title="@string/find" />
+ android:id="@+id/menu_my_config"
+ android:icon="@drawable/ic_bottom_person"
+ android:title="@string/my" />
diff --git a/app/src/main/res/menu/main_my.xml b/app/src/main/res/menu/main_my.xml
index db1161c28..02b6acbd0 100644
--- a/app/src/main/res/menu/main_my.xml
+++ b/app/src/main/res/menu/main_my.xml
@@ -6,20 +6,8 @@
-
-
-
-
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index e8409cf31..7a68d4392 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,5 +1,5 @@
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/launcher1.xml b/app/src/main/res/mipmap-anydpi-v26/launcher1.xml
index 57e845ad3..b3aa68018 100644
--- a/app/src/main/res/mipmap-anydpi-v26/launcher1.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/launcher1.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/launcher2.xml b/app/src/main/res/mipmap-anydpi-v26/launcher2.xml
index 409e0d965..5de236bd2 100644
--- a/app/src/main/res/mipmap-anydpi-v26/launcher2.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/launcher2.xml
@@ -1,5 +1,5 @@
-
-
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/launcher3.xml b/app/src/main/res/mipmap-anydpi-v26/launcher3.xml
index 24428243f..9e7ef6711 100644
--- a/app/src/main/res/mipmap-anydpi-v26/launcher3.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/launcher3.xml
@@ -1,5 +1,5 @@
-
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/launcher4.xml b/app/src/main/res/mipmap-anydpi-v26/launcher4.xml
new file mode 100644
index 000000000..63e51b918
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/launcher4.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/launcher5.xml b/app/src/main/res/mipmap-anydpi-v26/launcher5.xml
new file mode 100644
index 000000000..7a23c7c66
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/launcher5.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/launcher6.xml b/app/src/main/res/mipmap-anydpi-v26/launcher6.xml
new file mode 100644
index 000000000..4ba689a93
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/launcher6.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
index a7e087bc8..d2c418293 100644
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
index f0e9ad532..2d18ac346 100644
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index 371e045cd..4b44e0bba 100644
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index feff4e965..df9bfebb8 100644
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
index 63cb54502..6d101d806 100644
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml
index 18ab012e0..8ac7aa66a 100644
--- a/app/src/main/res/values-night/colors.xml
+++ b/app/src/main/res/values-night/colors.xml
@@ -7,9 +7,11 @@
@color/md_grey_900
@color/md_grey_850
@color/md_grey_800
+ #10303030
#69000000
+ #10ffffff
#30ffffff
#363636
diff --git a/app/src/main/res/values/array_values.xml b/app/src/main/res/values/array_values.xml
index c517c46ce..40bc6684e 100644
--- a/app/src/main/res/values/array_values.xml
+++ b/app/src/main/res/values/array_values.xml
@@ -6,9 +6,11 @@
- launcher1
- launcher2
- launcher3
+ - launcher4
+ - launcher5
+ - launcher6
-
- 0
- 60
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index a53546e7f..39c85cae3 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -79,6 +79,9 @@
- icon1
- icon2
- icon3
+ - icon4
+ - icon5
+ - icon6
diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml
index d5a4d11e1..9e920088b 100644
--- a/app/src/main/res/values/attrs.xml
+++ b/app/src/main/res/values/attrs.xml
@@ -173,4 +173,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index d6999a7ae..0fe64b7ca 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -16,8 +16,10 @@
@color/md_grey_50
@color/md_grey_100
@color/md_grey_200
+ @color/md_grey_50
#00000000
+ #10000000
#30000000
#d3321b
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 9f8946337..f7ecf2030 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -6,7 +6,6 @@
176dp
16dp
-
14sp
16sp
18sp
@@ -35,4 +34,6 @@
8dp
8dp
8dp
+
+ 8dp
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 84e6e6bb2..bc219ab39 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -29,6 +29,7 @@
删除
替换
替换净化
+ 配置替换净化规则
暂无
启用
替换净化-搜索
@@ -58,6 +59,7 @@
添加本地
书源
书源管理
+ 新建/导入/编辑/管理书源
设置
主题设置
与界面/颜色相关的一些设置
@@ -80,8 +82,9 @@
加载中…
重试
Web 服务
+ 启用Web服务
web编辑书源
- http://%s:%d
+ http://%1$s:%2$d
离线下载
离线下载
下载选择的章节到本地
@@ -207,14 +210,14 @@
替换规则为空或者不满足正则表达式要求
选择操作
全选
- 全选(%d/%d)
- 取消(%d/%d)
+ 全选(%1$d/%2$d)
+ 取消(%1$d/%2$d)
深色模式
启动页
开始下载
取消下载
暂无任务
- 已下载 %d/%d
+ 已下载 %1$d/%2$d
导入选择书籍
更新和搜索线程数,太多会卡顿
切换图标
@@ -225,7 +228,7 @@
内容简介
简介:%s
打开外部书籍
- 来源:%s
+ 来源: %s
本地导入
导入在线规则
检查更新间隔
@@ -289,7 +292,7 @@
右边距
校验书源
校验所选
- 进度 %d/%d
+ 进度 %1$d/%2$d
请安装并选择中文TTS!
TTS初始化失败!
简繁转换
@@ -442,6 +445,7 @@
选中时点击可弹出菜单
主题
主题模式
+ 选择主题模式
默认主题
恢复主题为默认配色
加入QQ群
@@ -457,6 +461,7 @@
导入本地书籍需存储权限
夜间模式
E-Ink 模式
+ 电子墨水屏模式
本软件需要存储权限来存储备份书籍信息
再按一次退出程序
导入本地书籍需存储权限
@@ -581,7 +586,7 @@
类型:
文本
音频
- 转到后台
+ 后台
正在导入
正在导出
自定义翻页按键
@@ -599,7 +604,7 @@
夜间模式跟随系统
上级
在线朗读音色
- (%d/%d)
+ (%1$d/%2$d)
显示订阅
服务已停止
正在启动服务\n具体信息查看通知栏
@@ -628,9 +633,8 @@
更多菜单
减
加
- 其它朗读设置
系统内置字体样式
- 是否删除源文件
+ 删除源文件
预设一
预设二
预设三
@@ -646,5 +650,8 @@
导航栏阴影
当前阴影大小(elevation): %s
默认
-
+ 主菜单
+ 点击授予权限
+ 阅读需要访问存储卡权限,请点击下方的"授予权限"按钮,或前往“设置”—“应用权限”—打开所需权限。如果授予权限后仍然不正常,请点击右上角的“选择文件夹”,使用系统文件夹选择器。
+ 全文朗读中不能朗读选中文字
diff --git a/app/src/main/res/xml/about.xml b/app/src/main/res/xml/about.xml
index 130e16e60..2970ab12b 100644
--- a/app/src/main/res/xml/about.xml
+++ b/app/src/main/res/xml/about.xml
@@ -2,68 +2,78 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/xml/donate.xml b/app/src/main/res/xml/donate.xml
index 13d448544..8732f864c 100644
--- a/app/src/main/res/xml/donate.xml
+++ b/app/src/main/res/xml/donate.xml
@@ -4,51 +4,64 @@
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
diff --git a/app/src/main/res/xml/pref_config_other.xml b/app/src/main/res/xml/pref_config_other.xml
index b049bcf22..560692986 100644
--- a/app/src/main/res/xml/pref_config_other.xml
+++ b/app/src/main/res/xml/pref_config_other.xml
@@ -4,6 +4,9 @@
-
-
-
-
diff --git a/app/src/main/res/xml/pref_config_theme.xml b/app/src/main/res/xml/pref_config_theme.xml
index 2f34e86ec..bf90dd7ab 100644
--- a/app/src/main/res/xml/pref_config_theme.xml
+++ b/app/src/main/res/xml/pref_config_theme.xml
@@ -26,21 +26,26 @@
android:title="@string/navigation_bar_color_change"
app:iconSpaceReserved="false" />
-
-
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:divider="@color/transparent"
+ app:allowDividerBelow="false"
+ app:allowDividerAbove="false"
+ >
-
-
+ app:iconSpaceReserved="false"
+ app:allowDividerAbove="true"
+ app:allowDividerBelow="false" >
-
-
-
-
-
diff --git a/build.gradle b/build.gradle
index 5eb9c16b8..839736b35 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.3.61'
+ ext.kotlin_version = '1.3.70'
repositories {
google()
jcenter()
@@ -29,4 +29,4 @@ allprojects {
task clean(type: Delete) {
delete rootProject.buildDir
-}
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 23339e0df..667087ef0 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -6,7 +6,7 @@
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx1536m
+org.gradle.jvmargs=-Xmx2048m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects