diff --git a/app/build.gradle b/app/build.gradle
index 585099975..1b0c7d4b0 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -54,6 +54,7 @@ android {
if (project.hasProperty("RELEASE_STORE_FILE")) {
signingConfig signingConfigs.myConfig
}
+ applicationIdSuffix '.release'
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
@@ -123,7 +124,7 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
//room
- def room_version = '2.2.3'
+ def room_version = '2.2.4'
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
@@ -139,7 +140,7 @@ dependencies {
implementation 'com.jeremyliao:live-event-bus-x:1.4.5'
//协程
- def coroutines_version = '1.2.2'
+ def coroutines_version = '1.3.3'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
@@ -175,7 +176,7 @@ dependencies {
//MarkDown
implementation 'ru.noties.markwon:core:3.0.2'
- // 转换繁体
+ //转换繁体
implementation 'com.github.houbb:opencc4j:1.4.0'
}
diff --git a/app/google-services.json b/app/google-services.json
index 8cc19b1d5..fb88090cb 100644
--- a/app/google-services.json
+++ b/app/google-services.json
@@ -63,6 +63,35 @@
]
}
}
+ },
+ {
+ "client_info": {
+ "mobilesdk_app_id": "1:453392274790:android:c1481c1c3d3f51eff624a7",
+ "android_client_info": {
+ "package_name": "io.legado.app.release"
+ }
+ },
+ "oauth_client": [
+ {
+ "client_id": "453392274790-hnbpatpce9hbjiggj76hgo7queu86atq.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ],
+ "api_key": [
+ {
+ "current_key": "AIzaSyD90mfNLhA7cAzzI9SonpSz5mrF5BnmyJA"
+ }
+ ],
+ "services": {
+ "appinvite_service": {
+ "other_platform_oauth_client": [
+ {
+ "client_id": "453392274790-hnbpatpce9hbjiggj76hgo7queu86atq.apps.googleusercontent.com",
+ "client_type": 3
+ }
+ ]
+ }
+ }
}
],
"configuration_version": "1"
diff --git a/app/src/debug/google-services.json b/app/src/debug/google-services.json
deleted file mode 100644
index 8cc19b1d5..000000000
--- a/app/src/debug/google-services.json
+++ /dev/null
@@ -1,69 +0,0 @@
-{
- "project_info": {
- "project_number": "453392274790",
- "firebase_url": "https://legado-fca69.firebaseio.com",
- "project_id": "legado-fca69",
- "storage_bucket": "legado-fca69.appspot.com"
- },
- "client": [
- {
- "client_info": {
- "mobilesdk_app_id": "1:453392274790:android:1d2b1eefbe0e78cff624a7",
- "android_client_info": {
- "package_name": "io.legado.app"
- }
- },
- "oauth_client": [
- {
- "client_id": "453392274790-hnbpatpce9hbjiggj76hgo7queu86atq.apps.googleusercontent.com",
- "client_type": 3
- }
- ],
- "api_key": [
- {
- "current_key": "AIzaSyD90mfNLhA7cAzzI9SonpSz5mrF5BnmyJA"
- }
- ],
- "services": {
- "appinvite_service": {
- "other_platform_oauth_client": [
- {
- "client_id": "453392274790-hnbpatpce9hbjiggj76hgo7queu86atq.apps.googleusercontent.com",
- "client_type": 3
- }
- ]
- }
- }
- },
- {
- "client_info": {
- "mobilesdk_app_id": "1:453392274790:android:c4eac14b1410eec5f624a7",
- "android_client_info": {
- "package_name": "io.legado.app.debug"
- }
- },
- "oauth_client": [
- {
- "client_id": "453392274790-hnbpatpce9hbjiggj76hgo7queu86atq.apps.googleusercontent.com",
- "client_type": 3
- }
- ],
- "api_key": [
- {
- "current_key": "AIzaSyD90mfNLhA7cAzzI9SonpSz5mrF5BnmyJA"
- }
- ],
- "services": {
- "appinvite_service": {
- "other_platform_oauth_client": [
- {
- "client_id": "453392274790-hnbpatpce9hbjiggj76hgo7queu86atq.apps.googleusercontent.com",
- "client_type": 3
- }
- ]
- }
- }
- }
- ],
- "configuration_version": "1"
-}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index a8ebd70da..e4bc75603 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -123,9 +123,6 @@
-
@@ -135,24 +132,51 @@
-
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
diff --git a/app/src/main/assets/txtTocRule.json b/app/src/main/assets/txtTocRule.json
index 4e93e04d7..1304059d0 100644
--- a/app/src/main/assets/txtTocRule.json
+++ b/app/src/main/assets/txtTocRule.json
@@ -1,62 +1,68 @@
[
- {
- "enable": true,
- "name": "数字 分隔符 标题名称",
- "rule": "^[ \\t]{0,4}\\d{1,5}[\\,\\., 、\\-].{1,30}$",
- "serialNumber": 0
- },
- {
- "enable": true,
- "name": "目录",
- "rule": "^[ \\t]{0,4}(?:(?:内容|文章)?简介|前言|序章|楔子|正文(?!完)|[Cc]hapter|[Ss]ection|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
- "serialNumber": 1
- },
- {
- "enable": false,
- "name": "目录(不匹配行前空白)",
- "rule": "^(?<=\\s)(?:(?:内容|文章)?简介|前言|序章|楔子|正文(?!完)|[Cc]hapter|[Ss]ection|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
- "serialNumber": 2
- },
- {
- "enable": false,
- "name": "目录(去简介)",
- "rule": "^(?<=\\s)(?:前言|序章|楔子|正文(?!完)|[Cc]hapter|[Ss]ection|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
- "serialNumber": 3
- },
- {
- "enable": false,
- "name": "目录(古典小说备用)",
- "rule": "^[ \\t]{0,4}(?:前言|序章|楔子|正文(?!完)|[Cc]hapter|[Ss]ection|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|场(?![和合比电是])|篇(?!张))).{0,30}$",
- "serialNumber": 4
- },
- {
- "enable": true,
- "name": "Chapter/Section/Part 序号 标题",
- "rule": "^[ \\t]{0,4}(?:[Cc]hapter|[Ss]ection|[Pp]art)\\s{0,4}\\d{1,4}.{0,30}$",
- "serialNumber": 5
- },
- {
- "enable": true,
- "name": "正文 标题/序号",
- "rule": "^[ \\t]{0,4}正文\\s{1,4}.{0,20}$",
- "serialNumber": 6
- },
- {
- "enable": true,
- "name": "特殊符号 序号 标题",
- "rule": "^[ \\t]{0,4}[〈〖〔【][第卷][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10}[章节][\\.:: \f\t].{0,30}$",
- "serialNumber": 7
- },
- {
- "enable": true,
- "name": "特殊符号 标题",
- "rule": "^[ \\t]{0,4}[〈〖〔【☆★].{1,30}[】〕〗〉]?\\s{0,4}$",
- "serialNumber": 8
- },
- {
- "enable":false,
- "name": "特殊符号 标题(不匹配空白字符)",
- "rule": "(?<=\\s)[〈〖〔【☆★].{1,30}[】〕〗〉]?\\s{0,4}$",
- "serialNumber": 9
- }
+ {
+ "enable": true,
+ "name": "目录",
+ "rule": "^[ \\t]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
+ "serialNumber": 0
+ },
+ {
+ "enable": false,
+ "name": "目录(不匹配行前空白)",
+ "rule": "^(?<=\\s)(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
+ "serialNumber": 1
+ },
+ {
+ "enable": false,
+ "name": "目录(去简介)",
+ "rule": "^(?<=\\s)(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
+ "serialNumber": 2
+ },
+ {
+ "enable": false,
+ "name": "目录(古典、轻小说备用)",
+ "rule": "^[ \\t]{0,4}(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$",
+ "serialNumber": 3
+ },
+ {
+ "enable": true,
+ "name": "数字 分隔符 标题名称",
+ "rule": "^[ \\t]{0,4}\\d{1,5}[\\,\\., 、\\-].{1,30}$",
+ "serialNumber": 4
+ },
+ {
+ "enable": true,
+ "name": "正文 标题/序号",
+ "rule": "^[ \\t]{0,4}正文\\s{1,4}.{0,20}$",
+ "serialNumber": 5
+ },
+ {
+ "enable": true,
+ "name": "Chapter/Section/Part 序号 标题",
+ "rule": "^[ \\t]{0,4}(?:[Cc]hapter|[Ss]ection|[Pp]art)\\s{0,4}\\d{1,4}.{0,30}$",
+ "serialNumber": 6
+ },
+ {
+ "enable": true,
+ "name": "特殊符号 序号 标题",
+ "rule": "^[ \\t]{0,4}[〈〖〔【][第卷][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10}[章节][\\.:: \f\t].{0,30}$",
+ "serialNumber": 7
+ },
+ {
+ "enable": true,
+ "name": "特殊符号 标题",
+ "rule": "^[ \\t]{0,4}[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$",
+ "serialNumber": 8
+ },
+ {
+ "enable":false,
+ "name": "特殊符号 标题(不匹配空白字符)",
+ "rule": "^(?<=\\s)[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$",
+ "serialNumber": 9
+ },
+ {
+ "enable":false,
+ "name": "顶格标题",
+ "rule": "^\\S.{1,20}$",
+ "serialNumber": 10
+ }
]
diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md
index b9379ed02..aabd6c581 100644
--- a/app/src/main/assets/updateLog.md
+++ b/app/src/main/assets/updateLog.md
@@ -2,6 +2,28 @@
* 旧版数据导入教程:
* 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
+**2020/02/29**
+* 添加书源一键导入
+* 修复主题模式跟随系统
+* 修复书源校验
+* 添加书架排序
+* 添加点击翻页开关
+* 修复共用布局没有记住配置的bug
+
+**2020/02/28**
+* 解决阅读界面部分字体超出范围的问题
+* 修复背景切换有时空白的bug
+* 修复滚动翻页问题
+
+**2020/02/27**
+* 修复bug,边距调节,换源等一些bug,记不清了
+* 修复默认字体问题
+* 改了下包名,好上架应用市场
+
+**2020/02/26**
+* 修复仿真翻页
+* 功能添加: 选择默认字体时, 可选择字体默认字体(非衬线), 系统衬线字体, 系统等宽字体by hingbong
+
**2020/02/25**
* 优化文本选择和滚动,感觉很完美了
diff --git a/app/src/main/java/io/legado/app/App.kt b/app/src/main/java/io/legado/app/App.kt
index 7310dcef8..cf0f4cf43 100644
--- a/app/src/main/java/io/legado/app/App.kt
+++ b/app/src/main/java/io/legado/app/App.kt
@@ -5,6 +5,7 @@ import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
+import android.content.res.Configuration
import android.os.Build
import android.os.Bundle
import androidx.annotation.RequiresApi
@@ -62,6 +63,13 @@ class App : Application() {
registerActivityLife()
}
+ override fun onConfigurationChanged(newConfig: Configuration) {
+ super.onConfigurationChanged(newConfig)
+ when (newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK) {
+ Configuration.UI_MODE_NIGHT_YES, Configuration.UI_MODE_NIGHT_NO -> applyDayNight()
+ }
+ }
+
/**
* 更新主题
*/
@@ -91,7 +99,6 @@ class App : Application() {
)
.apply()
}
-// ChapterProvider.upReadAloudSpan()
}
fun applyDayNight() {
diff --git a/app/src/main/java/io/legado/app/base/BaseDialogFragment.kt b/app/src/main/java/io/legado/app/base/BaseDialogFragment.kt
index 8bb119625..e893e34c4 100644
--- a/app/src/main/java/io/legado/app/base/BaseDialogFragment.kt
+++ b/app/src/main/java/io/legado/app/base/BaseDialogFragment.kt
@@ -2,6 +2,7 @@ package io.legado.app.base
import android.os.Bundle
import androidx.fragment.app.DialogFragment
+import io.legado.app.help.coroutine.Coroutine
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@@ -21,4 +22,12 @@ abstract class BaseDialogFragment : DialogFragment(), CoroutineScope {
super.onDestroy()
job.cancel()
}
+
+ fun execute(
+ scope: CoroutineScope = this,
+ context: CoroutineContext = Dispatchers.IO,
+ block: suspend CoroutineScope.() -> T
+ ): Coroutine {
+ return Coroutine.async(scope, context) { block() }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/constant/AppConst.kt b/app/src/main/java/io/legado/app/constant/AppConst.kt
index 3a5b42efe..96cc7567f 100644
--- a/app/src/main/java/io/legado/app/constant/AppConst.kt
+++ b/app/src/main/java/io/legado/app/constant/AppConst.kt
@@ -22,7 +22,7 @@ object AppConst {
const val UA_NAME = "User-Agent"
val userAgent: String by lazy {
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36"
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36"
}
val SCRIPT_ENGINE: ScriptEngine by lazy {
diff --git a/app/src/main/java/io/legado/app/constant/PreferKey.kt b/app/src/main/java/io/legado/app/constant/PreferKey.kt
index 50ab1c873..72d8c6b15 100644
--- a/app/src/main/java/io/legado/app/constant/PreferKey.kt
+++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt
@@ -5,6 +5,7 @@ object PreferKey {
const val themeMode = "themeMode"
const val downloadPath = "downloadPath"
const val hideStatusBar = "hideStatusBar"
+ const val clickTurnPage = "clickTurnPage"
const val clickAllNext = "clickAllNext"
const val hideNavigationBar = "hideNavigationBar"
const val precisionSearch = "precisionSearch"
@@ -16,6 +17,7 @@ object PreferKey {
const val nextKey = "nextKeyCode"
const val showRss = "showRss"
const val bookshelfLayout = "bookshelfLayout"
+ const val bookshelfSort = "bookshelfSort"
const val recordLog = "recordLog"
const val processText = "process_text"
const val cleanCache = "cleanCache"
@@ -38,4 +40,5 @@ object PreferKey {
const val bodyIndent = "textIndent"
const val shareLayout = "shareLayout"
const val readStyleSelect = "readStyleSelect"
+ const val systemTypefaces = "system_typefaces"
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/data/entities/BaseBook.kt b/app/src/main/java/io/legado/app/data/entities/BaseBook.kt
index 0c96c2123..62d322b6c 100644
--- a/app/src/main/java/io/legado/app/data/entities/BaseBook.kt
+++ b/app/src/main/java/io/legado/app/data/entities/BaseBook.kt
@@ -3,6 +3,7 @@ package io.legado.app.data.entities
import io.legado.app.utils.splitNotBlank
interface BaseBook {
+ var bookUrl: String
var variableMap: HashMap?
var kind: String?
var wordCount: String?
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 d01919a94..d29ce696e 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
@@ -17,7 +17,7 @@ import kotlin.math.max
@Entity(tableName = "books", indices = [(Index(value = ["bookUrl"], unique = true))])
data class Book(
@PrimaryKey
- var bookUrl: String = "", // 详情页Url(本地书源存储完整文件路径)
+ override var bookUrl: String = "", // 详情页Url(本地书源存储完整文件路径)
var tocUrl: String = "", // 目录页Url (toc=table of Contents)
var origin: String = BookType.local, // 书源URL(默认BookType.local)
var originName: String = "", //书源名称 or 本地书籍文件名
diff --git a/app/src/main/java/io/legado/app/data/entities/SearchBook.kt b/app/src/main/java/io/legado/app/data/entities/SearchBook.kt
index 2baf259e4..27f666547 100644
--- a/app/src/main/java/io/legado/app/data/entities/SearchBook.kt
+++ b/app/src/main/java/io/legado/app/data/entities/SearchBook.kt
@@ -21,7 +21,7 @@ import kotlinx.android.parcel.Parcelize
)
data class SearchBook(
@PrimaryKey
- var bookUrl: String = "",
+ override var bookUrl: String = "",
var origin: String = "", // 书源规则
var originName: String = "",
var type: Int = 0, // @BookType
@@ -78,16 +78,12 @@ data class SearchBook(
variable = GSON.toJson(variableMap)
}
- @Ignore
+ @delegate:Ignore
@IgnoredOnParcel
- var origins: LinkedHashSet? = null
- private set
+ val origins: LinkedHashSet by lazy { linkedSetOf(origin) }
fun addOrigin(origin: String) {
- if (origins == null) {
- origins = linkedSetOf(this.origin)
- }
- origins?.add(origin)
+ origins.add(origin)
}
fun getDisplayLastChapterTitle(): String {
diff --git a/app/src/main/java/io/legado/app/help/AppConfig.kt b/app/src/main/java/io/legado/app/help/AppConfig.kt
index 1f4302fdc..f71652c23 100644
--- a/app/src/main/java/io/legado/app/help/AppConfig.kt
+++ b/app/src/main/java/io/legado/app/help/AppConfig.kt
@@ -86,4 +86,10 @@ object AppConfig {
set(value) {
App.INSTANCE.putPrefInt(PreferKey.chineseConverterType, value)
}
+
+ var systemTypefaces: Int
+ get() = App.INSTANCE.getPrefInt(PreferKey.systemTypefaces)
+ set(value) {
+ App.INSTANCE.putPrefInt(PreferKey.systemTypefaces, value)
+ }
}
diff --git a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt
index 3b05df869..229d5d7e6 100644
--- a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt
+++ b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt
@@ -34,7 +34,7 @@ object ReadBookConfig {
var shareLayout = App.INSTANCE.getPrefBoolean(PreferKey.shareLayout)
set(value) {
field = value
- App.INSTANCE.putPrefBoolean(PreferKey.shareLayout)
+ App.INSTANCE.putPrefBoolean(PreferKey.shareLayout, value)
}
var pageAnim = App.INSTANCE.getPrefInt(PreferKey.pageAnim)
set(value) {
@@ -43,6 +43,7 @@ object ReadBookConfig {
App.INSTANCE.putPrefInt(PreferKey.pageAnim, value)
}
var isScroll = pageAnim == 3
+ val clickTurnPage get() = App.INSTANCE.getPrefBoolean(PreferKey.clickTurnPage, true)
var bg: Drawable? = null
init {
@@ -215,7 +216,7 @@ object ReadBookConfig {
class Config(
private var bgStr: String = "#EEEEEE",//白天背景
private var bgStrNight: String = "#000000",//夜间背景
- private var bgType: Int = 0,//白天背景类型
+ private var bgType: Int = 0,//白天背景类型 0:颜色, 1:assets图片, 2其它图片
private var bgTypeNight: Int = 0,//夜间背景类型
private var darkStatusIcon: Boolean = true,//白天是否暗色状态栏
private var darkStatusIconNight: Boolean = false,//晚上是否暗色状态栏
@@ -299,7 +300,7 @@ object ReadBookConfig {
1 -> {
BitmapDrawable(
resources,
- BitmapUtils.decodeBitmap(
+ BitmapUtils.decodeAssetsBitmap(
App.INSTANCE,
"bg" + File.separator + bgStr(),
width,
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 4076c96a7..921910f34 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
@@ -100,17 +100,9 @@ object Backup {
for (fileName in backupFileNames) {
val file = File(backupPath + File.separator + fileName)
if (file.exists()) {
- var doc = treeDoc.findFile(fileName)
- if (null != doc && doc.exists()) {
- doc.delete()
- }
- doc = treeDoc.createFile("", fileName)
- doc?.let {
- DocumentUtils.writeText(
- context,
- file.readText(),
- it.uri
- )
+ treeDoc.findFile(fileName)?.delete()
+ treeDoc.createFile("", fileName)?.let {
+ DocumentUtils.writeBytes(context, file.readBytes(), it.uri)
}
}
}
diff --git a/app/src/main/java/io/legado/app/help/storage/OldRule.kt b/app/src/main/java/io/legado/app/help/storage/OldRule.kt
index c8036e559..7696bb383 100644
--- a/app/src/main/java/io/legado/app/help/storage/OldRule.kt
+++ b/app/src/main/java/io/legado/app/help/storage/OldRule.kt
@@ -100,15 +100,22 @@ object OldRule {
if (oldRule.isNullOrBlank()) return null
var newRule = oldRule
var reverse = false
+ var allinone = false
if (oldRule.startsWith("-")) {
reverse = true
newRule = oldRule.substring(1)
}
+ if (newRule.startsWith("+")) {
+ allinone = true
+ newRule = newRule.substring(1)
+ }
if (!newRule.startsWith("@CSS:", true) &&
!newRule.startsWith("@XPath:", true) &&
!newRule.startsWith("//") &&
!newRule.startsWith("##") &&
- !newRule.startsWith(":")
+ !newRule.startsWith(":") &&
+ !newRule.contains("@js:",true) &&
+ !newRule.contains("",true)
) {
if (newRule.contains("#") && !newRule.contains("##")) {
newRule = oldRule.replace("#", "##")
@@ -134,8 +141,11 @@ object OldRule {
newRule = newRule.replace("&", "&&")
}
}
+ if (allinone) {
+ newRule = "+" + newRule
+ }
if (reverse) {
- newRule += "-"
+ newRule = "-" + newRule
}
return newRule
}
@@ -145,7 +155,7 @@ object OldRule {
if (!oldUrls.contains("\n") && !oldUrls.contains("&&"))
return toNewUrl(oldUrls)
- val urls = oldUrls.split("(&&|\n)+".toRegex())
+ val urls = oldUrls.split("(&&|\r?\n)+".toRegex())
return urls.map {
toNewUrl(it)?.replace("\n\\s*".toRegex(), "")
}.joinToString("\n")
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 9fd939f74..dd48d6472 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
@@ -12,6 +12,8 @@ import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.*
import io.legado.app.help.LauncherIconHelp
import io.legado.app.help.ReadBookConfig
+import io.legado.app.service.help.ReadBook
+import io.legado.app.ui.book.read.page.ChapterProvider
import io.legado.app.utils.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.withContext
@@ -113,6 +115,8 @@ object Restore {
hideStatusBar = App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar)
hideNavigationBar = App.INSTANCE.getPrefBoolean(PreferKey.hideNavigationBar)
}
+ ChapterProvider.upStyle()
+ ReadBook.loadContent()
}
LauncherIconHelp.changeIcon(App.INSTANCE.getPrefString(PreferKey.launcherIcon))
}
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 4cc2544f5..bb3f80b8d 100644
--- a/app/src/main/java/io/legado/app/model/WebBook.kt
+++ b/app/src/main/java/io/legado/app/model/WebBook.kt
@@ -86,17 +86,18 @@ class WebBook(val bookSource: BookSource) {
): Coroutine {
book.type = bookSource.bookSourceType
return Coroutine.async(scope, context) {
- val body = if (!book.infoHtml.isNullOrEmpty()) {
- book.infoHtml
- } else {
- val analyzeUrl = AnalyzeUrl(
- book = book,
- ruleUrl = book.bookUrl,
- baseUrl = sourceUrl,
- headerMapF = bookSource.getHeaderMap()
- )
- analyzeUrl.getResponseAwait().body
- }
+ val body =
+ if (!book.infoHtml.isNullOrEmpty()) {
+ book.infoHtml
+ } else {
+ val analyzeUrl = AnalyzeUrl(
+ book = book,
+ ruleUrl = book.bookUrl,
+ baseUrl = sourceUrl,
+ headerMapF = bookSource.getHeaderMap()
+ )
+ analyzeUrl.getResponseAwait().body
+ }
BookInfo.analyzeBookInfo(book, body, bookSource, book.bookUrl)
book
}
@@ -112,16 +113,17 @@ class WebBook(val bookSource: BookSource) {
): Coroutine> {
book.type = bookSource.bookSourceType
return Coroutine.async(scope, context) {
- val body = if (book.bookUrl == book.tocUrl && !book.tocHtml.isNullOrEmpty()) {
- book.tocHtml
- } else {
- AnalyzeUrl(
- book = book,
- ruleUrl = book.tocUrl,
- baseUrl = book.bookUrl,
- headerMapF = bookSource.getHeaderMap()
- ).getResponseAwait().body
- }
+ val body =
+ if (book.bookUrl == book.tocUrl && !book.tocHtml.isNullOrEmpty()) {
+ book.tocHtml
+ } else {
+ AnalyzeUrl(
+ book = book,
+ ruleUrl = book.tocUrl,
+ baseUrl = book.bookUrl,
+ headerMapF = bookSource.getHeaderMap()
+ ).getResponseAwait().body
+ }
BookChapterList.analyzeChapterList(this, book, body, bookSource, book.tocUrl)
}
}
@@ -141,22 +143,23 @@ class WebBook(val bookSource: BookSource) {
Debug.log(sourceUrl, "⇒正文规则为空,使用章节链接:${bookChapter.url}")
return@async bookChapter.url
}
- val body = if (bookChapter.url == book.bookUrl && !book.tocHtml.isNullOrEmpty()) {
- book.tocHtml
- } else {
- val analyzeUrl =
- AnalyzeUrl(
- book = book,
- ruleUrl = bookChapter.url,
- baseUrl = book.tocUrl,
- headerMapF = bookSource.getHeaderMap()
- )
- analyzeUrl.getResponseAwait(
- bookSource.bookSourceUrl,
- jsStr = bookSource.getContentRule().webJs,
- sourceRegex = bookSource.getContentRule().sourceRegex
- ).body
- }
+ val body =
+ if (bookChapter.url == book.bookUrl && !book.tocHtml.isNullOrEmpty()) {
+ book.tocHtml
+ } else {
+ val analyzeUrl =
+ AnalyzeUrl(
+ book = book,
+ ruleUrl = bookChapter.url,
+ baseUrl = book.tocUrl,
+ headerMapF = bookSource.getHeaderMap()
+ )
+ analyzeUrl.getResponseAwait(
+ bookSource.bookSourceUrl,
+ jsStr = bookSource.getContentRule().webJs,
+ sourceRegex = bookSource.getContentRule().sourceRegex
+ ).body
+ }
BookContent.analyzeContent(
this,
body,
diff --git a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt
index ff3467a8a..aa0b7de74 100644
--- a/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt
+++ b/app/src/main/java/io/legado/app/model/webBook/BookChapterList.kt
@@ -31,6 +31,7 @@ object BookChapterList {
App.INSTANCE.getString(R.string.error_get_web_content, baseUrl)
)
Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}")
+ val analyzeRule = AnalyzeRule(book)
val tocRule = bookSource.getTocRule()
val nextUrlList = arrayListOf(baseUrl)
var reverse = false
@@ -43,7 +44,10 @@ object BookChapterList {
listRule = listRule.substring(1)
}
var chapterData =
- analyzeChapterList(body, baseUrl, tocRule, listRule, book, bookSource, log = true)
+ analyzeChapterList(
+ analyzeRule.setContent(body, baseUrl),
+ book.bookUrl, baseUrl, tocRule, listRule, bookSource, log = true
+ )
chapterData.chapterList?.let {
chapterList.addAll(it)
}
@@ -63,8 +67,8 @@ object BookChapterList {
).getResponseAwait()
.body?.let { nextBody ->
chapterData = analyzeChapterList(
- nextBody, nextUrl, tocRule, listRule,
- book, bookSource, log = false
+ analyzeRule.setContent(nextBody, nextUrl),
+ book.bookUrl, nextUrl, tocRule, listRule, bookSource
)
nextUrl = if (chapterData.nextUrl.isNotEmpty()) {
chapterData.nextUrl[0]
@@ -98,7 +102,9 @@ object BookChapterList {
headerMapF = bookSource.getHeaderMap()
).getResponseAwait().body
val nextChapterData = analyzeChapterList(
- nextBody, item.nextUrl, tocRule, listRule, book, bookSource
+ analyzeRule.setContent(nextBody, item.nextUrl),
+ book.bookUrl, item.nextUrl, tocRule, listRule, bookSource,
+ false
)
synchronized(chapterDataList) {
val isFinished = addChapterListIsFinish(
@@ -167,19 +173,17 @@ object BookChapterList {
}
private fun analyzeChapterList(
- body: String?,
+ analyzeRule: AnalyzeRule,
+ bookUrl: String,
baseUrl: String,
tocRule: TocRule,
listRule: String,
- book: Book,
bookSource: BookSource,
getNextUrl: Boolean = true,
log: Boolean = false
): ChapterData> {
val chapterList = arrayListOf()
val nextUrlList = arrayListOf()
- val analyzeRule = AnalyzeRule(book)
- analyzeRule.setContent(body, baseUrl)
val nextTocRule = tocRule.nextTocUrl
if (getNextUrl && !nextTocRule.isNullOrEmpty()) {
Debug.log(bookSource.bookSourceUrl, "┌获取目录下一页列表", log)
@@ -208,7 +212,7 @@ object BookChapterList {
var isVip: String?
for (item in elements) {
analyzeRule.setContent(item)
- val bookChapter = BookChapter(bookUrl = book.bookUrl)
+ val bookChapter = BookChapter(bookUrl = bookUrl)
analyzeRule.chapter = bookChapter
bookChapter.title = analyzeRule.getString(nameRule)
bookChapter.url = analyzeRule.getString(urlRule, true)
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 47396ad9c..d8e874195 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
@@ -33,10 +33,14 @@ object BookContent {
)
)
Debug.log(bookSource.bookSourceUrl, "≡获取成功:${baseUrl}")
+ val analyzeRule = AnalyzeRule(book)
val content = StringBuilder()
val nextUrlList = arrayListOf(baseUrl)
val contentRule = bookSource.getContentRule()
- var contentData = analyzeContent(body, contentRule, book, bookChapter, bookSource, baseUrl)
+ var contentData = analyzeContent(
+ analyzeRule.setContent(body, baseUrl),
+ contentRule, bookChapter, bookSource
+ )
content.append(contentData.content.replace(bookChapter.title, ""))
if (contentData.nextUrl.size == 1) {
var nextUrl = contentData.nextUrl[0]
@@ -56,15 +60,15 @@ object BookContent {
headerMapF = bookSource.getHeaderMap()
).getResponseAwait()
.body?.let { nextBody ->
- contentData =
- analyzeContent(
- nextBody, contentRule, book,
- bookChapter, bookSource, baseUrl, false
- )
- nextUrl =
- if (contentData.nextUrl.isNotEmpty()) contentData.nextUrl[0] else ""
- content.append(contentData.content)
- }
+ contentData =
+ analyzeContent(
+ analyzeRule.setContent(nextBody, nextUrl),
+ contentRule, bookChapter, bookSource, false
+ )
+ nextUrl =
+ if (contentData.nextUrl.isNotEmpty()) contentData.nextUrl[0] else ""
+ content.append(contentData.content)
+ }
}
Debug.log(bookSource.bookSourceUrl, "◇本章总页数:${nextUrlList.size}")
} else if (contentData.nextUrl.size > 1) {
@@ -81,19 +85,20 @@ object BookContent {
headerMapF = bookSource.getHeaderMap()
).getResponseAwait()
.body?.let {
- contentData =
- analyzeContent(
- it, contentRule, book, bookChapter,
- bookSource, item.nextUrl, false
- )
- item.content = contentData.content
- }
+ contentData =
+ analyzeContent(
+ analyzeRule.setContent(it, item.nextUrl),
+ contentRule, bookChapter, bookSource, false
+ )
+ item.content = contentData.content
+ }
}
}
for (item in contentDataList) {
content.append(item.content)
}
}
+
Debug.log(bookSource.bookSourceUrl, "┌获取章节名称")
Debug.log(bookSource.bookSourceUrl, "└${bookChapter.title}")
Debug.log(bookSource.bookSourceUrl, "┌获取正文内容")
@@ -103,17 +108,13 @@ object BookContent {
@Throws(Exception::class)
private fun analyzeContent(
- body: String,
+ analyzeRule: AnalyzeRule,
contentRule: ContentRule,
- book: Book,
chapter: BookChapter,
bookSource: BookSource,
- baseUrl: String,
printLog: Boolean = true
): ContentData> {
val nextUrlList = arrayListOf()
- val analyzeRule = AnalyzeRule(book)
- analyzeRule.setContent(body, baseUrl)
analyzeRule.chapter = chapter
val nextUrlRule = contentRule.nextContentUrl
if (!nextUrlRule.isNullOrEmpty()) {
diff --git a/app/src/main/java/io/legado/app/service/CheckSourceService.kt b/app/src/main/java/io/legado/app/service/CheckSourceService.kt
index 18f704a6e..d94b3a6d1 100644
--- a/app/src/main/java/io/legado/app/service/CheckSourceService.kt
+++ b/app/src/main/java/io/legado/app/service/CheckSourceService.kt
@@ -14,14 +14,15 @@ import io.legado.app.model.WebBook
import io.legado.app.ui.book.source.manage.BookSourceActivity
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.asCoroutineDispatcher
+import org.jetbrains.anko.toast
import java.util.concurrent.Executors
class CheckSourceService : BaseService() {
private var searchPool =
Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher()
private var task: Coroutine<*>? = null
- private var idsCount = 0
- private val unCheckIds = LinkedHashSet()
+ private val allIds = LinkedHashSet()
+ private val checkedIds = LinkedHashSet()
override fun onCreate() {
super.onCreate()
@@ -46,12 +47,12 @@ class CheckSourceService : BaseService() {
private fun check(ids: List) {
task?.cancel()
- unCheckIds.clear()
- idsCount = ids.size
- unCheckIds.addAll(ids)
- updateNotification(0, getString(R.string.progress_show, 0, idsCount))
- task = execute {
- unCheckIds.forEach { sourceUrl ->
+ allIds.clear()
+ checkedIds.clear()
+ allIds.addAll(ids)
+ updateNotification(0, getString(R.string.progress_show, 0, allIds.size))
+ task = execute(context = searchPool) {
+ allIds.forEach { sourceUrl ->
App.db.bookSourceDao().getBookSource(sourceUrl)?.let { source ->
val webBook = WebBook(source)
webBook.searchBook("我的", scope = this, context = searchPool)
@@ -59,15 +60,16 @@ class CheckSourceService : BaseService() {
source.addGroup("失效")
App.db.bookSourceDao().update(source)
}.onFinally {
- unCheckIds.remove(sourceUrl)
- val checkedCount = idsCount - unCheckIds.size
+ checkedIds.add(sourceUrl)
updateNotification(
- checkedCount,
- getString(R.string.progress_show, checkedCount, idsCount)
+ checkedIds.size,
+ getString(R.string.progress_show, checkedIds.size, allIds.size)
)
}
}
}
+ }.onError {
+ toast("校验书源出错:${it.localizedMessage}")
}
task?.invokeOnCompletion {
@@ -92,7 +94,7 @@ class CheckSourceService : BaseService() {
getString(R.string.cancel),
IntentHelp.servicePendingIntent(this, IntentAction.stop)
)
- builder.setProgress(idsCount, state, false)
+ builder.setProgress(allIds.size, state, false)
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
val notification = builder.build()
startForeground(112202, notification)
diff --git a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt
index 2cf46831c..9b4b56ebe 100644
--- a/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt
+++ b/app/src/main/java/io/legado/app/service/HttpReadAloudService.kt
@@ -5,16 +5,15 @@ import android.media.MediaPlayer
import io.legado.app.constant.EventBus
import io.legado.app.help.AppConfig
import io.legado.app.help.IntentHelp
+import io.legado.app.help.coroutine.Coroutine
import io.legado.app.help.http.HttpHelper
import io.legado.app.help.http.api.HttpPostApi
import io.legado.app.service.help.ReadBook
import io.legado.app.utils.FileUtils
import io.legado.app.utils.LogUtils
import io.legado.app.utils.postEvent
-import kotlinx.coroutines.Dispatchers.IO
-import kotlinx.coroutines.Job
import kotlinx.coroutines.isActive
-import kotlinx.coroutines.launch
+import org.jetbrains.anko.toast
import java.io.File
import java.io.FileDescriptor
import java.io.FileInputStream
@@ -27,7 +26,7 @@ class HttpReadAloudService : BaseReadAloudService(),
private val mediaPlayer = MediaPlayer()
private lateinit var ttsFolder: String
- private var job: Job? = null
+ private var task: Coroutine<*>? = null
private var playingIndex = -1
override fun onCreate() {
@@ -40,13 +39,12 @@ class HttpReadAloudService : BaseReadAloudService(),
override fun onDestroy() {
super.onDestroy()
- job?.cancel()
+ task?.cancel()
mediaPlayer.release()
}
override fun newReadAloud(dataKey: String?, play: Boolean) {
mediaPlayer.reset()
- job?.cancel()
playingIndex = -1
super.newReadAloud(dataKey, play)
}
@@ -64,7 +62,8 @@ class HttpReadAloudService : BaseReadAloudService(),
}
private fun downloadAudio() {
- job = launch(IO) {
+ task?.cancel()
+ task = execute {
FileUtils.deleteFile(ttsFolder)
for (index in 0 until contentList.size) {
if (isActive) {
@@ -85,6 +84,8 @@ class HttpReadAloudService : BaseReadAloudService(),
break
}
}
+ }.onError {
+ toast("下载朗读文件出错:${it.localizedMessage}")
}
}
@@ -150,7 +151,7 @@ class HttpReadAloudService : BaseReadAloudService(),
* 更新朗读速度
*/
override fun upSpeechRate(reset: Boolean) {
- job?.cancel()
+ task?.cancel()
mediaPlayer.stop()
playingIndex = -1
downloadAudio()
diff --git a/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt b/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt
index 33587d0a2..8fc7d1294 100644
--- a/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/about/AboutActivity.kt
@@ -6,7 +6,7 @@ import android.view.MenuItem
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.utils.openUrl
-import io.legado.app.utils.shareText
+import org.jetbrains.anko.share
class AboutActivity : BaseActivity(R.layout.activity_about) {
@@ -27,9 +27,9 @@ class AboutActivity : BaseActivity(R.layout.activity_about) {
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.menu_scoring -> openUrl("market://details?id=$packageName")
- R.id.menu_share_it -> shareText(
- "App Share",
- getString(R.string.app_share_description)
+ R.id.menu_share_it -> share(
+ getString(R.string.app_share_description),
+ getString(R.string.app_name)
)
}
return super.onCompatOptionsItemSelected(item)
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 671736dc8..c0907f002 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
@@ -95,12 +95,14 @@ class ArrangeBookActivity : VMBaseActivity(R.layout.activi
private fun initBookData() {
booksLiveData?.removeObservers(this)
booksLiveData =
- if (groupId == -1) {
- App.db.bookDao().observeAll()
- } else {
- App.db.bookDao().observeByGroup(groupId)
+ when (groupId) {
+ -1 -> App.db.bookDao().observeAll()
+ -2 -> App.db.bookDao().observeLocal()
+ -3 -> App.db.bookDao().observeAudio()
+ else -> App.db.bookDao().observeByGroup(groupId)
}
booksLiveData?.observe(this, Observer {
+ adapter.selectedBooks.clear()
adapter.setItems(it)
upSelectCount()
})
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 cf88a03dd..48bdd6cf6 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
@@ -33,7 +33,6 @@ import io.legado.app.ui.book.info.BookInfoActivity
import io.legado.app.ui.book.read.config.*
import io.legado.app.ui.book.read.config.BgTextConfigDialog.Companion.BG_COLOR
import io.legado.app.ui.book.read.config.BgTextConfigDialog.Companion.TEXT_COLOR
-import io.legado.app.ui.book.read.page.ChapterProvider
import io.legado.app.ui.book.read.page.ContentTextView
import io.legado.app.ui.book.read.page.PageView
import io.legado.app.ui.book.read.page.TextPageFactory
@@ -656,7 +655,6 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
upSystemUiVisibility()
page_view.upBg()
page_view.upStyle()
- ChapterProvider.upStyle()
if (it) {
ReadBook.loadContent()
} else {
@@ -693,7 +691,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
upScreenTimeOut()
}
observeEvent(PreferKey.textSelectAble) {
- page_view.upSelectAble(it)
+ page_view.curPage.upSelectAble(it)
}
}
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 9d0959177..4dc9ba7da 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
@@ -15,6 +15,8 @@ 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 io.legado.app.R
@@ -22,9 +24,11 @@ import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.utils.isAbsUrl
import io.legado.app.utils.sendToClip
+import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.item_fillet_text.view.*
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
@SuppressLint("RestrictedApi")
@@ -48,10 +52,27 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
recycler_view.adapter = adapter
val menu = MenuBuilder(context)
SupportMenuInflater(context).inflate(R.menu.content_select_action, menu)
+ adapter.setItems(menu.visibleItems)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- onInitializeMenu(menu)
+ 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()
+ }
}
- adapter.setItems(menu.visibleItems)
}
inner class Adapter(context: Context) :
@@ -81,6 +102,7 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
private fun onMenuItemSelected(item: MenuItemImpl) {
when (item.itemId) {
R.id.menu_copy -> context.sendToClip(callBack.selectedText)
+ R.id.menu_share_str -> context.share(callBack.selectedText)
R.id.menu_browser -> {
try {
val intent = if (callBack.selectedText.isAbsUrl()) {
@@ -98,12 +120,6 @@ 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)
- }
- }
}
callBack.onMenuActionFinally()
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
index c78466569..40c3c9e94 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
@@ -12,7 +12,6 @@ import io.legado.app.R
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.help.BookHelp
-import io.legado.app.help.ImageLoader
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.theme.accentColor
@@ -20,6 +19,7 @@ import io.legado.app.lib.theme.primaryColor
import io.legado.app.ui.book.read.Help
import io.legado.app.ui.book.read.ReadBookActivity
import io.legado.app.ui.widget.font.FontSelectDialog
+import io.legado.app.utils.getIndexById
import io.legado.app.utils.getPrefString
import io.legado.app.utils.postEvent
import io.legado.app.utils.putPrefString
@@ -31,6 +31,8 @@ import org.jetbrains.anko.sdk27.listeners.onLongClick
class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
+ val callBack get() = activity as? ReadBookActivity
+
override fun onStart() {
super.onStart()
val dm = DisplayMetrics()
@@ -121,11 +123,8 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
}
}
tv_padding.onClick {
- val activity = activity
dismiss()
- if (activity is ReadBookActivity) {
- activity.showPaddingConfig()
- }
+ callBack?.showPaddingConfig()
}
dsb_text_size.onChanged = {
ReadBookConfig.textSize = it + 5
@@ -144,15 +143,9 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
postEvent(EventBus.UP_CONFIG, true)
}
rg_page_anim.onCheckedChange { _, checkedId ->
- for (i in 0 until rg_page_anim.childCount) {
- if (checkedId == rg_page_anim[i].id) {
- ReadBookConfig.pageAnim = i
- val activity = activity
- if (activity is ReadBookActivity) {
- activity.page_view.upPageAnim()
- }
- break
- }
+ rg_page_anim.getIndexById(checkedId).let {
+ ReadBookConfig.pageAnim = it
+ callBack?.page_view?.upPageAnim()
}
}
cb_share_layout.onCheckedChangeListener = { checkBox, isChecked ->
@@ -187,10 +180,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
private fun showBgTextConfig(index: Int): Boolean {
dismiss()
changeBg(index)
- val activity = activity
- if (activity is ReadBookActivity) {
- activity.showBgTextConfig()
- }
+ callBack?.showBgTextConfig()
return true
}
@@ -219,12 +209,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
4 -> bg4
else -> bg0
}
- ReadBookConfig.getConfig(i).apply {
- when (bgType()) {
- 2 -> ImageLoader.load(requireContext(), bgStr()).centerCrop().into(iv)
- else -> iv.setImageDrawable(bgDrawable(100, 150))
- }
- }
+ iv.setImageDrawable(ReadBookConfig.getConfig(i).bgDrawable(100, 150))
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt
index 7618ddc51..2c3996fd8 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt
@@ -8,10 +8,10 @@ import android.text.TextUtils
import io.legado.app.App
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.BookChapter
+import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp
import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.entities.TextChapter
-import io.legado.app.ui.book.read.page.entities.TextChar
import io.legado.app.ui.book.read.page.entities.TextLine
import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.utils.dp
@@ -78,7 +78,7 @@ object ChapterProvider {
durY = joinBody(text, durY, textPages, pageLines, pageLengths, stringBuilder)
}
}
- textPages.last().height = durY
+ textPages.last().height = durY + 20.dp
textPages.last().text = stringBuilder.toString()
if (pageLines.size < textPages.size) {
pageLines.add(textPages.last().textLines.size)
@@ -145,42 +145,15 @@ object ChapterProvider {
title.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
stringBuilder.append(words)
textLine.text = words
- val desiredWidth = layout.getLineMax(lineIndex)
+ val desiredWidth = layout.getLineWidth(lineIndex)
if (lineIndex != layout.lineCount - 1) {
- val gapCount: Int = words.length - 1
- val d = (visibleWidth - desiredWidth) / gapCount
- var x = 0f
- for (i in words.indices) {
- val char = words[i].toString()
- val cw = StaticLayout.getDesiredWidth(char, titlePaint)
- val x1 = if (i != words.lastIndex) (x + cw + d) else (x + cw)
- val textChar = TextChar(
- charData = char,
- start = paddingLeft + x,
- end = paddingLeft + x1
- )
- textLine.textChars.add(textChar)
- x = x1
- }
+ addCharsToLineMiddle(textLine, words, titlePaint, desiredWidth, 0f)
} else {
//最后一行
- textLine.text = "$words\n"
- stringBuilder.append("\n")
- var x = if (ReadBookConfig.titleCenter)
- (visibleWidth - layout.getLineMax(lineIndex)) / 2
+ val x = if (ReadBookConfig.titleCenter)
+ (visibleWidth - layout.getLineWidth(lineIndex)) / 2
else 0f
- for (i in words.indices) {
- val char = words[i].toString()
- val cw = StaticLayout.getDesiredWidth(char, titlePaint)
- val x1 = x + cw
- val textChar = TextChar(
- charData = char,
- start = paddingLeft + x,
- end = paddingLeft + x1
- )
- textLine.textChars.add(textChar)
- x = x1
- }
+ addCharsToLineLast(textLine, words, stringBuilder, titlePaint, x)
}
}
durY += paragraphSpacing
@@ -224,80 +197,120 @@ object ChapterProvider {
textLine.lineBase = (paddingTop + durY -
(layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))).toFloat()
textLine.lineBottom = textLine.lineBase + contentPaint.fontMetrics.descent
- var words =
+ val words =
text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
stringBuilder.append(words)
textLine.text = words
- val desiredWidth = layout.getLineMax(lineIndex)
+ val desiredWidth = layout.getLineWidth(lineIndex)
if (lineIndex == 0 && layout.lineCount > 1) {
//第一行
- var x = 0f
- val icw = StaticLayout.getDesiredWidth(bodyIndent, contentPaint) / bodyIndent.length
- for (i in 0..bodyIndent.lastIndex) {
- val x1 = x + icw
- val textChar = TextChar(
- charData = bodyIndent[i].toString(),
- start = paddingLeft + x,
- end = paddingLeft + x1
- )
- textLine.textChars.add(textChar)
- x = x1
- }
- words = words.replaceFirst(bodyIndent, "")
- val gapCount: Int = words.length - 1
- val d = (visibleWidth - desiredWidth) / gapCount
- for (i in words.indices) {
- val char = words[i].toString()
- val cw = StaticLayout.getDesiredWidth(char, contentPaint)
- val x1 = if (i != words.lastIndex) x + cw + d else x + cw
- val textChar1 = TextChar(
- charData = char,
- start = paddingLeft + x,
- end = paddingLeft + x1
- )
- textLine.textChars.add(textChar1)
- x = x1
- }
+ addCharsToLineFirst(textLine, words, contentPaint, desiredWidth)
} else if (lineIndex == layout.lineCount - 1) {
//最后一行
- stringBuilder.append("\n")
- textLine.text = "$words\n"
- var x = 0f
- for (i in words.indices) {
- val char = words[i].toString()
- val cw = StaticLayout.getDesiredWidth(char, contentPaint)
- val x1 = x + cw
- val textChar = TextChar(
- charData = char,
- start = paddingLeft + x,
- end = paddingLeft + x1
- )
- textLine.textChars.add(textChar)
- x = x1
- }
+ addCharsToLineLast(textLine, words, stringBuilder, contentPaint, 0f)
} else {
//中间行
- val gapCount: Int = words.length - 1
- val d = (visibleWidth - desiredWidth) / gapCount
- var x = 0f
- for (i in words.indices) {
- val char = words[i].toString()
- val cw = StaticLayout.getDesiredWidth(char, contentPaint)
- val x1 = if (i != words.lastIndex) x + cw + d else x + cw
- val textChar = TextChar(
- charData = char,
- start = paddingLeft + x,
- end = paddingLeft + x1
- )
- textLine.textChars.add(textChar)
- x = x1
- }
+ addCharsToLineMiddle(textLine, words, contentPaint, desiredWidth, 0f)
}
}
durY += paragraphSpacing
return durY
}
+ /**
+ * 有缩进,两端对齐
+ */
+ private fun addCharsToLineFirst(
+ textLine: TextLine,
+ words: String,
+ textPaint: TextPaint,
+ desiredWidth: Float
+ ) {
+ var x = 0f
+ val icw = StaticLayout.getDesiredWidth(bodyIndent, textPaint) / bodyIndent.length
+ for (i in 0..bodyIndent.lastIndex) {
+ val x1 = x + icw
+ textLine.addTextChar(
+ charData = bodyIndent[i].toString(),
+ start = paddingLeft + x,
+ end = paddingLeft + x1
+ )
+ x = x1
+ }
+ val words1 = words.replaceFirst(bodyIndent, "")
+ addCharsToLineMiddle(textLine, words1, textPaint, desiredWidth, x)
+ }
+
+ /**
+ * 无缩进,两端对齐
+ */
+ private fun addCharsToLineMiddle(
+ textLine: TextLine,
+ words: String,
+ textPaint: TextPaint,
+ desiredWidth: Float,
+ startX: Float
+ ) {
+ val gapCount: Int = words.length - 1
+ val d = (visibleWidth - desiredWidth) / gapCount
+ var x = startX
+ for (i in words.indices) {
+ val char = words[i]
+ val cw = StaticLayout.getDesiredWidth(char.toString(), textPaint)
+ val x1 = if (i != words.lastIndex) (x + cw + d) else (x + cw)
+ textLine.addTextChar(
+ charData = char.toString(),
+ start = paddingLeft + x,
+ end = paddingLeft + x1
+ )
+ x = x1
+ }
+ exceed(textLine, words)
+ }
+
+ /**
+ * 最后一行,自然排列
+ */
+ private fun addCharsToLineLast(
+ textLine: TextLine,
+ words: String,
+ stringBuilder: StringBuilder,
+ textPaint: TextPaint,
+ startX: Float
+ ) {
+ stringBuilder.append("\n")
+ textLine.text = "$words\n"
+ var x = startX
+ for (i in words.indices) {
+ val char = words[i].toString()
+ val cw = StaticLayout.getDesiredWidth(char, textPaint)
+ val x1 = x + cw
+ textLine.addTextChar(
+ charData = char,
+ start = paddingLeft + x,
+ end = paddingLeft + x1
+ )
+ x = x1
+ }
+ exceed(textLine, words)
+ }
+
+ /**
+ * 超出边界处理
+ */
+ private fun exceed(textLine: TextLine, words: String) {
+ val endX = textLine.textChars.last().end
+ if (endX > visibleRight) {
+ val cc = (endX - visibleRight) / words.length
+ for (i in 0..words.lastIndex) {
+ textLine.getTextCharReverseAt(i).let {
+ val py = cc * (words.length - i)
+ it.start = it.start - py
+ it.end = it.end - py
+ }
+ }
+ }
+ }
/**
* 更新样式
@@ -308,7 +321,11 @@ object ChapterProvider {
if (!TextUtils.isEmpty(fontPath)) {
Typeface.createFromFile(fontPath)
} else {
- Typeface.SANS_SERIF
+ when (AppConfig.systemTypefaces) {
+ 1 -> Typeface.SERIF
+ 2 -> Typeface.MONOSPACE
+ else -> Typeface.SANS_SERIF
+ }
}
} catch (e: Exception) {
App.INSTANCE.removePref(PreferKey.readBookFont)
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
index c31fedde6..d17e55a67 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
@@ -47,17 +47,21 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
invalidate()
}
- override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
- super.onSizeChanged(w, h, oldw, oldh)
- ChapterProvider.viewWidth = w
- ChapterProvider.viewHeight = h
- ChapterProvider.upSize()
+ fun upVisibleRect() {
visibleRect.set(
ChapterProvider.paddingLeft.toFloat(),
ChapterProvider.paddingTop.toFloat(),
ChapterProvider.visibleRight.toFloat(),
ChapterProvider.visibleBottom.toFloat()
)
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+ ChapterProvider.viewWidth = w
+ ChapterProvider.viewHeight = h
+ ChapterProvider.upSize()
+ upVisibleRect()
textPage.format()
}
@@ -160,15 +164,23 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
pageOffset += offset
if (pageOffset > 0) {
- pageFactory.moveToPrev()
- textPage = pageFactory.currentPage
- pageOffset -= textPage.height
- upView?.invoke(textPage)
+ if (!pageFactory.hasPrev()) {
+ pageOffset = 0f
+ } else {
+ pageFactory.moveToPrev()
+ textPage = pageFactory.currentPage
+ pageOffset -= textPage.height
+ upView?.invoke(textPage)
+ }
} else if (pageOffset < -textPage.height) {
- pageOffset += textPage.height
- pageFactory.moveToNext()
- textPage = pageFactory.currentPage
- upView?.invoke(textPage)
+ if (!pageFactory.hasNext()) {
+ pageOffset = -textPage.height.toFloat()
+ } else {
+ pageOffset += textPage.height
+ pageFactory.moveToNext()
+ textPage = pageFactory.currentPage
+ upView?.invoke(textPage)
+ }
}
invalidate()
}
@@ -185,6 +197,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
y: Float,
select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit
) {
+ if (!selectAble) return
if (!visibleRect.contains(x, y)) return
var relativeOffset = relativeOffset(0)
for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
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 c39ca8f14..7f1dcec1c 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
@@ -5,8 +5,10 @@ import android.content.Context
import android.graphics.drawable.Drawable
import android.view.MotionEvent
import android.widget.FrameLayout
+import com.github.houbb.opencc4j.core.impl.ZhConvertBootstrap
import io.legado.app.R
import io.legado.app.constant.AppConst.TIME_FORMAT
+import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.utils.*
@@ -37,8 +39,9 @@ class ContentView(context: Context) : FrameLayout(context) {
tv_bottom_right.typeface = ChapterProvider.typeface
//显示状态栏时隐藏header
if (hideStatusBar) {
- ll_header.layoutParams =
- ll_header.layoutParams.apply { height = context.statusBarHeight }
+ ll_header.layoutParams = ll_header.layoutParams.apply {
+ height = context.statusBarHeight + headerPaddingTop.dp + headerPaddingBottom.dp
+ }
ll_header.setPadding(
headerPaddingLeft.dp,
headerPaddingTop.dp,
@@ -51,18 +54,13 @@ class ContentView(context: Context) : FrameLayout(context) {
ll_header.gone()
page_panel.setPadding(0, context.statusBarHeight, 0, 0)
}
- content_text_view.setPadding(
- paddingLeft.dp,
- paddingTop.dp,
- paddingRight.dp,
- paddingBottom.dp
- )
ll_footer.setPadding(
footerPaddingLeft.dp,
footerPaddingTop.dp,
footerPaddingRight.dp,
footerPaddingBottom.dp
)
+ content_text_view.upVisibleRect()
durConfig.textColor().let {
tv_top_left.setTextColor(it)
tv_top_right.setTextColor(it)
@@ -94,7 +92,11 @@ class ContentView(context: Context) : FrameLayout(context) {
}
fun setContent(textPage: TextPage) {
- tv_bottom_left.text = textPage.title
+ tv_bottom_left.text = when (AppConfig.chineseConverterType) {
+ 1 -> ZhConvertBootstrap.newInstance().toSimple(textPage.title)
+ 2 -> ZhConvertBootstrap.newInstance().toTraditional(textPage.title)
+ else -> textPage.title
+ }
setPageIndex(textPage.index, textPage.pageSize)
content_text_view.resetPageOffset()
content_text_view.setContent(textPage)
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 d5a46d734..c6839b214 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
@@ -120,7 +120,7 @@ class PageView(context: Context, attrs: AttributeSet) :
if (noAnim) {
fillPage(PageDelegate.Direction.PREV)
} else {
- pageDelegate?.start(PageDelegate.Direction.PREV)
+ pageDelegate?.prevPageByAnim()
}
}
@@ -128,16 +128,12 @@ class PageView(context: Context, attrs: AttributeSet) :
if (noAnim) {
fillPage(PageDelegate.Direction.NEXT)
} else {
- pageDelegate?.start(PageDelegate.Direction.NEXT)
+ pageDelegate?.nextPageByAnim()
}
}
- fun upSelectAble(selectAble: Boolean) {
- pageDelegate?.upSelectAble(selectAble)
- curPage.upSelectAble(selectAble)
- }
-
fun upStyle() {
+ ChapterProvider.upStyle()
curPage.upStyle()
prevPage.upStyle()
nextPage.upStyle()
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt
index 586b3d88e..700728b14 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/HorizontalPageDelegate.kt
@@ -52,4 +52,19 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
}
}
+ override fun nextPageByAnim() {
+ if (!hasNext()) return
+ setDirection(Direction.NEXT)
+ setBitmap()
+ setTouchPoint(viewWidth.toFloat(), 0f)
+ onAnimStart()
+ }
+
+ override fun prevPageByAnim() {
+ if (!hasPrev()) return
+ setDirection(Direction.PREV)
+ setBitmap()
+ setTouchPoint(0f, 0f)
+ onAnimStart()
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt
index c710367c7..76b40fb78 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt
@@ -12,6 +12,7 @@ import android.widget.Scroller
import androidx.annotation.CallSuper
import com.google.android.material.snackbar.Snackbar
import io.legado.app.help.AppConfig
+import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.ContentView
import io.legado.app.ui.book.read.page.PageView
import io.legado.app.utils.screenshot
@@ -101,10 +102,6 @@ abstract class PageDelegate(protected val pageView: PageView) :
onScroll()
}
- fun upSelectAble(selectAble: Boolean) {
- detector.setIsLongpressEnabled(selectAble)
- }
-
open fun fling(
startX: Int, startY: Int, velocityX: Int, velocityY: Int,
minX: Int, maxX: Int, minY: Int, maxY: Int
@@ -161,34 +158,6 @@ abstract class PageDelegate(protected val pageView: PageView) :
}
}
- fun start(direction: Direction) {
- if (isStarted) return
- if (direction === Direction.NEXT) {
- val x = viewWidth.toFloat()
- val y = viewHeight.toFloat()
- //初始化动画
- setStartPoint(x, y, false)
- //设置点击点
- setTouchPoint(x, y, false)
- //设置方向
- if (!hasNext()) {
- return
- }
- } else {
- val x = 0.toFloat()
- val y = viewHeight.toFloat()
- //初始化动画
- setStartPoint(x, y, false)
- //设置点击点
- setTouchPoint(x, y, false)
- //设置方向方向
- if (!hasPrev()) {
- return
- }
- }
- onAnimStart()
- }
-
open fun onAnimStart() {}//scroller start
open fun onDraw(canvas: Canvas) {}//绘制
@@ -197,6 +166,10 @@ abstract class PageDelegate(protected val pageView: PageView) :
open fun onScroll() {}//移动contentView, slidePage
+ abstract fun nextPageByAnim()
+
+ abstract fun prevPageByAnim()
+
@CallSuper
open fun setDirection(direction: Direction) {
mDirection = direction
@@ -264,21 +237,14 @@ abstract class PageDelegate(protected val pageView: PageView) :
if (centerRectF.contains(x, y)) {
pageView.callBack.clickCenter()
setTouchPoint(x, y)
- } else {
+ } else if (ReadBookConfig.clickTurnPage) {
if (x > viewWidth / 2 ||
AppConfig.clickAllNext
) {
- //设置动画方向
- if (!hasNext()) return true
- setDirection(Direction.NEXT)
- setBitmap()
+ nextPageByAnim()
} else {
- if (!hasPrev()) return true
- setDirection(Direction.PREV)
- setBitmap()
+ prevPageByAnim()
}
- setTouchPoint(x, y)
- onAnimStart()
}
return true
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt
index d90ee8649..368a9ad8a 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/ScrollPageDelegate.kt
@@ -58,4 +58,12 @@ class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) {
super.onDestroy()
mVelocity.recycle()
}
+
+ override fun nextPageByAnim() {
+
+ }
+
+ override fun prevPageByAnim() {
+
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt
index ca4048c8d..6d7dd7bf8 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/delegate/SimulationPageDelegate.kt
@@ -40,7 +40,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
private var mMiddleY = 0f
private var mDegrees = 0f
private var mTouchToCornerDis = 0f
- private var mColorMatrixFilter: ColorMatrixColorFilter = ColorMatrixColorFilter(
+ private var mColorMatrixFilter = ColorMatrixColorFilter(
ColorMatrix(
floatArrayOf(
1f, 0f, 0f, 0f, 0f,
@@ -55,7 +55,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
// 是否属于右上左下
private var mIsRtOrLb = false
- private var mMaxLength = 0f
+ private var mMaxLength = hypot(viewWidth.toDouble(), viewHeight.toDouble()).toFloat()
// 背面颜色组
private var mBackShadowColors: IntArray
// 前面颜色组
@@ -71,15 +71,13 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
private var mFrontShadowDrawableVLR: GradientDrawable
private var mFrontShadowDrawableVRL: GradientDrawable
- private val mPaint: Paint = Paint()
+ private val mPaint: Paint = Paint().apply { style = Paint.Style.FILL }
private var curBitmap: Bitmap? = null
private var prevBitmap: Bitmap? = null
private var nextBitmap: Bitmap? = null
init {
- mMaxLength = hypot(viewWidth.toDouble(), viewWidth.toDouble()).toFloat()
- mPaint.style = Paint.Style.FILL
//设置颜色数组
val color = intArrayOf(0x333333, -0x4fcccccd)
mFolderShadowDrawableRL = GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, color)
@@ -117,7 +115,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
override fun setViewSize(width: Int, height: Int) {
super.setViewSize(width, height)
- mMaxLength = hypot(viewWidth.toDouble(), viewWidth.toDouble()).toFloat()
+ mMaxLength = hypot(viewWidth.toDouble(), viewHeight.toDouble()).toFloat()
}
override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) {
@@ -126,7 +124,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
}
override fun setTouchPoint(x: Float, y: Float, invalidate: Boolean) {
- super.setTouchPoint(x, y, invalidate)
+ super.setTouchPoint(x, y, false)
//触摸y中间位置吧y变成屏幕高度
if ((startY > viewHeight * 0.33 && startY < viewHeight * 0.66)
|| mDirection == Direction.PREV
@@ -139,6 +137,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
) {
touchY = 1f
}
+ pageView.invalidate()
}
override fun setDirection(direction: Direction) {
@@ -219,18 +218,22 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
}
override fun onDraw(canvas: Canvas) {
- if (mDirection === Direction.NEXT) {
- calcPoints()
- drawCurrentPageArea(canvas, curBitmap, mPath0)
- drawNextPageAreaAndShadow(canvas, nextBitmap)
- drawCurrentPageShadow(canvas)
- drawCurrentBackArea(canvas, curBitmap)
- } else {
- calcPoints()
- drawCurrentPageArea(canvas, prevBitmap, mPath0)
- drawNextPageAreaAndShadow(canvas, curBitmap)
- drawCurrentPageShadow(canvas)
- drawCurrentBackArea(canvas, prevBitmap)
+ when (mDirection) {
+ Direction.NEXT -> {
+ calcPoints()
+ drawCurrentPageArea(canvas, curBitmap)
+ drawNextPageAreaAndShadow(canvas, nextBitmap)
+ drawCurrentPageShadow(canvas)
+ drawCurrentBackArea(canvas, curBitmap)
+ }
+ Direction.PREV -> {
+ calcPoints()
+ drawCurrentPageArea(canvas, prevBitmap)
+ drawNextPageAreaAndShadow(canvas, curBitmap)
+ drawCurrentPageShadow(canvas)
+ drawCurrentBackArea(canvas, prevBitmap)
+ }
+ else -> return
}
}
@@ -258,7 +261,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
val left: Int
val right: Int
if (mIsRtOrLb) {
- left = mBezierStart1.x.toInt() - 1
+ left = (mBezierStart1.x - 1).toInt()
right = (mBezierStart1.x + f3 + 1).toInt()
mFolderShadowDrawable = mFolderShadowDrawableLR
} else {
@@ -337,17 +340,16 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
var mCurrentPageShadow: GradientDrawable
if (mIsRtOrLb) {
leftX = mBezierControl1.x.toInt()
- rightX = mBezierControl1.x.toInt() + 25
+ rightX = (mBezierControl1.x + 25).toInt()
mCurrentPageShadow = mFrontShadowDrawableVLR
} else {
- leftX = mBezierControl1.x.toInt() - 25
- rightX = mBezierControl1.x.toInt() + 1
+ leftX = (mBezierControl1.x - 25).toInt()
+ rightX = (mBezierControl1.x + 1).toInt()
mCurrentPageShadow = mFrontShadowDrawableVRL
}
- var rotateDegrees: Float =
- Math.toDegrees(
- atan2(mTouchX - mBezierControl1.x, mBezierControl1.y - mTouchY).toDouble()
- ).toFloat()
+ var rotateDegrees = Math.toDegrees(
+ atan2(mTouchX - mBezierControl1.x, mBezierControl1.y - mTouchY).toDouble()
+ ).toFloat()
canvas.rotate(rotateDegrees, mBezierControl1.x, mBezierControl1.y)
mCurrentPageShadow.setBounds(
leftX, (mBezierControl1.y - mMaxLength).toInt(),
@@ -372,24 +374,25 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
if (mIsRtOrLb) {
leftX = mBezierControl2.y.toInt()
- rightX = mBezierControl2.y.toInt() + 25
+ rightX = (mBezierControl2.y + 25).toInt()
mCurrentPageShadow = mFrontShadowDrawableHTB
} else {
- leftX = mBezierControl2.y.toInt() - 25
- rightX = mBezierControl2.y.toInt() + 1
+ leftX = (mBezierControl2.y - 25).toInt()
+ rightX = (mBezierControl2.y + 1).toInt()
mCurrentPageShadow = mFrontShadowDrawableHBT
}
rotateDegrees = Math.toDegrees(
atan2(mBezierControl2.y - mTouchY, mBezierControl2.x - mTouchX).toDouble()
).toFloat()
canvas.rotate(rotateDegrees, mBezierControl2.x, mBezierControl2.y)
- val temp: Float =
- if (mBezierControl2.y < 0) mBezierControl2.y - viewHeight else mBezierControl2.y
- val hmg = hypot(mBezierControl2.x.toDouble(), temp.toDouble()).toInt()
+ val temp =
+ if (mBezierControl2.y < 0) (mBezierControl2.y - viewHeight).toDouble()
+ else mBezierControl2.y.toDouble()
+ val hmg = hypot(mBezierControl2.x.toDouble(), temp)
if (hmg > mMaxLength)
mCurrentPageShadow.setBounds(
- (mBezierControl2.x - 25).toInt() - hmg, leftX,
- (mBezierControl2.x + mMaxLength).toInt() - hmg, rightX
+ (mBezierControl2.x - 25 - hmg).toInt(), leftX,
+ (mBezierControl2.x + mMaxLength - hmg).toInt(), rightX
)
else
mCurrentPageShadow.setBounds(
@@ -400,6 +403,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
canvas.restore()
}
+ //
private fun drawNextPageAreaAndShadow(
canvas: Canvas,
bitmap: Bitmap?
@@ -447,10 +451,10 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
canvas.restore()
}
+ //
private fun drawCurrentPageArea(
canvas: Canvas,
- bitmap: Bitmap?,
- path: Path
+ bitmap: Bitmap?
) {
bitmap ?: return
mPath0.reset()
@@ -461,11 +465,12 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
mPath0.quadTo(mBezierControl2.x, mBezierControl2.y, mBezierStart2.x, mBezierStart2.y)
mPath0.lineTo(mCornerX.toFloat(), mCornerY.toFloat())
mPath0.close()
+
canvas.save()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- canvas.clipOutPath(path)
+ canvas.clipOutPath(mPath0)
} else {
- canvas.clipPath(path, Region.Op.XOR)
+ canvas.clipPath(mPath0, Region.Op.XOR)
}
canvas.drawBitmap(bitmap, 0f, 0f, null)
canvas.restore()
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt
index 6fed2fe97..72798a9ea 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextChar.kt
@@ -2,7 +2,7 @@ package io.legado.app.ui.book.read.page.entities
data class TextChar(
val charData: String,
- var selected: Boolean = false,
- val start: Float,
- val end: Float
+ var start: Float,
+ var end: Float,
+ var selected: Boolean = false
)
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt
index 26fb31e90..a9c84fb68 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt
@@ -8,4 +8,21 @@ data class TextLine(
var lineBottom: Float = 0f,
val isTitle: Boolean = false,
var isReadAloud: Boolean = false
-)
\ No newline at end of file
+) {
+
+ fun addTextChar(charData: String, start: Float, end: Float) {
+ textChars.add(TextChar(charData, start = start, end = end))
+ }
+
+ fun getTextCharAt(index: Int): TextChar {
+ return textChars[index]
+ }
+
+ fun getTextCharReverseAt(index: Int): TextChar {
+ return textChars[textChars.lastIndex - index]
+ }
+
+ fun getTextCharsCount(): Int {
+ return textChars.size
+ }
+}
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 8db633cf8..07a3b16c5 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
@@ -16,10 +16,6 @@ class DiffCallBack(private val oldItems: List, private val newItems:
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
- return true
- }
-
- override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
if (oldItem.name != newItem.name) {
@@ -28,7 +24,13 @@ class DiffCallBack(private val oldItems: List, private val newItems:
if (oldItem.author != newItem.author) {
return false
}
- if (oldItem.origins?.size != newItem.origins?.size) {
+ return true
+ }
+
+ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ val oldItem = oldItems[oldItemPosition]
+ val newItem = newItems[newItemPosition]
+ if (oldItem.origins.size != newItem.origins.size) {
return false
}
if (oldItem.coverUrl != newItem.coverUrl) {
@@ -56,20 +58,20 @@ class DiffCallBack(private val oldItems: List, private val newItems:
if (oldItem.author != newItem.author) {
payload.putString("author", newItem.author)
}
- if (oldItem.origins?.size != newItem.origins?.size) {
- payload.putInt("origins", newItem.origins?.size ?: 1)
+ if (oldItem.origins.size != newItem.origins.size) {
+ payload.putInt("origins", newItem.origins.size)
}
if (oldItem.coverUrl != newItem.coverUrl) {
- payload.putString("group", newItem.coverUrl)
+ payload.putString("cover", newItem.coverUrl)
}
if (oldItem.kind != newItem.kind) {
- payload.putString("enabled", newItem.kind)
+ payload.putString("kind", newItem.kind)
}
if (oldItem.latestChapterTitle != newItem.latestChapterTitle) {
- payload.putString("enabled", newItem.latestChapterTitle)
+ payload.putString("last", newItem.latestChapterTitle)
}
if (oldItem.intro != newItem.intro) {
- payload.putString("enabled", newItem.intro)
+ payload.putString("intro", newItem.intro)
}
return payload
}
diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchAdapter.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchAdapter.kt
index fbdcb99cf..a96244113 100644
--- a/app/src/main/java/io/legado/app/ui/book/search/SearchAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/book/search/SearchAdapter.kt
@@ -40,7 +40,7 @@ class SearchAdapter(context: Context, val callBack: CallBack) :
with(itemView) {
tv_name.text = searchBook.name
tv_author.text = context.getString(R.string.author_show, searchBook.author)
- bv_originCount.setBadgeCount(searchBook.origins?.size ?: 1)
+ bv_originCount.setBadgeCount(searchBook.origins.size)
upLasted(itemView, searchBook.latestChapterTitle)
tv_introduce.text = context.getString(R.string.intro_show, searchBook.intro)
upKind(itemView, searchBook.getKindList())
@@ -56,9 +56,9 @@ class SearchAdapter(context: Context, val callBack: CallBack) :
"name" -> tv_name.text = searchBook.name
"author" -> tv_author.text =
context.getString(R.string.author_show, searchBook.author)
- "originCount" -> bv_originCount.setBadgeCount(searchBook.origins?.size ?: 1)
- "lasted" -> upLasted(itemView, searchBook.latestChapterTitle)
- "introduce" -> tv_introduce.text =
+ "origins" -> bv_originCount.setBadgeCount(searchBook.origins.size)
+ "last" -> upLasted(itemView, searchBook.latestChapterTitle)
+ "intro" -> tv_introduce.text =
context.getString(R.string.intro_show, searchBook.intro)
"kind" -> upKind(itemView, searchBook.getKindList())
"cover" -> iv_cover.load(
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 df104de88..47671626d 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
@@ -85,8 +85,8 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
private fun precisionSearch(searchBooks: List) {
val books = arrayListOf()
searchBooks.forEach { searchBook ->
- if (searchBook.name.equals(searchKey, true)
- || searchBook.author.equals(searchKey, true)
+ if (searchBook.name.contains(searchKey, true)
+ || searchBook.author.contains(searchKey, true)
) books.add(searchBook)
}
App.db.searchBookDao().insert(*books.toTypedArray())
@@ -130,9 +130,9 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
}
}
} else if (searchKey == item.author) {
- for ((i, searchBook) in copyDataS.withIndex()) {
+ for ((index, searchBook) in copyDataS.withIndex()) {
if (searchKey != searchBook.name && searchKey == searchBook.author) {
- copyDataS.add(i, item)
+ copyDataS.add(index, item)
break
}
}
@@ -141,6 +141,31 @@ class SearchViewModel(application: Application) : BaseViewModel(application) {
}
}
}
+ searchBooks.sortWith(Comparator { o1, o2 ->
+ if (o1.name == searchKey && o2.name != searchKey) {
+ 1
+ } else if (o1.name != searchKey && o2.name == searchKey) {
+ -1
+ } else if (o1.author == searchKey && o2.author != searchKey) {
+ 1
+ } else if (o1.author != searchKey && o2.author == searchKey) {
+ -1
+ } else if (o1.name == o2.name) {
+ when {
+ o1.origins.size > o2.origins.size -> {
+ 1
+ }
+ o1.origins.size < o2.origins.size -> {
+ -1
+ }
+ else -> {
+ 0
+ }
+ }
+ } else {
+ 0
+ }
+ })
searchBooks = copyDataS
searchBookLiveData.postValue(copyDataS)
}
diff --git a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt
index f693c07ed..c0eab1b32 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/edit/BookSourceEditActivity.kt
@@ -25,9 +25,13 @@ import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.ATH
import io.legado.app.ui.book.source.debug.BookSourceDebugActivity
import io.legado.app.ui.widget.KeyboardToolPop
-import io.legado.app.utils.*
+import io.legado.app.utils.GSON
+import io.legado.app.utils.applyTint
+import io.legado.app.utils.getViewModel
+import io.legado.app.utils.shareWithQr
import kotlinx.android.synthetic.main.activity_book_source_edit.*
import org.jetbrains.anko.displayMetrics
+import org.jetbrains.anko.share
import org.jetbrains.anko.startActivity
import org.jetbrains.anko.toast
import kotlin.math.abs
@@ -84,9 +88,7 @@ class BookSourceEditActivity :
}
}
R.id.menu_paste_source -> viewModel.pasteSource { upRecyclerView(it) }
- R.id.menu_share_str -> GSON.toJson(getSource())?.let { sourceStr ->
- shareText(getString(R.string.share_book_source), sourceStr)
- }
+ R.id.menu_share_str -> GSON.toJson(getSource())?.let { share(it) }
R.id.menu_share_qr -> GSON.toJson(getSource())?.let { sourceStr ->
shareWithQr(getString(R.string.share_book_source), sourceStr)
}
diff --git a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt
index 13b145a1e..1a683427f 100644
--- a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceViewModel.kt
@@ -58,7 +58,7 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
App.db.searchBookDao().insert(searchBook)
if (screenKey.isEmpty()) {
searchBooks.add(searchBook)
- } else if (searchBook.originName.contains(screenKey)) {
+ } else if (searchBook.name.contains(screenKey)) {
searchBooks.add(searchBook)
}
upAdapter()
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 16fd3941b..f314b5f1b 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
@@ -26,7 +26,7 @@ import kotlinx.coroutines.Dispatchers.Main
import org.jetbrains.anko.toast
object BackupRestoreUi {
-
+ private const val selectFolderRequestCode = 21
private const val backupSelectRequestCode = 22
private const val restoreSelectRequestCode = 33
private const val oldDataRequestCode = 11
@@ -34,7 +34,7 @@ object BackupRestoreUi {
fun backup(fragment: Fragment) {
val backupPath = AppConfig.backupPath
if (backupPath.isNullOrEmpty()) {
- selectBackupFolder(fragment)
+ selectBackupFolder(fragment, backupSelectRequestCode)
} else {
if (backupPath.isContentPath()) {
val uri = Uri.parse(backupPath)
@@ -46,40 +46,49 @@ object BackupRestoreUi {
fragment.toast(R.string.backup_success)
}
} else {
- selectBackupFolder(fragment)
+ selectBackupFolder(fragment, backupSelectRequestCode)
}
} else {
- backupUsePermission(fragment)
+ backupUsePermission(fragment, requestCode = backupSelectRequestCode)
}
}
}
- private fun backupUsePermission(fragment: Fragment, path: String = Backup.legadoPath) {
+ private fun backupUsePermission(
+ fragment: Fragment,
+ path: String = Backup.legadoPath,
+ requestCode: Int = selectFolderRequestCode
+ ) {
PermissionsCompat.Builder(fragment)
.addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage)
.onGranted {
- Coroutine.async {
- AppConfig.backupPath = Backup.legadoPath
- Backup.backup(fragment.requireContext(), path)
- }.onSuccess {
- fragment.toast(R.string.backup_success)
+ when (requestCode) {
+ selectFolderRequestCode -> AppConfig.backupPath = Backup.legadoPath
+ else -> {
+ Coroutine.async {
+ AppConfig.backupPath = Backup.legadoPath
+ Backup.backup(fragment.requireContext(), path)
+ }.onSuccess {
+ fragment.toast(R.string.backup_success)
+ }
+ }
}
}
.request()
}
- fun selectBackupFolder(fragment: Fragment) {
+ fun selectBackupFolder(fragment: Fragment, requestCode: Int = selectFolderRequestCode) {
fragment.alert {
titleResource = R.string.select_folder
items(fragment.resources.getStringArray(R.array.select_folder).toList()) { _, index ->
when (index) {
- 0 -> backupUsePermission(fragment)
+ 0 -> backupUsePermission(fragment, requestCode = requestCode)
1 -> {
try {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- fragment.startActivityForResult(intent, backupSelectRequestCode)
+ fragment.startActivityForResult(intent, requestCode)
} catch (e: java.lang.Exception) {
e.printStackTrace()
fragment.toast(e.localizedMessage ?: "ERROR")
@@ -88,7 +97,7 @@ object BackupRestoreUi {
2 -> {
FileChooserDialog.show(
fragment.childFragmentManager,
- backupSelectRequestCode,
+ requestCode,
mode = FileChooserDialog.DIRECTORY
)
}
@@ -231,6 +240,9 @@ object BackupRestoreUi {
App.INSTANCE.toast(R.string.restore_success)
}
}
+ selectFolderRequestCode -> {
+ AppConfig.backupPath = currentPath
+ }
}
}
@@ -240,7 +252,8 @@ object BackupRestoreUi {
data?.data?.let { uri ->
App.INSTANCE.contentResolver.takePersistableUriPermission(
uri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ Intent.FLAG_GRANT_READ_URI_PERMISSION
+ or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
AppConfig.backupPath = uri.toString()
Coroutine.async {
@@ -254,7 +267,8 @@ object BackupRestoreUi {
data?.data?.let { uri ->
App.INSTANCE.contentResolver.takePersistableUriPermission(
uri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ Intent.FLAG_GRANT_READ_URI_PERMISSION
+ or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
AppConfig.backupPath = uri.toString()
Coroutine.async {
@@ -264,6 +278,16 @@ object BackupRestoreUi {
}
}
}
+ selectFolderRequestCode -> if (resultCode == RESULT_OK) {
+ data?.data?.let { uri ->
+ App.INSTANCE.contentResolver.takePersistableUriPermission(
+ uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION
+ or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ )
+ AppConfig.backupPath = uri.toString()
+ }
+ }
oldDataRequestCode ->
if (resultCode == RESULT_OK) data?.data?.let { uri ->
ImportOldData.importUri(uri)
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 16b7b286f..f49cf2ea6 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
@@ -2,6 +2,7 @@ package io.legado.app.ui.main.bookshelf
import android.annotation.SuppressLint
import android.os.Bundle
+import android.view.LayoutInflater
import android.view.Menu
import android.view.MenuItem
import android.view.View
@@ -17,7 +18,10 @@ import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.BookGroup
-import io.legado.app.lib.dialogs.*
+import io.legado.app.lib.dialogs.alert
+import io.legado.app.lib.dialogs.customView
+import io.legado.app.lib.dialogs.noButton
+import io.legado.app.lib.dialogs.okButton
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.accentColor
import io.legado.app.ui.book.arrange.ArrangeBookActivity
@@ -27,6 +31,7 @@ import io.legado.app.ui.download.DownloadActivity
import io.legado.app.ui.importbook.ImportBookActivity
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.*
+import kotlinx.android.synthetic.main.dialog_bookshelf_config.view.*
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.fragment_bookshelf.*
import kotlinx.android.synthetic.main.view_tab_layout.*
@@ -61,7 +66,7 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b
super.onCompatOptionsItemSelected(item)
when (item.itemId) {
R.id.menu_search -> startActivity()
- R.id.menu_bookshelf_layout -> selectBookshelfLayout()
+ R.id.menu_bookshelf_layout -> configBookshelf()
R.id.menu_group_manage -> GroupManageDialog()
.show(childFragmentManager, "groupManageDialog")
R.id.menu_add_local -> startActivity()
@@ -145,14 +150,35 @@ class BookshelfFragment : VMBaseFragment(R.layout.fragment_b
}
}
- private fun selectBookshelfLayout() {
- selector(
- title = "选择书架布局",
- items = resources.getStringArray(R.array.bookshelf_layout).toList()
- ) { _, index ->
- putPrefInt(PreferKey.bookshelfLayout, index)
- activity?.recreate()
- }
+ @SuppressLint("InflateParams")
+ private fun configBookshelf() {
+ requireContext().alert(titleResource = R.string.bookshelf_layout) {
+ val bookshelfLayout = getPrefInt(PreferKey.bookshelfLayout)
+ val bookshelfSort = getPrefInt(PreferKey.bookshelfSort)
+ val root = LayoutInflater.from(requireContext())
+ .inflate(R.layout.dialog_bookshelf_config, null).apply {
+ rg_layout.checkByIndex(bookshelfLayout)
+ rg_sort.checkByIndex(bookshelfSort)
+ }
+ customView = root
+ okButton {
+ root.apply {
+ var changed = false
+ if (bookshelfLayout != rg_layout.getCheckedIndex()) {
+ putPrefInt(PreferKey.bookshelfLayout, rg_layout.getCheckedIndex())
+ changed = true
+ }
+ if (bookshelfSort != rg_sort.getCheckedIndex()) {
+ putPrefInt(PreferKey.bookshelfSort, rg_sort.getCheckedIndex())
+ changed = true
+ }
+ if (changed) {
+ activity?.recreate()
+ }
+ }
+ }
+ noButton()
+ }.show().applyTint()
}
@SuppressLint("InflateParams")
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 1a462a294..49cadb56c 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
@@ -103,10 +103,16 @@ class BooksFragment : BaseFragment(R.layout.fragment_books),
-3 -> App.db.bookDao().observeAudio()
else -> App.db.bookDao().observeByGroup(groupId)
}
- bookshelfLiveData?.observe(this, Observer {
+ bookshelfLiveData?.observe(this, Observer { list ->
+ val books = when (getPrefInt(PreferKey.bookshelfSort)) {
+ 1 -> list.sortedByDescending { it.latestChapterTime }
+ 2 -> list.sortedBy { it.name }
+ 3 -> list.sortedBy { it.order }
+ else -> list.sortedByDescending { it.durChapterTime }
+ }
val diffResult = DiffUtil
- .calculateDiff(BooksDiffCallBack(ArrayList(booksAdapter.getItems()), it))
- booksAdapter.setItems(it, diffResult)
+ .calculateDiff(BooksDiffCallBack(ArrayList(booksAdapter.getItems()), books))
+ booksAdapter.setItems(books, diffResult)
})
}
diff --git a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt
index bfd4d23a1..6ea1d85bd 100644
--- a/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/qrcode/QrCodeActivity.kt
@@ -2,6 +2,7 @@ package io.legado.app.ui.qrcode
import android.app.Activity
import android.content.Intent
+import android.graphics.BitmapFactory
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
@@ -11,7 +12,7 @@ import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat
-import io.legado.app.utils.RealPathUtil
+import io.legado.app.utils.readBytes
import kotlinx.android.synthetic.main.activity_qrcode_capture.*
import kotlinx.android.synthetic.main.view_title_bar.*
import org.jetbrains.anko.toast
@@ -98,9 +99,11 @@ class QrCodeActivity : BaseActivity(R.layout.activity_qrcode_capture), QRCodeVie
zxingview.startSpotAndShowRect() // 显示扫描框,并开始识别
if (resultCode == Activity.RESULT_OK && requestCode == requestQrImage) {
- val picturePath = RealPathUtil.getPath(this, it)
// 本来就用到 QRCodeView 时可直接调 QRCodeView 的方法,走通用的回调
- zxingview.decodeQRCode(picturePath)
+ it.readBytes(this)?.let { bytes ->
+ val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
+ zxingview.decodeQRCode(bitmap)
+ }
}
}
}
diff --git a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt
index 331c87a5d..5044e2667 100644
--- a/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt
@@ -55,6 +55,7 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi
private var dataInit = false
override fun onActivityCreated(savedInstanceState: Bundle?) {
+ initUriScheme()
initRecyclerView()
initSearchView()
initSelectActionView()
@@ -73,6 +74,22 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi
return super.onPrepareOptionsMenu(menu)
}
+ private fun initUriScheme() {
+ intent.data?.let {
+ when (it.path) {
+ "/importonline" -> it.getQueryParameter("src")?.let { url ->
+ Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show()
+ viewModel.importSource(url) { msg ->
+ title_bar.snackbar(msg)
+ }
+ }
+ else -> {
+ toast("格式不对")
+ }
+ }
+ }
+ }
+
private fun initRecyclerView() {
ATH.applyEdgeEffectColor(recycler_view)
recycler_view.layoutManager = LinearLayoutManager(this)
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 c68db66a1..255505178 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
@@ -5,7 +5,9 @@ import android.os.Bundle
import android.view.KeyEvent
import android.view.Menu
import android.view.MenuItem
+import android.webkit.WebResourceRequest
import android.webkit.WebSettings
+import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.lifecycle.Observer
import io.legado.app.R
@@ -14,10 +16,11 @@ import io.legado.app.lib.theme.DrawableUtils
import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.getViewModel
-import io.legado.app.utils.shareText
+import io.legado.app.utils.openUrl
import kotlinx.android.synthetic.main.activity_rss_read.*
import kotlinx.coroutines.launch
import org.apache.commons.text.StringEscapeUtils
+import org.jetbrains.anko.share
import org.jsoup.Jsoup
import org.jsoup.safety.Whitelist
@@ -54,7 +57,7 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
when (item.itemId) {
R.id.menu_rss_star -> viewModel.favorite()
R.id.menu_share_it -> viewModel.rssArticle?.let {
- shareText("链接分享", it.link)
+ share(it.link)
}
R.id.menu_aloud -> readAloud()
}
@@ -62,10 +65,24 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r
}
private fun initWebView() {
- webView.webViewClient = WebViewClient()
+ webView.webViewClient = object : WebViewClient() {
+ override fun shouldOverrideUrlLoading(
+ view: WebView?,
+ request: WebResourceRequest?
+ ): Boolean {
+ if (request?.url?.scheme == "http" || request?.url?.scheme == "https") {
+ return false
+ }
+ request?.url?.let {
+ openUrl(it)
+ }
+ return true
+ }
+ }
webView.settings.apply {
mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
domStorageEnabled = true
+ allowContentAccess = true
}
}
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
index 09839e458..944135b45 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditActivity.kt
@@ -4,6 +4,7 @@ import android.app.Activity
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
+import android.content.Intent
import android.graphics.Rect
import android.os.Bundle
import android.view.Gravity
@@ -19,22 +20,25 @@ import io.legado.app.constant.AppConst
import io.legado.app.data.entities.RssSource
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.ATH
+import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.ui.rss.source.debug.RssSourceDebugActivity
import io.legado.app.ui.widget.KeyboardToolPop
-import io.legado.app.utils.*
+import io.legado.app.utils.GSON
+import io.legado.app.utils.applyTint
+import io.legado.app.utils.getViewModel
+import io.legado.app.utils.shareWithQr
import kotlinx.android.synthetic.main.activity_rss_source_edit.*
-import org.jetbrains.anko.displayMetrics
-import org.jetbrains.anko.startActivity
-import org.jetbrains.anko.toast
+import org.jetbrains.anko.*
import kotlin.math.abs
class RssSourceEditActivity :
VMBaseActivity(R.layout.activity_rss_source_edit, false),
+ ViewTreeObserver.OnGlobalLayoutListener,
KeyboardToolPop.CallBack {
private var mSoftKeyboardTool: PopupWindow? = null
private var mIsSoftKeyBoardShowing = false
-
+ private val qrRequestCode = 101
private val adapter = RssSourceEditAdapter()
private val sourceEntities: ArrayList = ArrayList()
@@ -99,9 +103,10 @@ class RssSourceEditActivity :
clipboard?.setPrimaryClip(ClipData.newPlainText(null, sourceStr))
}
}
+ R.id.menu_qr_code_camera -> startActivityForResult(qrRequestCode)
R.id.menu_paste_source -> viewModel.pasteSource { upRecyclerView(it) }
R.id.menu_share_str -> GSON.toJson(getRssSource())?.let { sourceStr ->
- shareText(getString(R.string.share_rss_source), sourceStr)
+ share(sourceStr)
}
R.id.menu_share_qr -> GSON.toJson(getRssSource())?.let { sourceStr ->
shareWithQr(getString(R.string.share_rss_source), sourceStr)
@@ -113,7 +118,7 @@ class RssSourceEditActivity :
private fun initView() {
ATH.applyEdgeEffectColor(recycler_view)
mSoftKeyboardTool = KeyboardToolPop(this, AppConst.keyboardToolChars, this)
- window.decorView.viewTreeObserver.addOnGlobalLayoutListener(KeyboardOnGlobalChangeListener())
+ window.decorView.viewTreeObserver.addOnGlobalLayoutListener(this)
recycler_view.layoutManager = LinearLayoutManager(this)
recycler_view.adapter = adapter
}
@@ -204,23 +209,34 @@ class RssSourceEditActivity :
mSoftKeyboardTool?.dismiss()
}
- private inner class KeyboardOnGlobalChangeListener : ViewTreeObserver.OnGlobalLayoutListener {
- override fun onGlobalLayout() {
- val rect = Rect()
- // 获取当前页面窗口的显示范围
- window.decorView.getWindowVisibleDisplayFrame(rect)
- val screenHeight = this@RssSourceEditActivity.displayMetrics.heightPixels
- val keyboardHeight = screenHeight - rect.bottom // 输入法的高度
- val preShowing = mIsSoftKeyBoardShowing
- if (abs(keyboardHeight) > screenHeight / 5) {
- mIsSoftKeyBoardShowing = true // 超过屏幕五分之一则表示弹出了输入法
- recycler_view.setPadding(0, 0, 0, 100)
- showKeyboardTopPopupWindow()
- } else {
- mIsSoftKeyBoardShowing = false
- recycler_view.setPadding(0, 0, 0, 0)
- if (preShowing) {
- closePopupWindow()
+ override fun onGlobalLayout() {
+ val rect = Rect()
+ // 获取当前页面窗口的显示范围
+ window.decorView.getWindowVisibleDisplayFrame(rect)
+ val screenHeight = this@RssSourceEditActivity.displayMetrics.heightPixels
+ val keyboardHeight = screenHeight - rect.bottom // 输入法的高度
+ val preShowing = mIsSoftKeyBoardShowing
+ if (abs(keyboardHeight) > screenHeight / 5) {
+ mIsSoftKeyBoardShowing = true // 超过屏幕五分之一则表示弹出了输入法
+ recycler_view.setPadding(0, 0, 0, 100)
+ showKeyboardTopPopupWindow()
+ } else {
+ mIsSoftKeyBoardShowing = false
+ recycler_view.setPadding(0, 0, 0, 0)
+ if (preShowing) {
+ closePopupWindow()
+ }
+ }
+ }
+
+ override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ super.onActivityResult(requestCode, resultCode, data)
+ when (requestCode) {
+ qrRequestCode -> if (resultCode == RESULT_OK) {
+ data?.getStringExtra("result")?.let {
+ viewModel.importSource(it) { source: RssSource ->
+ upRecyclerView(source)
+ }
}
}
}
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt b/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt
index ae6e8c739..eb9c4e034 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/edit/RssSourceEditViewModel.kt
@@ -71,4 +71,16 @@ class RssSourceEditViewModel(application: Application) : BaseViewModel(applicati
}
}
}
+
+ fun importSource(text: String, finally: (source: RssSource) -> Unit) {
+ execute {
+ val text1 = text.trim()
+ GSON.fromJsonObject(text1)?.let {
+ finally.invoke(it)
+ }
+ }.onError {
+ toast(it.localizedMessage ?: "Error")
+ }
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
index 0af2de45b..22277476e 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
@@ -50,13 +50,14 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r
get() = getViewModel(RssSourceViewModel::class.java)
private val importRecordKey = "rssSourceRecordKey"
private val qrRequestCode = 101
- private val importSource = 13141
+ private val importSource = 124
private lateinit var adapter: RssSourceAdapter
private var sourceLiveData: LiveData>? = null
private var groups = hashSetOf()
private var groupMenu: SubMenu? = null
override fun onActivityCreated(savedInstanceState: Bundle?) {
+ initUriScheme()
initRecyclerView()
initSearchView()
initLiveDataGroup()
@@ -102,6 +103,22 @@ class RssSourceActivity : VMBaseActivity(R.layout.activity_r
return true
}
+ private fun initUriScheme() {
+ intent.data?.let {
+ when (it.path) {
+ "/importonline" -> it.getQueryParameter("src")?.let { url ->
+ Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show()
+ viewModel.importSource(url) { msg ->
+ title_bar.snackbar(msg)
+ }
+ }
+ else -> {
+ toast("格式不对")
+ }
+ }
+ }
+ }
+
private fun initRecyclerView() {
ATH.applyEdgeEffectColor(recycler_view)
recycler_view.layoutManager = LinearLayoutManager(this)
diff --git a/app/src/main/java/io/legado/app/ui/widget/font/FontAdapter.kt b/app/src/main/java/io/legado/app/ui/widget/font/FontAdapter.kt
index f7c73030a..f2724abcd 100644
--- a/app/src/main/java/io/legado/app/ui/widget/font/FontAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/font/FontAdapter.kt
@@ -20,7 +20,7 @@ class FontAdapter(context: Context, val callBack: CallBack) :
tv_font.typeface = typeface
tv_font.text = item.name
this.onClick { callBack.onClick(item) }
- if (item.name == callBack.curFilePath().substringAfterLast(File.separator)) {
+ if (item.name == callBack.curFilePath.substringAfterLast(File.separator)) {
iv_checked.visible()
} else {
iv_checked.invisible()
@@ -38,6 +38,6 @@ class FontAdapter(context: Context, val callBack: CallBack) :
interface CallBack {
fun onClick(file: File)
- fun curFilePath(): String
+ val curFilePath: String
}
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt b/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt
index be5523b58..87d87e8d6 100644
--- a/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt
@@ -16,7 +16,9 @@ import androidx.recyclerview.widget.LinearLayoutManager
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
+import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
+import io.legado.app.help.AppConfig
import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat
import io.legado.app.lib.dialogs.alert
@@ -87,13 +89,14 @@ class FontSelectDialog : BaseDialogFragment(),
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_default -> {
- val cb = (parentFragment as? CallBack) ?: (activity as? CallBack)
- cb?.let {
- if (it.curFontPath != "") {
- it.selectFile("")
+ val requireContext = requireContext()
+ requireContext.alert(titleResource = R.string.system_typeface) {
+ items(requireContext.resources.getStringArray(R.array.system_typefaces).toList()) { _, i ->
+ AppConfig.systemTypefaces = i
+ onDefaultFontChange()
+ dismiss()
}
- }
- dismiss()
+ }.show()
}
R.id.menu_other -> {
openFolder()
@@ -103,46 +106,48 @@ class FontSelectDialog : BaseDialogFragment(),
}
private fun openFolder() {
- alert {
- titleResource = R.string.select_folder
- items(resources.getStringArray(R.array.select_folder).toList()) { _, index ->
- when (index) {
- 0 -> {
- val path = "${FileUtils.getSdCardPath()}${File.separator}Fonts"
- putPrefString(PreferKey.fontFolder, path)
- getFontFilesByPermission(path)
- }
- 1 -> {
- try {
- val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
- intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- startActivityForResult(intent, fontFolderRequestCode)
- } catch (e: java.lang.Exception) {
- e.printStackTrace()
- requireContext().toast(e.localizedMessage ?: "ERROR")
+ launch(Main) {
+ alert {
+ titleResource = R.string.select_folder
+ items(resources.getStringArray(R.array.select_folder).toList()) { _, index ->
+ when (index) {
+ 0 -> {
+ val path = "${FileUtils.getSdCardPath()}${File.separator}Fonts"
+ putPrefString(PreferKey.fontFolder, path)
+ getFontFilesByPermission(path)
}
- }
- 2 -> {
- PermissionsCompat.Builder(this@FontSelectDialog)
- .addPermissions(*Permissions.Group.STORAGE)
- .rationale(R.string.tip_perm_request_storage)
- .onGranted {
- FileChooserDialog.show(
- childFragmentManager,
- fontFolderRequestCode,
- mode = FileChooserDialog.DIRECTORY
- )
+ 1 -> {
+ try {
+ val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ startActivityForResult(intent, fontFolderRequestCode)
+ } catch (e: java.lang.Exception) {
+ e.printStackTrace()
+ requireContext().toast(e.localizedMessage ?: "ERROR")
}
- .request()
+ }
+ 2 -> {
+ PermissionsCompat.Builder(this@FontSelectDialog)
+ .addPermissions(*Permissions.Group.STORAGE)
+ .rationale(R.string.tip_perm_request_storage)
+ .onGranted {
+ FileChooserDialog.show(
+ childFragmentManager,
+ fontFolderRequestCode,
+ mode = FileChooserDialog.DIRECTORY
+ )
+ }
+ .request()
+ }
}
}
- }
- }.show()
+ }.show()
+ }
}
@SuppressLint("DefaultLocale")
private fun getFontFiles(doc: DocumentFile) {
- launch(IO) {
+ execute {
val docItems = DocumentUtils.listFiles(App.INSTANCE, doc.uri)
fontCacheFolder.listFiles()?.forEach { fontFile ->
var contain = false
@@ -177,6 +182,8 @@ class FontSelectDialog : BaseDialogFragment(),
} catch (e: Exception) {
toast(e.localizedMessage ?: "")
}
+ }.onError {
+ toast("getFontFiles:${it.localizedMessage}")
}
}
@@ -204,12 +211,9 @@ class FontSelectDialog : BaseDialogFragment(),
launch(IO) {
file.copyTo(FileUtils.createFileIfNotExist(fontFolder, file.name), true)
.absolutePath.let { path ->
- val cb = (parentFragment as? CallBack) ?: (activity as? CallBack)
- cb?.let {
- if (it.curFontPath != path) {
- withContext(Main) {
- it.selectFile(path)
- }
+ if (curFilePath != path) {
+ withContext(Main) {
+ callBack?.selectFile(path)
}
}
}
@@ -217,12 +221,6 @@ class FontSelectDialog : BaseDialogFragment(),
}
}
- override fun curFilePath(): String {
- return (parentFragment as? CallBack)?.curFontPath
- ?: (activity as? CallBack)?.curFontPath
- ?: ""
- }
-
override fun onFilePicked(requestCode: Int, currentPath: String) {
when (requestCode) {
fontFolderRequestCode -> {
@@ -255,6 +253,19 @@ class FontSelectDialog : BaseDialogFragment(),
}
}
+ private fun onDefaultFontChange() {
+ if (curFilePath == "") {
+ postEvent(EventBus.UP_CONFIG, true)
+ } else {
+ callBack?.selectFile("")
+ }
+ }
+
+ override val curFilePath: String get() = callBack?.curFontPath ?: ""
+
+ private val callBack: CallBack?
+ get() = (parentFragment as? CallBack) ?: (activity as? CallBack)
+
interface CallBack {
fun selectFile(path: String)
val curFontPath: String
diff --git a/app/src/main/java/io/legado/app/ui/widget/image/CircleImageView.kt b/app/src/main/java/io/legado/app/ui/widget/image/CircleImageView.kt
index 253c1bef3..7f14686e8 100644
--- a/app/src/main/java/io/legado/app/ui/widget/image/CircleImageView.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/image/CircleImageView.kt
@@ -24,7 +24,11 @@ import io.legado.app.utils.sp
import kotlin.math.min
import kotlin.math.pow
-class CircleImageView : AppCompatImageView {
+class CircleImageView(context: Context, attrs: AttributeSet) :
+ AppCompatImageView(
+ context,
+ attrs
+ ) {
private val mDrawableRect = RectF()
private val mBorderRect = RectF()
@@ -59,10 +63,9 @@ class CircleImageView : AppCompatImageView {
private var mBorderOverlay: Boolean = false
var isDisableCircularTransformation: Boolean = false
set(disableCircularTransformation) {
- if (isDisableCircularTransformation == disableCircularTransformation) {
+ if (field == disableCircularTransformation) {
return
}
-
field = disableCircularTransformation
initializeBitmap()
}
@@ -85,7 +88,6 @@ class CircleImageView : AppCompatImageView {
if (circleBackgroundColor == mCircleBackgroundColor) {
return
}
-
mCircleBackgroundColor = circleBackgroundColor
mCircleBackgroundPaint.color = circleBackgroundColor
invalidate()
@@ -117,23 +119,14 @@ class CircleImageView : AppCompatImageView {
private var textColor = context.getCompatColor(R.color.tv_text_default)
- constructor(context: Context) : super(context) {
- init()
- }
-
- @JvmOverloads
- constructor(context: Context, attrs: AttributeSet, defStyle: Int = 0) : super(
- context,
- attrs,
- defStyle
- ) {
-
- val a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0)
-
- mBorderWidth = a.getDimensionPixelSize(
- R.styleable.CircleImageView_civ_border_width,
- DEFAULT_BORDER_WIDTH
- )
+ init {
+ super.setScaleType(SCALE_TYPE)
+ val a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView)
+ mBorderWidth =
+ a.getDimensionPixelSize(
+ R.styleable.CircleImageView_civ_border_width,
+ DEFAULT_BORDER_WIDTH
+ )
mBorderColor =
a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR)
mBorderOverlay =
@@ -152,11 +145,6 @@ class CircleImageView : AppCompatImageView {
}
a.recycle()
- init()
- }
-
- private fun init() {
- super.setScaleType(SCALE_TYPE)
mReady = true
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
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 ff3fa801c..b295e5ad3 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
@@ -167,8 +167,14 @@ class IconListPreference(context: Context, attrs: AttributeSet) : ListPreference
dialogIconNames?.let {
val resId = context.resources
.getIdentifier(it[index].toString(), "mipmap", context.packageName)
- val d = context.getCompatDrawable(resId)
- icon.setImageDrawable(d)
+ val d = try {
+ context.getCompatDrawable(resId)
+ } catch (e: Exception) {
+ null
+ }
+ d?.let {
+ icon.setImageDrawable(d)
+ }
}
label.isChecked = item.toString() == dialogValue
onClick {
diff --git a/app/src/main/java/io/legado/app/utils/BitmapUtils.kt b/app/src/main/java/io/legado/app/utils/BitmapUtils.kt
index 14212f442..371be8122 100644
--- a/app/src/main/java/io/legado/app/utils/BitmapUtils.kt
+++ b/app/src/main/java/io/legado/app/utils/BitmapUtils.kt
@@ -121,7 +121,12 @@ object BitmapUtils {
* @throws IOException
*/
@Throws(IOException::class)
- fun decodeBitmap(context: Context, fileNameInAssets: String, width: Int, height: Int): Bitmap? {
+ fun decodeAssetsBitmap(
+ context: Context,
+ fileNameInAssets: String,
+ width: Int,
+ height: Int
+ ): Bitmap? {
var inputStream = context.assets.open(fileNameInAssets)
val op = BitmapFactory.Options()
diff --git a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt
index 90415484a..3436c9d94 100644
--- a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt
+++ b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt
@@ -92,17 +92,6 @@ val Context.navigationBarHeight: Int
return resources.getDimensionPixelSize(resourceId)
}
-fun Context.shareText(title: String, text: String) {
- try {
- val textIntent = Intent(Intent.ACTION_SEND)
- textIntent.type = "text/plain"
- textIntent.putExtra(Intent.EXTRA_TEXT, text)
- startActivity(Intent.createChooser(textIntent, title))
- } catch (e: Exception) {
- toast(R.string.can_not_share)
- }
-}
-
@SuppressLint("SetWorldReadable")
fun Context.shareWithQr(title: String, text: String) {
QRCodeEncoder.HINTS[EncodeHintType.ERROR_CORRECTION] = ErrorCorrectionLevel.L
@@ -144,6 +133,9 @@ fun Context.sendToClip(text: String) {
}
}
+/**
+ * 系统是否暗色主题
+ */
fun Context.sysIsDarkMode(): Boolean {
val mode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
return mode == Configuration.UI_MODE_NIGHT_YES
@@ -160,8 +152,12 @@ val Context.sysBattery: Int
}
fun Context.openUrl(url: String) {
+ openUrl(Uri.parse(url))
+}
+
+fun Context.openUrl(uri: Uri) {
val intent = Intent(Intent.ACTION_VIEW)
- intent.data = Uri.parse(url)
+ intent.data = uri
if (intent.resolveActivity(packageManager) != null) {
try {
startActivity(intent)
diff --git a/app/src/main/java/io/legado/app/utils/StringExtensions.kt b/app/src/main/java/io/legado/app/utils/StringExtensions.kt
index 63954162e..3f4f40ee2 100644
--- a/app/src/main/java/io/legado/app/utils/StringExtensions.kt
+++ b/app/src/main/java/io/legado/app/utils/StringExtensions.kt
@@ -48,4 +48,14 @@ fun String.splitNotBlank(regex: Regex, limit: Int = 0): Array = run {
this.split(regex, limit).map { it.trim() }.filterNot { it.isBlank() }.toTypedArray()
}
-
+fun Char?.isHAN(): Boolean {
+ this ?: return false
+ val ub: Character.UnicodeBlock = Character.UnicodeBlock.of(this) ?: return false
+ return ub === Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
+ || ub === Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
+ || ub === Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
+ || ub === Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_C
+ || ub === Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D
+ || ub === Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
+ || ub === Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT
+}
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 8eb3cb1f6..b8a457325 100644
--- a/app/src/main/java/io/legado/app/utils/ViewExtensions.kt
+++ b/app/src/main/java/io/legado/app/utils/ViewExtensions.kt
@@ -7,8 +7,10 @@ import android.os.Build
import android.view.View
import android.view.View.*
import android.view.inputmethod.InputMethodManager
+import android.widget.RadioGroup
import android.widget.SeekBar
import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.get
import io.legado.app.App
@@ -69,4 +71,26 @@ fun View.screenshot(): Bitmap? {
fun SeekBar.progressAdd(int: Int) {
progress += int
+}
+
+fun RadioGroup.getIndexById(id: Int): Int {
+ for (i in 0 until this.childCount) {
+ if (id == get(i).id) {
+ return i
+ }
+ }
+ return 0
+}
+
+fun RadioGroup.getCheckedIndex(): Int {
+ for (i in 0 until this.childCount) {
+ if (checkedRadioButtonId == get(i).id) {
+ return i
+ }
+ }
+ return 0
+}
+
+fun RadioGroup.checkByIndex(index: Int) {
+ check(get(index).id)
}
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_bookshelf_config.xml b/app/src/main/res/layout/dialog_bookshelf_config.xml
new file mode 100644
index 000000000..944913f4c
--- /dev/null
+++ b/app/src/main/res/layout/dialog_bookshelf_config.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_number_picker.xml b/app/src/main/res/layout/dialog_number_picker.xml
index 5a471be84..f54fd0a00 100644
--- a/app/src/main/res/layout/dialog_number_picker.xml
+++ b/app/src/main/res/layout/dialog_number_picker.xml
@@ -7,6 +7,7 @@
android:id="@+id/number_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center" />
+ android:layout_gravity="center"
+ android:scrollbars="none" />
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_read_book_style.xml b/app/src/main/res/layout/dialog_read_book_style.xml
index 358d68063..2e6c05061 100644
--- a/app/src/main/res/layout/dialog_read_book_style.xml
+++ b/app/src/main/res/layout/dialog_read_book_style.xml
@@ -269,6 +269,7 @@
android:id="@+id/bg0"
android:layout_width="48dp"
android:layout_height="48dp"
+ android:src="@drawable/image_cover_default"
app:civ_border_color="@color/tv_text_default"
app:civ_border_width="1dp"
app:text="@string/text"
@@ -281,6 +282,7 @@
android:id="@+id/bg1"
android:layout_width="48dp"
android:layout_height="48dp"
+ android:src="@drawable/image_cover_default"
app:civ_border_color="@color/tv_text_default"
app:civ_border_width="1dp"
app:text="@string/text"
@@ -293,6 +295,7 @@
android:id="@+id/bg2"
android:layout_width="48dp"
android:layout_height="48dp"
+ android:src="@drawable/image_cover_default"
app:civ_border_color="@color/tv_text_default"
app:civ_border_width="1dp"
app:text="@string/text"
@@ -305,6 +308,7 @@
android:id="@+id/bg3"
android:layout_width="48dp"
android:layout_height="48dp"
+ android:src="@drawable/image_cover_default"
app:civ_border_color="@color/tv_text_default"
app:civ_border_width="1dp"
app:text="@string/text"
@@ -317,6 +321,7 @@
android:id="@+id/bg4"
android:layout_width="48dp"
android:layout_height="48dp"
+ android:src="@drawable/image_cover_default"
app:civ_border_color="@color/tv_text_default"
app:civ_border_width="1dp"
app:text="@string/text"
diff --git a/app/src/main/res/layout/popup_action_menu.xml b/app/src/main/res/layout/popup_action_menu.xml
index b5273a77a..c6b9067d7 100644
--- a/app/src/main/res/layout/popup_action_menu.xml
+++ b/app/src/main/res/layout/popup_action_menu.xml
@@ -1,8 +1,27 @@
-
+ android:padding="5dp"
+ android:orientation="horizontal">
+
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/content_select_action.xml b/app/src/main/res/menu/content_select_action.xml
index 52ac3e0f6..d11ee6154 100644
--- a/app/src/main/res/menu/content_select_action.xml
+++ b/app/src/main/res/menu/content_select_action.xml
@@ -13,4 +13,7 @@
android:id="@+id/menu_browser"
android:title="@string/browser" />
+
\ No newline at end of file
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index 87fd0d8e0..4cd90b40e 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -35,14 +35,6 @@
- 9
-
- - @string/layout_list
- - @string/layout_grid3
- - @string/layout_grid4
- - @string/layout_grid5
- - @string/layout_grid6
-
-
- @string/indent_0
- @string/indent_1
@@ -111,18 +103,6 @@
- @string/screen_sensor
-
- - @string/bookshelf_px_0
- - @string/bookshelf_px_1
- - @string/bookshelf_px_2
-
-
-
- - 0
- - 1
- - 2
-
-
@@ -152,4 +132,9 @@
- 简体转繁体
+
+ - 系统默认字体
+ - 系统衬线字体
+ - 系统等宽字体
+
\ 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 f64f60c5a..3f54ea194 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -47,12 +47,13 @@
书架还空着,先去添加吧!
搜索
下载
- 列表视图
- 网格视图三列
- 网格视图四列
- 网格视图五列
- 网格视图六列
+ 列表
+ 网格三列
+ 网格四列
+ 网格五列
+ 网格六列
书架布局
+ 视图
书城
添加本地
书源
@@ -192,7 +193,7 @@
封面
书
音量键翻页
- 点击翻页
+ 点击翻页
点击总是翻下一页
翻页动画
屏幕超时
@@ -226,11 +227,11 @@
来源:%s
本地导入
网络导入
- 书架排序
检查更新间隔
- 按阅读时间排序
- 按更新时间排序
- 手动排序
+ 按阅读时间
+ 按更新时间
+ 按书名
+ 手动排序
阅读方式
删除所选
是否确认删除?
@@ -627,4 +628,5 @@
减
加
其它朗读设置
+ 系统内置字体样式
diff --git a/app/src/main/res/xml/pref_config_read.xml b/app/src/main/res/xml/pref_config_read.xml
index 37b089a09..a19a8ba4c 100644
--- a/app/src/main/res/xml/pref_config_read.xml
+++ b/app/src/main/res/xml/pref_config_read.xml
@@ -28,6 +28,12 @@
android:key="volumeKeyPage"
app:iconSpaceReserved="false" />
+
+