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 1b0651e76..4e93e04d7 100644
--- a/app/src/main/assets/txtTocRule.json
+++ b/app/src/main/assets/txtTocRule.json
@@ -1,56 +1,62 @@
[
{
- "enable": true,
- "name": "数字 分隔符 标题名称",
- "rule": "^[ \\t]{0,4}\\d{1,5}[\\,\\., 、\\-].{1,30}$",
- "serialNumber": 0
+ "enable": true,
+ "name": "数字 分隔符 标题名称",
+ "rule": "^[ \\t]{0,4}\\d{1,5}[\\,\\., 、\\-].{1,30}$",
+ "serialNumber": 0
},
{
- "enable": true,
- "name": "特殊符号 序号 标题",
- "rule": "^[ \\t]{0,4}[\\[〈「『〖〔《(【\\(]?[第卷][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10}[\\.:: \f\t].{0,30}$",
- "serialNumber": 1
+ "enable": true,
+ "name": "目录",
+ "rule": "^[ \\t]{0,4}(?:(?:内容|文章)?简介|前言|序章|楔子|正文(?!完)|[Cc]hapter|[Ss]ection|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
+ "serialNumber": 1
},
{
- "enable": true,
- "name": "特殊符号 标题",
- "rule": "^[ \\t]{0,4}[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$",
- "serialNumber": 2
+ "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)[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$",
- "serialNumber": 3
+ "enable": false,
+ "name": "目录(去简介)",
+ "rule": "^(?<=\\s)(?:前言|序章|楔子|正文(?!完)|[Cc]hapter|[Ss]ection|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
+ "serialNumber": 3
},
{
- "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": 4
+ "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": "正文 标题/序号",
- "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": 5
},
{
- "enable": true,
- "name": "目录",
- "rule": "^[ \\t]{0,4}(?:(?:内容|文章)?简介|前言|序章|楔子|正文(?!完)|[Cc]hapter|[Ss]ection|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
- "serialNumber": 6
+ "enable": true,
+ "name": "正文 标题/序号",
+ "rule": "^[ \\t]{0,4}正文\\s{1,4}.{0,20}$",
+ "serialNumber": 6
},
{
- "enable": false,
- "name": "目录(不匹配行前空白,正文不出现双标题)",
- "rule": "^(?<=\\s)(?:(?:内容|文章)?简介|前言|序章|楔子|正文(?!完)|[Cc]hapter|[Ss]ection|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
- "serialNumber": 7
+ "enable": true,
+ "name": "特殊符号 序号 标题",
+ "rule": "^[ \\t]{0,4}[〈〖〔【][第卷][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10}[章节][\\.:: \f\t].{0,30}$",
+ "serialNumber": 7
},
{
- "enable": false,
- "name": "目录(古典小说备用)",
- "rule": "^[ \\t]{0,4}(?:(?:内容|文章)?简介|前言|序章|楔子|正文(?!完)|[Cc]hapter|[Ss]ection|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|场(?![和合比电是])|篇(?!张))).{0,30}$",
- "serialNumber": 8
+ "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
}
]
diff --git a/app/src/main/assets/updateLog.md b/app/src/main/assets/updateLog.md
index 697c57b4b..dea42f86d 100644
--- a/app/src/main/assets/updateLog.md
+++ b/app/src/main/assets/updateLog.md
@@ -2,6 +2,41 @@
* 旧版数据导入教程:
* 先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
+**2020/02/29**
+* 添加书源一键导入
+* 修复主题模式跟随系统
+* 修复书源校验
+* 添加书架排序
+
+**2020/02/28**
+* 解决阅读界面部分字体超出范围的问题
+* 修复背景切换有时空白的bug
+* 修复滚动翻页问题
+
+**2020/02/27**
+* 修复bug,边距调节,换源等一些bug,记不清了
+* 修复默认字体问题
+* 改了下包名,好上架应用市场
+
+**2020/02/26**
+* 修复仿真翻页
+* 功能添加: 选择默认字体时, 可选择字体默认字体(非衬线), 系统衬线字体, 系统等宽字体by hingbong
+
+**2020/02/25**
+* 优化文本选择和滚动,感觉很完美了
+
+**2020/02/24**
+* 滚动暂时可以滚了,先这样吧,头大
+* 紧急修复朗读报错的bug
+
+**2020/02/23**
+* 修复BUG
+* 本地目录正则自定义完成
+* 选择文本修复框选不全的问题,增加操作按钮
+
+**2020/02/22**
+* 长按选择完成
+
**2020/02/21**
* 重写了阅读界面,实现了段距调整,两端对齐,页眉页脚调整
* 选择文本暂不可用,滚动暂不可用,仿真翻页还有问题
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/base/adapter/CommonRecyclerAdapter.kt b/app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt
index 25e6b25c4..f864aa4e8 100644
--- a/app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt
+++ b/app/src/main/java/io/legado/app/base/adapter/CommonRecyclerAdapter.kt
@@ -293,6 +293,11 @@ abstract class CommonRecyclerAdapter- (protected val context: Context) :
fun getItem(position: Int): ITEM? =
if (position in 0 until items.size) items[position] else null
+ fun getItemByLayoutPosition(position: Int): ITEM? {
+ val pos = position - getHeaderCount()
+ return if (pos in 0 until items.size) items[pos] else null
+ }
+
fun getItems(): List
- = items
protected open fun getItemViewType(item: ITEM, position: Int): Int {
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 320bc87ee..225154a97 100644
--- a/app/src/main/java/io/legado/app/constant/PreferKey.kt
+++ b/app/src/main/java/io/legado/app/constant/PreferKey.kt
@@ -16,6 +16,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"
@@ -36,4 +37,7 @@ object PreferKey {
const val textSelectAble = "selectText"
const val lastBackup = "lastBackup"
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/dao/BookSourceDao.kt b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
index 0981a94ea..9ce5c65be 100644
--- a/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
+++ b/app/src/main/java/io/legado/app/data/dao/BookSourceDao.kt
@@ -14,19 +14,19 @@ interface BookSourceDao {
@Query("select * from book_sources where bookSourceName like :searchKey or bookSourceGroup like :searchKey or bookSourceUrl like :searchKey order by customOrder asc")
fun liveDataSearch(searchKey: String = ""): LiveData
>
- @Query("select * from book_sources where enabled = 1 and enabledExplore = 1 and exploreUrl is not null and exploreUrl <> '' order by customOrder asc")
+ @Query("select * from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' order by customOrder asc")
fun liveExplore(): LiveData>
- @Query("select * from book_sources where enabled = 1 and enabledExplore = 1 and exploreUrl is not null and exploreUrl <> '' and (bookSourceGroup like :key or bookSourceName like :key) order by customOrder asc")
+ @Query("select * from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' and (bookSourceGroup like :key or bookSourceName like :key) order by customOrder asc")
fun liveExplore(key: String): LiveData>
- @Query("select bookSourceGroup from book_sources where bookSourceGroup is not null and bookSourceGroup <> ''")
+ @Query("select bookSourceGroup from book_sources where trim(bookSourceGroup) <> ''")
fun liveGroup(): LiveData>
- @Query("select bookSourceGroup from book_sources where enabled = 1 and bookSourceGroup is not null and bookSourceGroup <> ''")
+ @Query("select bookSourceGroup from book_sources where enabled = 1 and trim(bookSourceGroup) <> ''")
fun liveGroupEnabled(): LiveData>
- @Query("select bookSourceGroup from book_sources where enabled = 1 and enabledExplore = 1 and exploreUrl is not null and exploreUrl <> '' and bookSourceGroup is not null and bookSourceGroup <> ''")
+ @Query("select bookSourceGroup from book_sources where enabledExplore = 1 and trim(exploreUrl) <> '' and trim(bookSourceGroup) <> ''")
fun liveGroupExplore(): LiveData>
@Query("select distinct enabled from book_sources where bookSourceName like :searchKey or bookSourceGroup like :searchKey or bookSourceUrl like :searchKey")
@@ -41,7 +41,7 @@ interface BookSourceDao {
@Query("select * from book_sources where enabled = 1 and bookSourceGroup like '%' || :group || '%'")
fun getEnabledByGroup(group: String): List
- @get:Query("select * from book_sources where bookUrlPattern is not null || bookUrlPattern <> ''")
+ @get:Query("select * from book_sources where trim(bookUrlPattern) <> ''")
val hasBookUrlPattern: List
@get:Query("select * from book_sources where bookSourceGroup is null or bookSourceGroup = ''")
diff --git a/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt b/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt
index c1c1e85d9..5436ee26c 100644
--- a/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt
+++ b/app/src/main/java/io/legado/app/data/dao/RssSourceDao.kt
@@ -28,7 +28,7 @@ interface RssSourceDao {
@Query("SELECT * FROM rssSources where enabled = 1 order by customOrder")
fun liveEnabled(): LiveData>
- @Query("select sourceGroup from rssSources where sourceGroup is not null and sourceGroup <> ''")
+ @Query("select sourceGroup from rssSources where trim(sourceGroup) <> ''")
fun liveGroup(): LiveData>
@get:Query("select min(customOrder) from rssSources")
diff --git a/app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt b/app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt
index 119daa651..5812b419c 100644
--- a/app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt
+++ b/app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt
@@ -1,11 +1,15 @@
package io.legado.app.data.dao
+import androidx.lifecycle.LiveData
import androidx.room.*
import io.legado.app.data.entities.TxtTocRule
@Dao
interface TxtTocRuleDao {
+ @Query("select * from txtTocRules order by serialNumber")
+ fun observeAll(): LiveData>
+
@get:Query("select * from txtTocRules order by serialNumber")
val all: List
@@ -15,6 +19,10 @@ interface TxtTocRuleDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg rule: TxtTocRule)
+ @Update(onConflict = OnConflictStrategy.REPLACE)
+ fun update(vararg rule: TxtTocRule)
+
@Delete
fun delete(vararg rule: TxtTocRule)
+
}
\ 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/data/entities/TxtTocRule.kt b/app/src/main/java/io/legado/app/data/entities/TxtTocRule.kt
index c9cd51d4d..cdfca7567 100644
--- a/app/src/main/java/io/legado/app/data/entities/TxtTocRule.kt
+++ b/app/src/main/java/io/legado/app/data/entities/TxtTocRule.kt
@@ -9,6 +9,6 @@ data class TxtTocRule(
@PrimaryKey
var name: String = "",
var rule: String = "",
- var serialNumber: Int,
+ var serialNumber: Int = -1,
var enable: Boolean = true
)
\ No newline at end of file
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/BookHelp.kt b/app/src/main/java/io/legado/app/help/BookHelp.kt
index e474bff96..c7560b819 100644
--- a/app/src/main/java/io/legado/app/help/BookHelp.kt
+++ b/app/src/main/java/io/legado/app/help/BookHelp.kt
@@ -237,8 +237,14 @@ object BookHelp {
private var bookName: String? = null
private var bookOrigin: String? = null
private var replaceRules: List = arrayListOf()
- val bodyIndent
- get() = " ".repeat(App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2))
+ var bodyIndentCount = App.INSTANCE.getPrefInt(PreferKey.bodyIndent, 2)
+ set(value) {
+ field = value
+ App.INSTANCE.putPrefInt(PreferKey.bodyIndent, value)
+ bodyIndent = " ".repeat(value)
+ }
+ var bodyIndent = " ".repeat(bodyIndentCount)
+
fun disposeContent(
title: String,
@@ -247,7 +253,6 @@ object BookHelp {
content: String,
enableReplace: Boolean
): String {
- var c = content
synchronized(this) {
if (enableReplace && (bookName != name || bookOrigin != origin)) {
replaceRules = if (origin.isNullOrEmpty()) {
@@ -257,6 +262,7 @@ object BookHelp {
}
}
}
+ var c = content
for (item in replaceRules) {
item.pattern.let {
if (it.isNotEmpty()) {
@@ -268,8 +274,8 @@ object BookHelp {
}
}
}
- if (!content.substringBefore("\n").contains(title)) {
- c = title + "\n" + c
+ if (!c.substringBefore("\n").contains(title)) {
+ c = "$title\n$c"
}
when (AppConfig.chineseConverterType) {
1 -> c = ZhConvertBootstrap.newInstance().toSimple(c)
diff --git a/app/src/main/java/io/legado/app/help/FirstTopListUpCallback.kt b/app/src/main/java/io/legado/app/help/FirstTopListUpCallback.kt
deleted file mode 100644
index a35f0cd8c..000000000
--- a/app/src/main/java/io/legado/app/help/FirstTopListUpCallback.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-package io.legado.app.help
-
-import androidx.recyclerview.widget.ListUpdateCallback
-import androidx.recyclerview.widget.RecyclerView
-import io.legado.app.base.adapter.ItemViewHolder
-
-class FirstTopListUpCallback : ListUpdateCallback {
- var firstInsert = -1
- lateinit var adapter: RecyclerView.Adapter
-
- override fun onChanged(position: Int, count: Int, payload: Any?) {
- adapter.notifyItemRangeChanged(position, count, payload)
- }
-
- override fun onMoved(fromPosition: Int, toPosition: Int) {
- if (toPosition == 0) {
- firstInsert = 0
- }
- adapter.notifyItemMoved(fromPosition, toPosition)
- }
-
- override fun onInserted(position: Int, count: Int) {
- if (firstInsert == -1 || firstInsert > position) {
- firstInsert = position
- }
- adapter.notifyItemRangeInserted(position, count)
- }
-
- override fun onRemoved(position: Int, count: Int) {
- adapter.notifyItemRangeRemoved(position, count)
- }
-
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/help/ItemTouchCallback.kt b/app/src/main/java/io/legado/app/help/ItemTouchCallback.kt
index 0e9a29351..99ba74ef1 100644
--- a/app/src/main/java/io/legado/app/help/ItemTouchCallback.kt
+++ b/app/src/main/java/io/legado/app/help/ItemTouchCallback.kt
@@ -128,7 +128,7 @@ class ItemTouchCallback : ItemTouchHelper.Callback() {
* @return 开发者处理了操作应该返回true,开发者没有处理就返回false
*/
fun onMove(srcPosition: Int, targetPosition: Int): Boolean {
- return false
+ return true
}
fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
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 5dc2d4817..57ccd5f14 100644
--- a/app/src/main/java/io/legado/app/help/ReadBookConfig.kt
+++ b/app/src/main/java/io/legado/app/help/ReadBookConfig.kt
@@ -6,6 +6,7 @@ import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import io.legado.app.App
import io.legado.app.R
+import io.legado.app.constant.PreferKey
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.ui.book.read.page.ChapterProvider
import io.legado.app.utils.*
@@ -23,12 +24,25 @@ object ReadBookConfig {
val json = String(App.INSTANCE.assets.open(readConfigFileName).readBytes())
GSON.fromJsonArray(json)!!
}
- val durConfig
- get() = getConfig(styleSelect)
-
- var styleSelect
- get() = App.INSTANCE.getPrefInt("readStyleSelect")
- set(value) = App.INSTANCE.putPrefInt("readStyleSelect", value)
+ val durConfig get() = getConfig(styleSelect)
+ private val shareConfig get() = getConfig(5)
+ var styleSelect = App.INSTANCE.getPrefInt(PreferKey.readStyleSelect)
+ set(value) {
+ field = value
+ App.INSTANCE.putPrefInt(PreferKey.readStyleSelect, value)
+ }
+ var shareLayout = App.INSTANCE.getPrefBoolean(PreferKey.shareLayout)
+ set(value) {
+ field = value
+ App.INSTANCE.putPrefBoolean(PreferKey.shareLayout, value)
+ }
+ var pageAnim = App.INSTANCE.getPrefInt(PreferKey.pageAnim)
+ set(value) {
+ field = value
+ isScroll = value == 3
+ App.INSTANCE.putPrefInt(PreferKey.pageAnim, value)
+ }
+ var isScroll = pageAnim == 3
var bg: Drawable? = null
init {
@@ -40,6 +54,9 @@ object ReadBookConfig {
if (configList.size < 5) {
resetAll()
}
+ if (configList.size < 6) {
+ configList.add(Config())
+ }
return configList[index]
}
@@ -94,20 +111,122 @@ object ReadBookConfig {
}
}
- data class Config(
- var bgStr: String = "#EEEEEE",//白天背景
- var bgStrNight: String = "#000000",//夜间背景
- var bgType: Int = 0,//白天背景类型
- var bgTypeNight: Int = 0,//夜间背景类型
- var darkStatusIcon: Boolean = true,//白天是否暗色状态栏
- var darkStatusIconNight: Boolean = false,//晚上是否暗色状态栏
- var textColor: String = "#3E3D3B",//白天文字颜色
- var textColorNight: String = "#adadad",//夜间文字颜色
+ //配置写入读取
+ var hideStatusBar = App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar)
+ var hideNavigationBar = App.INSTANCE.getPrefBoolean(PreferKey.hideNavigationBar)
+ var textBold: Boolean
+ get() = if (shareLayout) shareConfig.textBold else durConfig.textBold
+ set(value) = if (shareLayout) shareConfig.textBold = value else durConfig.textBold = value
+
+ var textSize: Int
+ get() = if (shareLayout) shareConfig.textSize else durConfig.textSize
+ set(value) = if (shareLayout) shareConfig.textSize = value else durConfig.textSize = value
+
+ var letterSpacing: Float
+ get() = if (shareLayout) shareConfig.letterSpacing else durConfig.letterSpacing
+ set(value) =
+ if (shareLayout) shareConfig.letterSpacing = value else durConfig.letterSpacing = value
+
+ var lineSpacingExtra: Int
+ get() = if (shareLayout) shareConfig.lineSpacingExtra else durConfig.lineSpacingExtra
+ set(value) =
+ if (shareLayout) shareConfig.lineSpacingExtra = value
+ else durConfig.lineSpacingExtra = value
+
+ var paragraphSpacing: Int
+ get() = if (shareLayout) shareConfig.paragraphSpacing else durConfig.paragraphSpacing
+ set(value) =
+ if (shareLayout) shareConfig.paragraphSpacing = value
+ else durConfig.paragraphSpacing = value
+
+ var titleCenter: Boolean
+ get() = if (shareLayout) shareConfig.titleCenter else durConfig.titleCenter
+ set(value) =
+ if (shareLayout) shareConfig.titleCenter = value else durConfig.titleCenter = value
+
+ var paddingBottom: Int
+ get() = if (shareLayout) shareConfig.paddingBottom else durConfig.paddingBottom
+ set(value) =
+ if (shareLayout) shareConfig.paddingBottom = value else durConfig.paddingBottom = value
+
+ var paddingLeft: Int
+ get() = if (shareLayout) shareConfig.paddingLeft else durConfig.paddingLeft
+ set(value) =
+ if (shareLayout) shareConfig.paddingLeft = value else durConfig.paddingLeft = value
+
+ var paddingRight: Int
+ get() = if (shareLayout) shareConfig.paddingRight else durConfig.paddingRight
+ set(value) =
+ if (shareLayout) shareConfig.paddingRight = value else durConfig.paddingRight = value
+
+ var paddingTop: Int
+ get() = if (shareLayout) shareConfig.paddingTop else durConfig.paddingTop
+ set(value) =
+ if (shareLayout) shareConfig.paddingTop = value else durConfig.paddingTop = value
+
+ var headerPaddingBottom: Int
+ get() = if (shareLayout) shareConfig.headerPaddingBottom else durConfig.headerPaddingBottom
+ set(value) =
+ if (shareLayout) shareConfig.headerPaddingBottom = value
+ else durConfig.headerPaddingBottom = value
+
+ var headerPaddingLeft: Int
+ get() = if (shareLayout) shareConfig.headerPaddingLeft else durConfig.headerPaddingLeft
+ set(value) =
+ if (shareLayout) shareConfig.headerPaddingLeft = value
+ else durConfig.headerPaddingLeft = value
+
+ var headerPaddingRight: Int
+ get() = if (shareLayout) shareConfig.headerPaddingRight else durConfig.headerPaddingRight
+ set(value) =
+ if (shareLayout) shareConfig.headerPaddingRight = value
+ else durConfig.headerPaddingRight = value
+
+ var headerPaddingTop: Int
+ get() = if (shareLayout) shareConfig.headerPaddingTop else durConfig.headerPaddingTop
+ set(value) =
+ if (shareLayout) shareConfig.headerPaddingTop = value
+ else durConfig.headerPaddingTop = value
+
+ var footerPaddingBottom: Int
+ get() = if (shareLayout) shareConfig.footerPaddingBottom else durConfig.footerPaddingBottom
+ set(value) =
+ if (shareLayout) shareConfig.footerPaddingBottom = value
+ else durConfig.footerPaddingBottom = value
+
+ var footerPaddingLeft: Int
+ get() = if (shareLayout) shareConfig.footerPaddingLeft else durConfig.footerPaddingLeft
+ set(value) =
+ if (shareLayout) shareConfig.footerPaddingLeft = value
+ else durConfig.footerPaddingLeft = value
+
+ var footerPaddingRight: Int
+ get() = if (shareLayout) shareConfig.footerPaddingRight else durConfig.footerPaddingRight
+ set(value) =
+ if (shareLayout) shareConfig.footerPaddingRight = value
+ else durConfig.footerPaddingRight = value
+
+ var footerPaddingTop: Int
+ get() = if (shareLayout) shareConfig.footerPaddingTop else durConfig.footerPaddingTop
+ set(value) =
+ if (shareLayout) shareConfig.footerPaddingTop = value
+ else durConfig.footerPaddingTop = value
+
+ class Config(
+ private var bgStr: String = "#EEEEEE",//白天背景
+ private var bgStrNight: String = "#000000",//夜间背景
+ private var bgType: Int = 0,//白天背景类型 0:颜色, 1:assets图片, 2其它图片
+ private var bgTypeNight: Int = 0,//夜间背景类型
+ private var darkStatusIcon: Boolean = true,//白天是否暗色状态栏
+ private var darkStatusIconNight: Boolean = false,//晚上是否暗色状态栏
+ private var textColor: String = "#3E3D3B",//白天文字颜色
+ private var textColorNight: String = "#ADADAD",//夜间文字颜色
var textBold: Boolean = false,//是否粗体字
- var textSize: Int = 15,//文字大小
+ var textSize: Int = 20,//文字大小
var letterSpacing: Float = 1f,//字间距
var lineSpacingExtra: Int = 12,//行间距
- var paragraphSpacing: Int = 12,
+ var paragraphSpacing: Int = 12,//段距
+ var titleCenter: Boolean = true,//标题居中
var paddingBottom: Int = 6,
var paddingLeft: Int = 16,
var paddingRight: Int = 16,
@@ -137,7 +256,7 @@ object ReadBookConfig {
} else {
textColor = "#${color.hexString}"
}
- ChapterProvider.upStyle(this)
+ ChapterProvider.upStyle()
}
fun setStatusIconDark(isDark: Boolean) {
@@ -180,7 +299,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 c7de266ca..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,13 +100,9 @@ object Backup {
for (fileName in backupFileNames) {
val file = File(backupPath + File.separator + fileName)
if (file.exists()) {
- val doc = treeDoc.findFile(fileName) ?: treeDoc.createFile("", fileName)
- doc?.let {
- DocumentUtils.writeText(
- context,
- file.readText(),
- doc.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/Restore.kt b/app/src/main/java/io/legado/app/help/storage/Restore.kt
index 123332df6..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
@@ -104,8 +106,17 @@ object Restore {
else -> Unit
}
edit.putInt(PreferKey.versionCode, App.INSTANCE.versionCode)
- edit.commit()
+ edit.apply()
}
+ ReadBookConfig.apply {
+ styleSelect = App.INSTANCE.getPrefInt(PreferKey.readStyleSelect)
+ shareLayout = App.INSTANCE.getPrefBoolean(PreferKey.shareLayout)
+ pageAnim = App.INSTANCE.getPrefInt(PreferKey.pageAnim)
+ 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/localBook/AnalyzeTxtFile.kt b/app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt
index 99b6b95d3..fad747b34 100644
--- a/app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt
+++ b/app/src/main/java/io/legado/app/model/localBook/AnalyzeTxtFile.kt
@@ -205,6 +205,9 @@ object AnalyzeTxtFile {
}
private fun getTocRule(book: Book, bookStream: RandomAccessFile, charset: Charset): Pattern? {
+ if (book.tocUrl.isNotEmpty()) {
+ return Pattern.compile(book.tocUrl, Pattern.MULTILINE)
+ }
val tocRules = getTocRules()
var rulePattern: Pattern? = null
//首先获取128k的数据
@@ -227,13 +230,18 @@ object AnalyzeTxtFile {
private fun getTocRules(): List {
val rules = App.db.txtTocRule().all
if (rules.isEmpty()) {
- App.INSTANCE.assets.open("txtTocRule.json").readBytes().let { byteArray ->
- GSON.fromJsonArray(String(byteArray))?.let {
- App.db.txtTocRule().insert(*it.toTypedArray())
- return it
- }
- }
+ return getDefaultRules()
}
return rules
}
+
+ fun getDefaultRules(): List {
+ App.INSTANCE.assets.open("txtTocRule.json").readBytes().let { byteArray ->
+ GSON.fromJsonArray(String(byteArray))?.let {
+ App.db.txtTocRule().insert(*it.toTypedArray())
+ return it
+ }
+ }
+ return emptyList()
+ }
}
\ No newline at end of file
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 b9455e6ff..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,11 +33,15 @@ 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)
- content.append(contentData.content)
+ 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]
val nextChapterUrl = if (!nextChapterUrlF.isNullOrEmpty())
@@ -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/receiver/MediaButtonReceiver.kt b/app/src/main/java/io/legado/app/receiver/MediaButtonReceiver.kt
index 12083c652..00c9d0522 100644
--- a/app/src/main/java/io/legado/app/receiver/MediaButtonReceiver.kt
+++ b/app/src/main/java/io/legado/app/receiver/MediaButtonReceiver.kt
@@ -45,7 +45,7 @@ class MediaButtonReceiver : BroadcastReceiver() {
}
}
}
- return false
+ return true
}
private fun readAloud(context: Context) {
diff --git a/app/src/main/java/io/legado/app/receiver/TimeElectricityReceiver.kt b/app/src/main/java/io/legado/app/receiver/TimeBatteryReceiver.kt
similarity index 85%
rename from app/src/main/java/io/legado/app/receiver/TimeElectricityReceiver.kt
rename to app/src/main/java/io/legado/app/receiver/TimeBatteryReceiver.kt
index 765d05b3e..ce2e04fbe 100644
--- a/app/src/main/java/io/legado/app/receiver/TimeElectricityReceiver.kt
+++ b/app/src/main/java/io/legado/app/receiver/TimeBatteryReceiver.kt
@@ -9,12 +9,12 @@ import io.legado.app.constant.EventBus
import io.legado.app.utils.postEvent
-class TimeElectricityReceiver : BroadcastReceiver() {
+class TimeBatteryReceiver : BroadcastReceiver() {
companion object {
- fun register(context: Context): TimeElectricityReceiver {
- val receiver = TimeElectricityReceiver()
+ fun register(context: Context): TimeBatteryReceiver {
+ val receiver = TimeBatteryReceiver()
val filter = IntentFilter()
filter.addAction(Intent.ACTION_TIME_TICK)
filter.addAction(Intent.ACTION_BATTERY_CHANGED)
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/service/help/ReadBook.kt b/app/src/main/java/io/legado/app/service/help/ReadBook.kt
index 41bfa3c77..207c34ec6 100644
--- a/app/src/main/java/io/legado/app/service/help/ReadBook.kt
+++ b/app/src/main/java/io/legado/app/service/help/ReadBook.kt
@@ -17,6 +17,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
+import org.jetbrains.anko.toast
object ReadBook {
@@ -25,10 +26,10 @@ object ReadBook {
var book: Book? = null
var inBookshelf = false
var chapterSize = 0
- var callBack: CallBack? = null
var durChapterIndex = 0
var durPageIndex = 0
var isLocalBook = true
+ var callBack: CallBack? = null
var prevTextChapter: TextChapter? = null
var curTextChapter: TextChapter? = null
var nextTextChapter: TextChapter? = null
@@ -126,7 +127,13 @@ object ReadBook {
saveRead()
}
- fun curPageChanged() {
+ fun setPageIndex(pageIndex: Int) {
+ durPageIndex = pageIndex
+ saveRead()
+ curPageChanged()
+ }
+
+ private fun curPageChanged() {
callBack?.upPageProgress()
if (BaseReadAloudService.isRun) {
readAloud(!BaseReadAloudService.pause)
@@ -283,6 +290,9 @@ object ReadBook {
}
}
}
+ }.onError {
+ it.printStackTrace()
+ App.INSTANCE.toast(it.localizedMessage ?: "ChapterProvider ERROR")
}
}
@@ -302,7 +312,7 @@ object ReadBook {
}
interface CallBack {
- fun upContent(position: Int = 0)
+ fun upContent(relativePosition: Int = 0)
fun upView()
fun upPageProgress()
fun contentLoadFinish()
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 18e3bcdb9..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
@@ -1,14 +1,12 @@
package io.legado.app.ui.about
-import android.content.Intent
-import android.net.Uri
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import io.legado.app.R
import io.legado.app.base.BaseActivity
-import io.legado.app.utils.shareText
-import org.jetbrains.anko.toast
+import io.legado.app.utils.openUrl
+import org.jetbrains.anko.share
class AboutActivity : BaseActivity(R.layout.activity_about) {
@@ -28,22 +26,13 @@ class AboutActivity : BaseActivity(R.layout.activity_about) {
override fun onCompatOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
- R.id.menu_scoring -> openIntent("market://details?id=$packageName")
- R.id.menu_share_it -> shareText(
- "App Share",
- getString(R.string.app_share_description)
+ R.id.menu_scoring -> openUrl("market://details?id=$packageName")
+ R.id.menu_share_it -> share(
+ getString(R.string.app_share_description),
+ getString(R.string.app_name)
)
}
return super.onCompatOptionsItemSelected(item)
}
- private fun openIntent(address: String) {
- try {
- val intent = Intent(Intent.ACTION_VIEW)
- intent.data = Uri.parse(address)
- startActivity(intent)
- } catch (e: Exception) {
- toast(R.string.can_not_open)
- }
- }
}
diff --git a/app/src/main/java/io/legado/app/ui/about/AboutFragment.kt b/app/src/main/java/io/legado/app/ui/about/AboutFragment.kt
index 454fda8e9..365381fbd 100644
--- a/app/src/main/java/io/legado/app/ui/about/AboutFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/about/AboutFragment.kt
@@ -1,8 +1,5 @@
package io.legado.app.ui.about
-import android.content.ClipData
-import android.content.ClipboardManager
-import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
@@ -14,6 +11,8 @@ import io.legado.app.App
import io.legado.app.R
import io.legado.app.lib.dialogs.alert
import io.legado.app.ui.widget.dialog.TextDialog
+import io.legado.app.utils.openUrl
+import io.legado.app.utils.sendToClip
import io.legado.app.utils.toast
class AboutFragment : PreferenceFragmentCompat() {
@@ -43,32 +42,32 @@ class AboutFragment : PreferenceFragmentCompat() {
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
when (preference?.key) {
- "contributors" -> openIntent(Intent.ACTION_VIEW, R.string.contributors_url)
+ "contributors" -> openUrl(R.string.contributors_url)
"update_log" -> showUpdateLog()
- "check_update" -> openIntent(Intent.ACTION_VIEW, R.string.latest_release_url)
- "mail" -> openIntent(Intent.ACTION_SENDTO, "mailto:kunfei.ge@gmail.com")
- "git" -> openIntent(Intent.ACTION_VIEW, R.string.this_github_url)
- "home_page" -> openIntent(Intent.ACTION_VIEW, R.string.home_page_url)
- "license" -> openIntent(Intent.ACTION_VIEW, licenseUrl)
- "disclaimer" -> openIntent(Intent.ACTION_VIEW, disclaimerUrl)
+ "check_update" -> openUrl(R.string.latest_release_url)
+ "mail" -> sendMail()
+ "git" -> openUrl(R.string.this_github_url)
+ "home_page" -> openUrl(R.string.home_page_url)
+ "license" -> requireContext().openUrl(licenseUrl)
+ "disclaimer" -> requireContext().openUrl(disclaimerUrl)
"qq" -> showQqGroups()
- "gzGzh" -> sendToClip("开源阅读软件")
+ "gzGzh" -> requireContext().sendToClip("开源阅读软件")
}
return super.onPreferenceTreeClick(preference)
}
@Suppress("SameParameterValue")
- private fun openIntent(intentName: String, @StringRes addressID: Int) {
- openIntent(intentName, getString(addressID))
+ private fun openUrl(@StringRes addressID: Int) {
+ requireContext().openUrl(getString(addressID))
}
- private fun openIntent(intentName: String, address: String) {
+ private fun sendMail() {
try {
- val intent = Intent(intentName)
- intent.data = Uri.parse(address)
+ val intent = Intent(Intent.ACTION_SENDTO)
+ intent.data = Uri.parse("mailto:kunfei.ge@gmail.com")
startActivity(intent)
} catch (e: Exception) {
- toast(R.string.can_not_open)
+ toast(e.localizedMessage ?: "Error")
}
}
@@ -86,7 +85,7 @@ class AboutFragment : PreferenceFragmentCompat() {
items(names) { _, index ->
qqGroups[names[index]]?.let {
if (!joinQQGroup(it)) {
- sendToClip(it)
+ requireContext().sendToClip(it)
}
}
}
@@ -107,13 +106,4 @@ class AboutFragment : PreferenceFragmentCompat() {
}
}
- private fun sendToClip(text: String) {
- val clipboard =
- requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
- val clipData = ClipData.newPlainText(null, text)
- clipboard?.let {
- clipboard.setPrimaryClip(clipData)
- toast(R.string.copy_complete)
- }
- }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/about/DonateFragment.kt b/app/src/main/java/io/legado/app/ui/about/DonateFragment.kt
index a202946b6..0270b3f77 100644
--- a/app/src/main/java/io/legado/app/ui/about/DonateFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/about/DonateFragment.kt
@@ -1,7 +1,5 @@
package io.legado.app.ui.about
-import android.content.ClipData
-import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.net.Uri
@@ -11,7 +9,8 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat
import io.legado.app.R
import io.legado.app.utils.ACache
-import io.legado.app.utils.toast
+import io.legado.app.utils.openUrl
+import io.legado.app.utils.sendToClip
import org.jetbrains.anko.longToast
import java.net.URLEncoder
@@ -33,40 +32,19 @@ class DonateFragment : PreferenceFragmentCompat() {
override fun onPreferenceTreeClick(preference: Preference?): Boolean {
when (preference?.key) {
- "wxZsm" -> openIntent(Intent.ACTION_VIEW, wxZsRwmUrl)
- "zfbHbRwm" -> openIntent(Intent.ACTION_VIEW, zfbHbRwmUrl)
- "zfbSkRwm" -> openIntent(Intent.ACTION_VIEW, zfbSkRwmUrl)
- "qqSkRwm" -> openIntent(Intent.ACTION_VIEW, qqSkRwmUrl)
+ "wxZsm" -> requireContext().openUrl(wxZsRwmUrl)
+ "zfbHbRwm" -> requireContext().openUrl(zfbHbRwmUrl)
+ "zfbSkRwm" -> requireContext().openUrl(zfbSkRwmUrl)
+ "qqSkRwm" -> requireContext().openUrl(qqSkRwmUrl)
"zfbSk" -> aliDonate(requireContext())
"zfbHbSsm" -> getZfbHb(requireContext())
- "gzGzh" -> sendToClip("开源阅读软件")
+ "gzGzh" -> requireContext().sendToClip("开源阅读软件")
}
return super.onPreferenceTreeClick(preference)
}
- @Suppress("SameParameterValue")
- private fun openIntent(intentName: String, address: String) {
- try {
- val intent = Intent(intentName)
- intent.data = Uri.parse(address)
- startActivity(intent)
- } catch (e: Exception) {
- toast(R.string.can_not_open)
- }
- }
-
- private fun sendToClip(text: String) {
- val clipboard =
- requireContext().getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
- val clipData = ClipData.newPlainText(null, text)
- clipboard?.let {
- clipboard.setPrimaryClip(clipData)
- toast(R.string.copy_complete)
- }
- }
-
private fun getZfbHb(context: Context) {
- sendToClip("537954522")
+ requireContext().sendToClip("537954522")
context.longToast("高级功能已开启\n红包码已复制\n支付宝首页搜索“537954522” 立即领红包")
try {
val packageManager = context.applicationContext.packageManager
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 e3ca00964..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
@@ -19,8 +19,8 @@ import io.legado.app.lib.theme.ATH
import io.legado.app.ui.book.group.GroupManageDialog
import io.legado.app.ui.book.group.GroupSelectDialog
import io.legado.app.ui.widget.SelectActionBar
+import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
-import io.legado.app.utils.getVerticalDivider
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_arrange_book.*
@@ -60,7 +60,7 @@ class ArrangeBookActivity : VMBaseActivity(R.layout.activi
private fun initView() {
ATH.applyEdgeEffectColor(recycler_view)
recycler_view.layoutManager = LinearLayoutManager(this)
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(this))
adapter = ArrangeBookAdapter(this, this)
recycler_view.adapter = adapter
select_action_bar.setMainActionText(R.string.move_to_group)
@@ -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/group/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
index e982cbbe0..1a4322da6 100644
--- a/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/group/GroupManageDialog.kt
@@ -28,8 +28,8 @@ 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.yesButton
+import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
-import io.legado.app.utils.getVerticalDivider
import io.legado.app.utils.getViewModel
import io.legado.app.utils.requestInputMethod
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
@@ -77,7 +77,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
.isChecked = AppConst.bookGroupAudioShow
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
App.db.bookGroupDao().liveDataAll().observe(viewLifecycleOwner, Observer {
val diffResult =
diff --git a/app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt b/app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt
index 2d90a82e2..b290bc5df 100644
--- a/app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/group/GroupSelectDialog.kt
@@ -28,8 +28,8 @@ import io.legado.app.lib.dialogs.customView
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.accentColor
+import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.applyTint
-import io.legado.app.utils.getVerticalDivider
import io.legado.app.utils.getViewModel
import io.legado.app.utils.requestInputMethod
import kotlinx.android.synthetic.main.dialog_book_group_picker.*
@@ -98,7 +98,7 @@ class GroupSelectDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
tool_bar.menu.findItem(R.id.menu_group_audio).isVisible = false
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter
diff --git a/app/src/main/java/io/legado/app/ui/book/read/Help.kt b/app/src/main/java/io/legado/app/ui/book/read/Help.kt
index 3a3e04191..549b7b987 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/Help.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/Help.kt
@@ -10,7 +10,6 @@ import android.view.View.NO_ID
import android.widget.EditText
import io.legado.app.App
import io.legado.app.R
-import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Bookmark
import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
@@ -23,7 +22,6 @@ import io.legado.app.lib.theme.ThemeStore
import io.legado.app.service.help.Download
import io.legado.app.service.help.ReadBook
import io.legado.app.utils.applyTint
-import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.requestInputMethod
import kotlinx.android.synthetic.main.dialog_download_choice.view.*
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
@@ -42,15 +40,14 @@ object Help {
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
- val hideNavigationBar = App.INSTANCE.getPrefBoolean(PreferKey.hideNavigationBar)
- if (hideNavigationBar) {
+ if (ReadBookConfig.hideNavigationBar) {
flag = flag or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
}
if (toolBarHide) {
- if (App.INSTANCE.getPrefBoolean(PreferKey.hideStatusBar)) {
+ if (ReadBookConfig.hideStatusBar) {
flag = flag or View.SYSTEM_UI_FLAG_FULLSCREEN
}
- if (hideNavigationBar) {
+ if (ReadBookConfig.hideNavigationBar) {
flag = flag or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
}
}
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 248f2b5e5..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
@@ -7,6 +7,7 @@ import android.net.Uri
import android.os.Bundle
import android.os.Handler
import android.view.*
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import androidx.core.view.get
import androidx.core.view.isVisible
import androidx.core.view.size
@@ -24,7 +25,7 @@ import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.okButton
import io.legado.app.lib.theme.accentColor
-import io.legado.app.receiver.TimeElectricityReceiver
+import io.legado.app.receiver.TimeBatteryReceiver
import io.legado.app.service.BaseReadAloudService
import io.legado.app.service.help.ReadAloud
import io.legado.app.service.help.ReadBook
@@ -32,10 +33,10 @@ 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
import io.legado.app.ui.book.read.page.delegate.PageDelegate
-import io.legado.app.ui.book.read.page.entities.SelectPoint
import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.changesource.ChangeSourceDialog
import io.legado.app.ui.chapterlist.ChapterListActivity
@@ -55,27 +56,32 @@ import org.jetbrains.anko.toast
class ReadBookActivity : VMBaseActivity(R.layout.activity_book_read),
View.OnTouchListener,
PageView.CallBack,
+ TextActionMenu.CallBack,
+ ContentTextView.CallBack,
ReadMenu.CallBack,
ReadAloudDialog.CallBack,
ChangeSourceDialog.CallBack,
ReadBook.CallBack,
+ TocRegexDialog.CallBack,
ColorPickerDialogListener {
private val requestCodeChapterList = 568
private val requestCodeEditSource = 111
private val requestCodeReplace = 312
private var menu: Menu? = null
+ private var textActionMenu: TextActionMenu? = null
override val viewModel: ReadBookViewModel
get() = getViewModel(ReadBookViewModel::class.java)
- override val isInitFinish: Boolean
- get() = viewModel.isInitFinish
+ override val isInitFinish: Boolean get() = viewModel.isInitFinish
private val mHandler = Handler()
private val keepScreenRunnable: Runnable = Runnable { Help.keepScreenOn(window, false) }
private var screenTimeOut: Long = 0
- private var timeElectricityReceiver: TimeElectricityReceiver? = null
+ private var timeBatteryReceiver: TimeBatteryReceiver? = null
+ override val pageFactory: TextPageFactory get() = page_view.pageFactory
+ override val headerHeight: Int get() = page_view.curPage.headerHeight
override fun onActivityCreated(savedInstanceState: Bundle?) {
Help.upLayoutInDisplayCutoutMode(window)
@@ -98,15 +104,15 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
override fun onResume() {
super.onResume()
upSystemUiVisibility()
- timeElectricityReceiver = TimeElectricityReceiver.register(this)
+ timeBatteryReceiver = TimeBatteryReceiver.register(this)
page_view.upTime()
}
override fun onPause() {
super.onPause()
- timeElectricityReceiver?.let {
+ timeBatteryReceiver?.let {
unregisterReceiver(it)
- timeElectricityReceiver = null
+ timeBatteryReceiver = null
}
upSystemUiVisibility()
}
@@ -209,6 +215,10 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
R.id.menu_book_info -> ReadBook.book?.let {
startActivity(Pair("bookUrl", it.bookUrl))
}
+ R.id.menu_toc_regex -> TocRegexDialog.show(
+ supportFragmentManager,
+ ReadBook.book?.tocUrl
+ )
}
return super.onCompatOptionsItemSelected(item)
}
@@ -318,14 +328,98 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
when (event.action) {
MotionEvent.ACTION_MOVE, MotionEvent.ACTION_UP -> {
when (v.id) {
- R.id.cursor_left -> page_view.selectStartMove(event.x, event.y)
- R.id.cursor_right -> page_view.selectEndMove(event.x, event.y)
+ R.id.cursor_left -> page_view.curPage.selectStartMove(
+ event.rawX + cursor_left.width,
+ event.rawY - cursor_left.height
+ )
+ R.id.cursor_right -> page_view.curPage.selectEndMove(
+ event.rawX - cursor_right.width,
+ event.rawY - cursor_right.height
+ )
}
}
}
return true
}
+ /**
+ * 更新文字选择开始位置
+ */
+ override fun upSelectedStart(x: Float, y: Float) {
+ cursor_left.x = x - cursor_left.width
+ cursor_left.y = y
+ cursor_left.visible(true)
+ showTextActionMenu()
+ }
+
+ /**
+ * 更新文字选择结束位置
+ */
+ override fun upSelectedEnd(x: Float, y: Float) {
+ cursor_right.x = x
+ cursor_right.y = y
+ cursor_right.visible(true)
+ showTextActionMenu()
+ }
+
+ /**
+ * 取消文字选择
+ */
+ override fun onCancelSelect() {
+ cursor_left.invisible()
+ cursor_right.invisible()
+ textActionMenu?.dismiss()
+ }
+
+ /**
+ * 显示文本操作菜单
+ */
+ private fun showTextActionMenu() {
+ textActionMenu ?: let {
+ textActionMenu = TextActionMenu(this, this)
+ }
+ val x = cursor_left.x.toInt() + cursor_left.width
+ val y = if (cursor_left.y - statusBarHeight > ReadBookConfig.textSize.dp * 1.5 + 20.dp) {
+ (page_view.height - cursor_left.y + ReadBookConfig.textSize.dp * 1.5).toInt()
+ } else {
+ (page_view.height - cursor_left.y - cursor_left.height - 40.dp).toInt()
+ }
+ textActionMenu?.let { popup ->
+ if (!popup.isShowing) {
+ popup.showAtLocation(cursor_left, Gravity.BOTTOM or Gravity.START, x, y)
+ } else {
+ popup.update(x, y, WRAP_CONTENT, WRAP_CONTENT)
+ }
+ }
+ }
+
+ /**
+ * 当前选择的文本
+ */
+ override val selectedText: String get() = page_view.curPage.selectedText
+
+ /**
+ * 文本选择菜单操作
+ */
+ override fun onMenuItemSelected(itemId: Int): Boolean {
+ when (itemId) {
+ R.id.menu_replace -> {
+ ReplaceEditDialog.show(supportFragmentManager, pattern = selectedText)
+ return true
+ }
+ }
+ return false
+ }
+
+ /**
+ * 文本选择菜单操作完成
+ */
+ override fun onMenuActionFinally() {
+ textActionMenu?.dismiss()
+ page_view.curPage.cancelSelect()
+ page_view.pageDelegate?.isTextSelected = false
+ }
+
/**
* 音量键翻页
*/
@@ -357,12 +451,18 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
}
}
- override fun upContent(position: Int) {
+ /**
+ * 更新内容
+ */
+ override fun upContent(relativePosition: Int) {
launch {
- page_view.upContent(position)
+ page_view.upContent(relativePosition)
}
}
+ /**
+ * 更新视图
+ */
override fun upView() {
launch {
ReadBook.curTextChapter?.let {
@@ -380,12 +480,18 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
}
}
+ /**
+ * 更新进度条
+ */
override fun upPageProgress() {
launch {
seek_read_page.progress = ReadBook.durPageIndex
}
}
+ /**
+ * 显示菜单
+ */
override fun showMenuBar() {
read_menu.runMenuIn()
}
@@ -397,12 +503,6 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
viewModel.changeTo(book)
}
- override fun setPageIndex(pageIndex: Int) {
- ReadBook.durPageIndex = pageIndex
- ReadBook.saveRead()
- ReadBook.curPageChanged()
- }
-
override fun clickCenter() {
if (BaseReadAloudService.isRun) {
showReadAloudDialog()
@@ -411,19 +511,16 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
}
}
- override fun selectText(selectPoint: SelectPoint) {
- cursor_left.x = selectPoint.startX - cursor_left.width
- cursor_left.y = selectPoint.startY
- cursor_right.x = selectPoint.endX
- cursor_right.y = selectPoint.endY
- cursor_left.visible()
- cursor_right.visible()
- }
-
+ /**
+ * 显示朗读菜单
+ */
override fun showReadAloudDialog() {
ReadAloudDialog().show(supportFragmentManager, "readAloud")
}
+ /**
+ * 自动翻页
+ */
override fun autoPage() {
}
@@ -495,6 +592,13 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
*/
override fun onDialogDismissed(dialogId: Int) = Unit
+ override fun onTocRegexDialogResult(tocRegex: String) {
+ ReadBook.book?.let {
+ it.tocUrl = tocRegex
+ viewModel.loadChapterList(it)
+ }
+ }
+
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
@@ -528,6 +632,8 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
override fun onDestroy() {
super.onDestroy()
mHandler.removeCallbacks(keepScreenRunnable)
+ textActionMenu?.dismiss()
+ page_view.onDestroy()
}
override fun observeLiveBus() {
@@ -549,7 +655,6 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
upSystemUiVisibility()
page_view.upBg()
page_view.upStyle()
- ChapterProvider.upStyle(ReadBookConfig.durConfig)
if (it) {
ReadBook.loadContent()
} else {
@@ -586,7 +691,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
upScreenTimeOut()
}
observeEvent(PreferKey.textSelectAble) {
- page_view.upSelectAble(it)
+ page_view.curPage.upSelectAble(it)
}
}
@@ -605,7 +710,7 @@ class ReadBookActivity : VMBaseActivity(R.layout.activity_boo
Help.keepScreenOn(window, true)
return
}
- val t = screenTimeOut - getScreenOffTime()
+ val t = screenTimeOut - sysScreenOffTime
if (t > 0) {
mHandler.removeCallbacks(keepScreenRunnable)
Help.keepScreenOn(window, true)
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
index db7529cab..2ebab7875 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadBookViewModel.kt
@@ -99,6 +99,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
execute {
if (book.isLocalBook()) {
AnalyzeTxtFile.analyze(context, book).let {
+ App.db.bookChapterDao().delByBook(book.bookUrl)
App.db.bookChapterDao().insert(*it.toTypedArray())
App.db.bookDao().update(book)
ReadBook.chapterSize = it.size
diff --git a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt
index 12bbb6d8b..6dec43d12 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/ReadMenu.kt
@@ -10,8 +10,8 @@ import android.widget.SeekBar
import androidx.core.view.isVisible
import io.legado.app.App
import io.legado.app.R
-import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig
+import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.accentColor
import io.legado.app.lib.theme.buttonDisabledColor
import io.legado.app.service.help.ReadBook
@@ -199,9 +199,8 @@ class ReadMenu : FrameLayout {
vw_menu_bg.onClick { runMenuOut() }
vwNavigationBar.layoutParams = vwNavigationBar.layoutParams.apply {
height =
- if (context.getPrefBoolean(PreferKey.hideNavigationBar)
- && Help.isNavigationBarExist(activity)
- ) context.getNavigationBarHeight()
+ if (ReadBookConfig.hideNavigationBar && Help.isNavigationBarExist(activity))
+ context.navigationBarHeight
else 0
}
}
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
new file mode 100644
index 000000000..4dc9ba7da
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt
@@ -0,0 +1,173 @@
+package io.legado.app.ui.book.read
+
+import android.annotation.SuppressLint
+import android.app.SearchManager
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ResolveInfo
+import android.net.Uri
+import android.os.Build
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.ViewGroup
+import android.widget.PopupWindow
+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
+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")
+class TextActionMenu(private val context: Context, private val callBack: CallBack) :
+ PopupWindow(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT) {
+
+ init {
+ @SuppressLint("InflateParams")
+ contentView = LayoutInflater.from(context).inflate(R.layout.popup_action_menu, null)
+
+ isTouchable = true
+ isOutsideTouchable = false
+ isFocusable = false
+
+ initRecyclerView()
+ }
+
+ private fun initRecyclerView() = with(contentView) {
+ val adapter = Adapter(context)
+ recycler_view.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
+ recycler_view.adapter = adapter
+ val menu = MenuBuilder(context)
+ SupportMenuInflater(context).inflate(R.menu.content_select_action, menu)
+ adapter.setItems(menu.visibleItems)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ val popupMenu = PopupMenu(context, iv_menu_more)
+ onInitializeMenu(popupMenu.menu)
+ if (popupMenu.menu.size > 0) {
+ iv_menu_more.visible()
+ popupMenu.setOnMenuItemClickListener { item ->
+ item.intent?.let {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ it.putExtra(Intent.EXTRA_PROCESS_TEXT, callBack.selectedText)
+ context.startActivity(it)
+ }
+ }
+ this@TextActionMenu.dismiss()
+ true
+ }
+ }
+ iv_menu_more.onClick {
+ popupMenu.show()
+ }
+ }
+ }
+
+ inner class Adapter(context: Context) :
+ SimpleRecyclerAdapter(context, R.layout.item_text) {
+
+ override fun convert(
+ holder: ItemViewHolder,
+ item: MenuItemImpl,
+ payloads: MutableList
+ ) {
+ with(holder.itemView) {
+ text_view.text = item.title
+ }
+ }
+
+ override fun registerListener(holder: ItemViewHolder) {
+ holder.itemView.onClick {
+ getItem(holder.layoutPosition)?.let {
+ if (!callBack.onMenuItemSelected(it.itemId)) {
+ onMenuItemSelected(it)
+ }
+ }
+ }
+ }
+ }
+
+ 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()) {
+ Intent(Intent.ACTION_VIEW).apply {
+ data = Uri.parse(callBack.selectedText)
+ }
+ } else {
+ Intent(Intent.ACTION_WEB_SEARCH).apply {
+ putExtra(SearchManager.QUERY, callBack.selectedText)
+ }
+ }
+ context.startActivity(intent)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ context.toast(e.localizedMessage ?: "ERROR")
+ }
+ }
+ }
+ callBack.onMenuActionFinally()
+ }
+
+ @RequiresApi(Build.VERSION_CODES.M)
+ private fun createProcessTextIntent(): Intent {
+ return Intent()
+ .setAction(Intent.ACTION_PROCESS_TEXT)
+ .setType("text/plain")
+ }
+
+ @RequiresApi(Build.VERSION_CODES.M)
+ private fun getSupportedActivities(): List {
+ return context.packageManager
+ .queryIntentActivities(createProcessTextIntent(), 0)
+ }
+
+ @RequiresApi(Build.VERSION_CODES.M)
+ private fun createProcessTextIntentForResolveInfo(info: ResolveInfo): Intent {
+ return createProcessTextIntent()
+ .putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, false)
+ .setClassName(info.activityInfo.packageName, info.activityInfo.name)
+ }
+
+ /**
+ * Start with a menu Item order value that is high enough
+ * so that your "PROCESS_TEXT" menu items appear after the
+ * standard selection menu items like Cut, Copy, Paste.
+ */
+ @RequiresApi(Build.VERSION_CODES.M)
+ private fun onInitializeMenu(menu: Menu) {
+ // Start with a menu Item order value that is high enough
+ // so that your "PROCESS_TEXT" menu items appear after the
+ // standard selection menu items like Cut, Copy, Paste.
+ var menuItemOrder = 100
+ for (resolveInfo in getSupportedActivities()) {
+ menu.add(
+ Menu.NONE, Menu.NONE,
+ menuItemOrder++, resolveInfo.loadLabel(context.packageManager)
+ ).intent = createProcessTextIntentForResolveInfo(resolveInfo)
+ }
+ }
+
+ interface CallBack {
+ val selectedText: String
+
+ fun onMenuItemSelected(itemId: Int): Boolean
+
+ fun onMenuActionFinally()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt
index f4f1d9ff8..5a3d3ec8b 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt
@@ -155,7 +155,7 @@ class BgTextConfigDialog : DialogFragment() {
override fun registerListener(holder: ItemViewHolder) {
holder.itemView.apply {
this.onClick {
- getItem(holder.layoutPosition)?.let {
+ getItemByLayoutPosition(holder.layoutPosition)?.let {
ReadBookConfig.durConfig.setBg(1, it)
ReadBookConfig.upBg()
postEvent(EventBus.UP_CONFIG, false)
diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt
index b087a1508..9b975e7e8 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt
@@ -14,6 +14,7 @@ import androidx.preference.PreferenceFragmentCompat
import io.legado.app.R
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
+import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.ATH
import io.legado.app.ui.book.read.Help
import io.legado.app.utils.getPrefBoolean
@@ -92,8 +93,14 @@ class MoreConfigDialog : DialogFragment() {
key: String?
) {
when (key) {
- PreferKey.hideStatusBar -> postEvent(EventBus.UP_CONFIG, true)
- PreferKey.hideNavigationBar -> postEvent(EventBus.UP_CONFIG, true)
+ PreferKey.hideStatusBar -> {
+ ReadBookConfig.hideStatusBar = getPrefBoolean(PreferKey.hideStatusBar)
+ postEvent(EventBus.UP_CONFIG, true)
+ }
+ PreferKey.hideNavigationBar -> {
+ ReadBookConfig.hideNavigationBar = getPrefBoolean(PreferKey.hideNavigationBar)
+ postEvent(EventBus.UP_CONFIG, true)
+ }
PreferKey.keepLight -> postEvent(key, true)
PreferKey.textSelectAble -> postEvent(key, getPrefBoolean(key))
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt
index 1cb0715e3..1155e1d7d 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt
@@ -10,7 +10,9 @@ import io.legado.app.R
import io.legado.app.constant.EventBus
import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.Help
+import io.legado.app.utils.dp
import io.legado.app.utils.postEvent
+import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.dialog_read_padding.*
class PaddingConfigDialog : DialogFragment() {
@@ -49,7 +51,15 @@ class PaddingConfigDialog : DialogFragment() {
ReadBookConfig.save()
}
- private fun initData() = with(ReadBookConfig.durConfig) {
+ private fun initData() = with(ReadBookConfig) {
+ if (hideStatusBar) {
+ tv_header_padding.visible()
+ dsb_header_padding_top.visible()
+ dsb_header_padding_bottom.visible()
+ dsb_header_padding_left.visible()
+ dsb_header_padding_right.visible()
+ tv_body_padding.setPadding(0, 10.dp, 0, 10.dp)
+ }
//正文
dsb_padding_top.progress = paddingTop
dsb_padding_bottom.progress = paddingBottom
@@ -67,7 +77,7 @@ class PaddingConfigDialog : DialogFragment() {
dsb_footer_padding_right.progress = footerPaddingRight
}
- private fun initView() = with(ReadBookConfig.durConfig) {
+ private fun initView() = with(ReadBookConfig) {
//正文
dsb_padding_top.onChanged = {
paddingTop = it
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 ecfdb4525..a6482caed 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
@@ -11,7 +11,7 @@ import androidx.fragment.app.DialogFragment
import io.legado.app.R
import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
-import io.legado.app.help.ImageLoader
+import io.legado.app.help.BookHelp
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.dialogs.selector
import io.legado.app.lib.theme.accentColor
@@ -19,7 +19,10 @@ 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.*
+import io.legado.app.utils.getIndexById
+import io.legado.app.utils.getPrefString
+import io.legado.app.utils.postEvent
+import io.legado.app.utils.putPrefString
import kotlinx.android.synthetic.main.activity_book_read.*
import kotlinx.android.synthetic.main.dialog_read_book_style.*
import org.jetbrains.anko.sdk27.listeners.onCheckedChange
@@ -28,6 +31,8 @@ import org.jetbrains.anko.sdk27.listeners.onLongClick
class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
+ val callBack = activity as? ReadBookActivity
+
override fun onStart() {
super.onStart()
val dm = DisplayMetrics()
@@ -76,7 +81,8 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
}
private fun initData() {
- requireContext().getPrefInt(PreferKey.pageAnim).let {
+ cb_share_layout.isChecked = ReadBookConfig.shareLayout
+ ReadBookConfig.pageAnim.let {
if (it >= 0 && it < rg_page_anim.childCount) {
rg_page_anim.check(rg_page_anim[it].id)
}
@@ -90,8 +96,15 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
chinese_converter.onChanged {
postEvent(EventBus.UP_CONFIG, true)
}
+ tv_title_center.onClick {
+ ReadBookConfig.apply {
+ titleCenter = !titleCenter
+ tv_title_center.isSelected = titleCenter
+ }
+ postEvent(EventBus.UP_CONFIG, true)
+ }
tv_text_bold.onClick {
- ReadBookConfig.durConfig.apply {
+ ReadBookConfig.apply {
textBold = !textBold
tv_text_bold.isSelected = textBold
}
@@ -105,43 +118,41 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
title = getString(R.string.text_indent),
items = resources.getStringArray(R.array.indent).toList()
) { _, index ->
- putPrefInt(PreferKey.bodyIndent, index)
+ BookHelp.bodyIndentCount = index
postEvent(EventBus.UP_CONFIG, true)
}
}
tv_padding.onClick {
- val activity = activity
dismiss()
- if (activity is ReadBookActivity) {
- activity.showPaddingConfig()
- }
+ callBack?.showPaddingConfig()
}
dsb_text_size.onChanged = {
- ReadBookConfig.durConfig.textSize = it + 5
+ ReadBookConfig.textSize = it + 5
postEvent(EventBus.UP_CONFIG, true)
}
dsb_text_letter_spacing.onChanged = {
- ReadBookConfig.durConfig.letterSpacing = (it - 50) / 100f
+ ReadBookConfig.letterSpacing = (it - 50) / 100f
postEvent(EventBus.UP_CONFIG, true)
}
dsb_line_size.onChanged = {
- ReadBookConfig.durConfig.lineSpacingExtra = it
+ ReadBookConfig.lineSpacingExtra = it
postEvent(EventBus.UP_CONFIG, true)
}
dsb_paragraph_spacing.onChanged = {
- ReadBookConfig.durConfig.paragraphSpacing = it
+ ReadBookConfig.paragraphSpacing = it
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) {
- requireContext().putPrefInt(PreferKey.pageAnim, i)
- val activity = activity
- if (activity is ReadBookActivity) {
- activity.page_view.upPageAnim(i)
- }
- break
- }
+ rg_page_anim.getIndexById(checkedId).let {
+ ReadBookConfig.pageAnim = it
+ callBack?.page_view?.upPageAnim()
+ }
+ }
+ cb_share_layout.onCheckedChangeListener = { checkBox, isChecked ->
+ if (checkBox.isPressed) {
+ ReadBookConfig.shareLayout = isChecked
+ upStyle()
+ postEvent(EventBus.UP_CONFIG, true)
}
}
bg0.onClick { changeBg(0) }
@@ -169,15 +180,13 @@ 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
}
private fun upStyle() {
- ReadBookConfig.durConfig.let {
+ ReadBookConfig.let {
+ tv_title_center.isSelected = it.titleCenter
tv_text_bold.isSelected = it.textBold
dsb_text_size.progress = it.textSize - 5
dsb_text_letter_spacing.progress = (it.letterSpacing * 100).toInt() + 50
@@ -200,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/config/TocRegexDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt
new file mode 100644
index 000000000..f2046ac4b
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt
@@ -0,0 +1,259 @@
+package io.legado.app.ui.book.read.config
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Bundle
+import android.util.DisplayMetrics
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.widget.Toolbar
+import androidx.fragment.app.FragmentManager
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.Observer
+import androidx.recyclerview.widget.ItemTouchHelper
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import io.legado.app.App
+import io.legado.app.R
+import io.legado.app.base.BaseDialogFragment
+import io.legado.app.base.adapter.ItemViewHolder
+import io.legado.app.base.adapter.SimpleRecyclerAdapter
+import io.legado.app.constant.Theme
+import io.legado.app.data.entities.TxtTocRule
+import io.legado.app.help.ItemTouchCallback
+import io.legado.app.lib.dialogs.alert
+import io.legado.app.lib.dialogs.cancelButton
+import io.legado.app.lib.dialogs.customView
+import io.legado.app.lib.dialogs.okButton
+import io.legado.app.model.localBook.AnalyzeTxtFile
+import io.legado.app.ui.widget.recycler.VerticalDivider
+import io.legado.app.utils.applyTint
+import kotlinx.android.synthetic.main.dialog_toc_regex.*
+import kotlinx.android.synthetic.main.dialog_toc_regex_edit.view.*
+import kotlinx.android.synthetic.main.item_toc_regex.view.*
+import kotlinx.coroutines.Dispatchers.IO
+import kotlinx.coroutines.launch
+import org.jetbrains.anko.sdk27.listeners.onClick
+import java.util.*
+
+
+class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
+
+ private lateinit var adapter: TocRegexAdapter
+ private var tocRegexLiveData: LiveData>? = null
+ var selectedName: String? = null
+ private var durRegex: String? = null
+
+ override fun onStart() {
+ super.onStart()
+ val dm = DisplayMetrics()
+ activity?.windowManager?.defaultDisplay?.getMetrics(dm)
+ dialog?.window?.setLayout((dm.widthPixels * 0.9).toInt(), (dm.heightPixels * 0.8).toInt())
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return inflater.inflate(R.layout.dialog_toc_regex, container)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ durRegex = arguments?.getString("tocRegex")
+ tool_bar.setTitle(R.string.txt_toc_regex)
+ tool_bar.inflateMenu(R.menu.txt_toc_regex)
+ tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
+ tool_bar.setOnMenuItemClickListener(this)
+ initView()
+ initData()
+ }
+
+ private fun initView() {
+ adapter = TocRegexAdapter(requireContext())
+ recycler_view.layoutManager = LinearLayoutManager(requireContext())
+ recycler_view.addItemDecoration(VerticalDivider(requireContext()))
+ recycler_view.adapter = adapter
+ val itemTouchCallback = ItemTouchCallback()
+ itemTouchCallback.onItemTouchCallbackListener = adapter
+ itemTouchCallback.isCanDrag = true
+ ItemTouchHelper(itemTouchCallback).attachToRecyclerView(recycler_view)
+ tv_cancel.onClick {
+ dismiss()
+ }
+ tv_ok.onClick {
+ adapter.getItems().forEach { tocRule ->
+ if (selectedName == tocRule.name) {
+ val callBack = activity as? CallBack
+ callBack?.onTocRegexDialogResult(tocRule.rule)
+ dismiss()
+ return@onClick
+ }
+ }
+ }
+ }
+
+ private fun initData() {
+ tocRegexLiveData?.removeObservers(viewLifecycleOwner)
+ tocRegexLiveData = App.db.txtTocRule().observeAll()
+ tocRegexLiveData?.observe(viewLifecycleOwner, Observer { tocRules ->
+ initSelectedName(tocRules)
+ adapter.setItems(tocRules)
+ })
+ }
+
+ private fun initSelectedName(tocRules: List) {
+ if (selectedName == null && durRegex != null) {
+ tocRules.forEach {
+ if (durRegex == it.rule) {
+ selectedName = it.name
+ return@forEach
+ }
+ }
+ if (selectedName == null) {
+ selectedName = ""
+ }
+ }
+ }
+
+ override fun onMenuItemClick(item: MenuItem?): Boolean {
+ when (item?.itemId) {
+ R.id.menu_add -> editRule()
+ R.id.menu_default -> importDefault()
+ }
+ return false
+ }
+
+ private fun importDefault() {
+ launch(IO) {
+ AnalyzeTxtFile.getDefaultRules().let {
+ App.db.txtTocRule().insert(*it.toTypedArray())
+ }
+ }
+ }
+
+ @SuppressLint("InflateParams")
+ private fun editRule(rule: TxtTocRule? = null) {
+ val tocRule = rule?.copy() ?: TxtTocRule()
+ requireContext().alert(titleResource = R.string.txt_toc_regex) {
+ var rootView: View? = null
+ customView {
+ LayoutInflater.from(requireContext())
+ .inflate(R.layout.dialog_toc_regex_edit, null).apply {
+ rootView = this
+ tv_rule_name.setText(tocRule.name)
+ tv_rule_regex.setText(tocRule.rule)
+ }
+ }
+ okButton {
+ rootView?.apply {
+ tocRule.name = tv_rule_name.text.toString()
+ tocRule.rule = tv_rule_regex.text.toString()
+ saveRule(tocRule, rule)
+ }
+ }
+ cancelButton()
+ }.show().applyTint()
+ }
+
+ private fun saveRule(rule: TxtTocRule, oldRule: TxtTocRule? = null) {
+ launch(IO) {
+ if (rule.serialNumber < 0) {
+ rule.serialNumber = adapter.getItems().lastOrNull()?.serialNumber ?: 0 + 1
+ }
+ oldRule?.let {
+ App.db.txtTocRule().delete(oldRule)
+ }
+ App.db.txtTocRule().insert(rule)
+ }
+ }
+
+ inner class TocRegexAdapter(context: Context) :
+ SimpleRecyclerAdapter(context, R.layout.item_toc_regex),
+ ItemTouchCallback.OnItemTouchCallbackListener {
+
+ override fun convert(holder: ItemViewHolder, item: TxtTocRule, payloads: MutableList) {
+ holder.itemView.apply {
+ if (payloads.isEmpty()) {
+ rb_regex_name.text = item.name
+ rb_regex_name.isChecked = item.name == selectedName
+ swt_enabled.isChecked = item.enable
+ } else {
+ rb_regex_name.isChecked = item.name == selectedName
+ }
+ }
+ }
+
+ override fun registerListener(holder: ItemViewHolder) {
+ holder.itemView.apply {
+ rb_regex_name.setOnCheckedChangeListener { buttonView, isChecked ->
+ if (buttonView.isPressed && isChecked) {
+ selectedName = getItem(holder.layoutPosition)?.name
+ updateItems(0, itemCount - 1, true)
+ }
+ }
+ swt_enabled.setOnCheckedChangeListener { buttonView, isChecked ->
+ if (buttonView.isPressed) {
+ getItem(holder.layoutPosition)?.let {
+ it.enable = isChecked
+ launch(IO) {
+ App.db.txtTocRule().update(it)
+ }
+ }
+ }
+ }
+ iv_edit.onClick {
+ editRule(getItem(holder.layoutPosition))
+ }
+ iv_delete.onClick {
+ getItem(holder.layoutPosition)?.let { item ->
+ launch(IO) {
+ App.db.txtTocRule().delete(item)
+ }
+ }
+ }
+ }
+ }
+
+ private var isMoved = false
+
+ override fun onMove(srcPosition: Int, targetPosition: Int): Boolean {
+ Collections.swap(getItems(), srcPosition, targetPosition)
+ notifyItemMoved(srcPosition, targetPosition)
+ isMoved = true
+ return super.onMove(srcPosition, targetPosition)
+ }
+
+ override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
+ super.clearView(recyclerView, viewHolder)
+ if (isMoved) {
+ for ((index, item) in getItems().withIndex()) {
+ item.serialNumber = index + 1
+ }
+ launch(IO) {
+ App.db.txtTocRule().update(*getItems().toTypedArray())
+ }
+ }
+ isMoved = false
+ }
+ }
+
+
+ companion object {
+ fun show(fragmentManager: FragmentManager, tocRegex: String? = null) {
+ val dialog = TocRegexDialog()
+ val bundle = Bundle()
+ bundle.putString("tocRegex", tocRegex)
+ dialog.arguments = bundle
+ dialog.show(fragmentManager, "tocRegexDialog")
+ }
+ }
+
+ interface CallBack {
+ fun onTocRegexDialogResult(tocRegex: String) {}
+ }
+
+}
\ No newline at end of file
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 8d5cdc422..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,9 +8,12 @@ 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.*
+import io.legado.app.ui.book.read.page.entities.TextChapter
+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
import io.legado.app.utils.getPrefString
import io.legado.app.utils.removePref
@@ -20,10 +23,12 @@ import io.legado.app.utils.removePref
object ChapterProvider {
var viewWidth = 0
var viewHeight = 0
- private var visibleWidth = 0
- private var visibleHeight = 0
- private var paddingLeft = 0
- private var paddingTop = 0
+ var paddingLeft = 0
+ var paddingTop = 0
+ var visibleWidth = 0
+ var visibleHeight = 0
+ var visibleRight = 0
+ var visibleBottom = 0
private var lineSpacingExtra = 0f
private var paragraphSpacing = 0
var typeface: Typeface = Typeface.SANS_SERIF
@@ -32,50 +37,12 @@ object ChapterProvider {
private var bodyIndent = BookHelp.bodyIndent
init {
- upStyle(ReadBookConfig.durConfig)
- }
-
- fun upStyle(config: ReadBookConfig.Config) {
- typeface = try {
- val fontPath = App.INSTANCE.getPrefString(PreferKey.readBookFont)
- if (!TextUtils.isEmpty(fontPath)) {
- Typeface.createFromFile(fontPath)
- } else {
- Typeface.SANS_SERIF
- }
- } catch (e: Exception) {
- App.INSTANCE.removePref(PreferKey.readBookFont)
- Typeface.SANS_SERIF
- }
- //标题
- titlePaint.isAntiAlias = true
- titlePaint.color = config.textColor()
- titlePaint.letterSpacing = config.letterSpacing
- titlePaint.typeface = Typeface.create(typeface, Typeface.BOLD)
- //正文
- contentPaint.isAntiAlias = true
- contentPaint.color = config.textColor()
- contentPaint.letterSpacing = config.letterSpacing
- val bold = if (config.textBold) Typeface.BOLD else Typeface.NORMAL
- contentPaint.typeface = Typeface.create(typeface, bold)
- //间距
- lineSpacingExtra = config.lineSpacingExtra.dp.toFloat()
- paragraphSpacing = config.paragraphSpacing.dp
- titlePaint.textSize = (config.textSize + 2).dp.toFloat()
- contentPaint.textSize = config.textSize.dp.toFloat()
-
- bodyIndent = BookHelp.bodyIndent
-
- upSize(config)
- }
-
- fun upSize(config: ReadBookConfig.Config) {
- paddingLeft = config.paddingLeft.dp
- paddingTop = config.paddingTop.dp
- visibleWidth = viewWidth - paddingLeft - config.paddingRight.dp
- visibleHeight = viewHeight - paddingTop - config.paddingBottom.dp
+ upStyle()
}
+ /**
+ * 获取拆分完的章节数据
+ */
fun getTextChapter(
bookChapter: BookChapter,
content: String,
@@ -111,6 +78,8 @@ object ChapterProvider {
durY = joinBody(text, durY, textPages, pageLines, pageLengths, stringBuilder)
}
}
+ textPages.last().height = durY + 20.dp
+ textPages.last().text = stringBuilder.toString()
if (pageLines.size < textPages.size) {
pageLines.add(textPages.last().textLines.size)
}
@@ -152,61 +121,39 @@ object ChapterProvider {
Layout.Alignment.ALIGN_NORMAL, 1f, lineSpacingExtra, true
)
for (lineIndex in 0 until layout.lineCount) {
+ textPages.last().height = durY
durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex)
val textLine = TextLine(isTitle = true)
if (durY < visibleHeight) {
textPages.last().textLines.add(textLine)
} else {
- durY = layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex)
textPages.last().text = stringBuilder.toString()
stringBuilder.clear()
pageLines.add(textPages.last().textLines.size)
pageLengths.add(textPages.last().text.length)
+ //新页面
+ durY = layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex)
textPages.add(TextPage())
textPages.last().textLines.add(textLine)
}
- textLine.lineBottom = paddingTop + durY -
- (layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))
- textLine.lineTop = paddingTop + durY -
- (layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex))
+ textLine.lineTop = (paddingTop + durY -
+ (layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex))).toFloat()
+ textLine.lineBase = (paddingTop + durY -
+ (layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))).toFloat()
+ textLine.lineBottom = textLine.lineBase + titlePaint.fontMetrics.descent
val words =
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,
- leftBottomPosition = TextPoint(paddingLeft + x, textLine.lineBottom),
- rightTopPosition = TextPoint(paddingLeft + x1, textLine.lineTop)
- )
- textLine.textChars.add(textChar)
- x = x1
- }
+ addCharsToLineMiddle(textLine, words, titlePaint, desiredWidth, 0f)
} else {
//最后一行
- textLine.text = "$words\n"
- stringBuilder.append("\n")
- var x = 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,
- leftBottomPosition = TextPoint(paddingLeft + x, textLine.lineBottom),
- rightTopPosition = TextPoint(paddingLeft + x1, textLine.lineTop)
- )
- textLine.textChars.add(textChar)
- x = x1
- }
+ val x = if (ReadBookConfig.titleCenter)
+ (visibleWidth - layout.getLineWidth(lineIndex)) / 2
+ else 0f
+ addCharsToLineLast(textLine, words, stringBuilder, titlePaint, x)
}
}
durY += paragraphSpacing
@@ -230,92 +177,192 @@ object ChapterProvider {
Layout.Alignment.ALIGN_NORMAL, 1f, lineSpacingExtra, true
)
for (lineIndex in 0 until layout.lineCount) {
- val textLine = TextLine(isTitle = false)
+ textPages.last().height = durY
durY = durY + layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex)
+ val textLine = TextLine()
if (durY < visibleHeight) {
textPages.last().textLines.add(textLine)
} else {
- durY = layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex)
textPages.last().text = stringBuilder.toString()
stringBuilder.clear()
pageLines.add(textPages.last().textLines.size)
pageLengths.add(textPages.last().text.length)
+ //新页面
+ durY = layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex)
textPages.add(TextPage())
textPages.last().textLines.add(textLine)
}
- textLine.lineBottom = paddingTop + durY -
- (layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))
- textLine.lineTop = paddingTop + durY -
- (layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex))
- var words =
+ textLine.lineTop = (paddingTop + durY -
+ (layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex))).toFloat()
+ textLine.lineBase = (paddingTop + durY -
+ (layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex))).toFloat()
+ textLine.lineBottom = textLine.lineBase + contentPaint.fontMetrics.descent
+ 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)
- var x1 = x + icw
- val textChar = TextChar(
- charData = bodyIndent,
- leftBottomPosition = TextPoint(paddingLeft + x, textLine.lineBottom),
- rightTopPosition = TextPoint(paddingLeft + x1, textLine.lineTop)
- )
- 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)
- x1 = if (i != words.lastIndex) x + cw + d else x + cw
- val textChar1 = TextChar(
- charData = char,
- leftBottomPosition = TextPoint(paddingLeft + x, textLine.lineBottom),
- rightTopPosition = TextPoint(paddingLeft + x1, textLine.lineTop)
- )
- 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,
- leftBottomPosition = TextPoint(paddingLeft + x, textLine.lineBottom),
- rightTopPosition = TextPoint(paddingLeft + x1, textLine.lineTop)
- )
- 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,
- leftBottomPosition = TextPoint(paddingLeft + x, textLine.lineBottom),
- rightTopPosition = TextPoint(paddingLeft + x1, textLine.lineTop)
- )
- 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
+ }
+ }
+ }
+ }
+
+ /**
+ * 更新样式
+ */
+ fun upStyle() {
+ typeface = try {
+ val fontPath = App.INSTANCE.getPrefString(PreferKey.readBookFont)
+ if (!TextUtils.isEmpty(fontPath)) {
+ Typeface.createFromFile(fontPath)
+ } else {
+ when (AppConfig.systemTypefaces) {
+ 1 -> Typeface.SERIF
+ 2 -> Typeface.MONOSPACE
+ else -> Typeface.SANS_SERIF
+ }
+ }
+ } catch (e: Exception) {
+ App.INSTANCE.removePref(PreferKey.readBookFont)
+ Typeface.SANS_SERIF
+ }
+ //标题
+ titlePaint.isAntiAlias = true
+ titlePaint.color = ReadBookConfig.durConfig.textColor()
+ titlePaint.letterSpacing = ReadBookConfig.letterSpacing
+ titlePaint.typeface = Typeface.create(typeface, Typeface.BOLD)
+ //正文
+ contentPaint.isAntiAlias = true
+ contentPaint.color = ReadBookConfig.durConfig.textColor()
+ contentPaint.letterSpacing = ReadBookConfig.letterSpacing
+ val bold = if (ReadBookConfig.textBold) Typeface.BOLD else Typeface.NORMAL
+ contentPaint.typeface = Typeface.create(typeface, bold)
+ //间距
+ lineSpacingExtra = ReadBookConfig.lineSpacingExtra.dp.toFloat()
+ paragraphSpacing = ReadBookConfig.paragraphSpacing.dp
+ titlePaint.textSize = (ReadBookConfig.textSize + 2).dp.toFloat()
+ contentPaint.textSize = ReadBookConfig.textSize.dp.toFloat()
+
+ bodyIndent = BookHelp.bodyIndent
+
+ upSize()
+ }
+
+ /**
+ * 更新View尺寸
+ */
+ fun upSize() {
+ paddingLeft = ReadBookConfig.paddingLeft.dp
+ paddingTop = ReadBookConfig.paddingTop.dp
+ visibleWidth = viewWidth - paddingLeft - ReadBookConfig.paddingRight.dp
+ visibleHeight = viewHeight - paddingTop - ReadBookConfig.paddingBottom.dp
+ visibleRight = paddingLeft + visibleWidth
+ visibleBottom = paddingTop + visibleHeight
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/ContentSelectActionCallback.kt b/app/src/main/java/io/legado/app/ui/book/read/page/ContentSelectActionCallback.kt
deleted file mode 100644
index f274b2744..000000000
--- a/app/src/main/java/io/legado/app/ui/book/read/page/ContentSelectActionCallback.kt
+++ /dev/null
@@ -1,39 +0,0 @@
-package io.legado.app.ui.book.read.page
-
-import android.view.ActionMode
-import android.view.Menu
-import android.view.MenuItem
-import android.widget.TextView
-
-import io.legado.app.R
-import io.legado.app.constant.EventBus
-import io.legado.app.utils.postEvent
-
-class ContentSelectActionCallback(private val textView: TextView) : ActionMode.Callback {
-
- override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
- when (item?.itemId) {
- R.id.menu_replace -> {
- val text = textView.text.substring(textView.selectionStart, textView.selectionEnd)
- postEvent(EventBus.REPLACE, text)
- mode?.finish()
- return true
- }
- }
- return false
- }
-
- override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
- mode?.menuInflater?.inflate(R.menu.content_select_action, menu)
- return true
- }
-
- override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
- return false
- }
-
- override fun onDestroyActionMode(mode: ActionMode?) {
-
- }
-
-}
\ No newline at end of file
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 52176ac4d..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
@@ -1,124 +1,591 @@
package io.legado.app.ui.book.read.page
-import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
+import android.graphics.RectF
import android.util.AttributeSet
-import android.view.MotionEvent
import android.view.View
import io.legado.app.R
import io.legado.app.constant.PreferKey
import io.legado.app.help.ReadBookConfig
import io.legado.app.lib.theme.accentColor
-import io.legado.app.ui.book.read.page.entities.SelectPoint
+import io.legado.app.ui.book.read.page.entities.TextChar
import io.legado.app.ui.book.read.page.entities.TextPage
+import io.legado.app.utils.activity
import io.legado.app.utils.getCompatColor
import io.legado.app.utils.getPrefBoolean
class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
+ var selectAble = context.getPrefBoolean(PreferKey.textSelectAble)
+ var upView: ((TextPage) -> Unit)? = null
private val selectedPaint by lazy {
Paint().apply {
color = context.getCompatColor(R.color.btn_bg_press_2)
style = Paint.Style.FILL
}
}
- var selectAble = context.getPrefBoolean(PreferKey.textSelectAble)
- var selectStartLine = 0
- var selectStartChar = 0
- var selectEndLine = 0
- var selectEndChar = 0
- private var textPage: TextPage? = null
+ private var callBack: CallBack
+ private val visibleRect = RectF()
+ private val selectStart = arrayOf(0, 0, 0)
+ private val selectEnd = arrayOf(0, 0, 0)
+ private var textPage: TextPage = TextPage()
+ //滚动参数
+ private val pageFactory: TextPageFactory get() = callBack.pageFactory
+ private val maxScrollOffset = 100f
+ private var pageOffset = 0f
+
+ init {
+ callBack = activity as CallBack
+ contentDescription = textPage.text
+ }
- fun setContent(textPage: TextPage?) {
+ fun setContent(textPage: TextPage) {
this.textPage = textPage
+ contentDescription = textPage.text
invalidate()
}
+ 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)
- ReadBookConfig.durConfig.let {
- ChapterProvider.viewWidth = w
- ChapterProvider.viewHeight = h
- ChapterProvider.upSize(ReadBookConfig.durConfig)
+ ChapterProvider.viewWidth = w
+ ChapterProvider.viewHeight = h
+ ChapterProvider.upSize()
+ upVisibleRect()
+ textPage.format()
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+ canvas.clipRect(visibleRect)
+ drawPage(canvas)
+ }
+
+ /**
+ * 绘制页面
+ */
+ private fun drawPage(canvas: Canvas) {
+ var relativeOffset = relativeOffset(0)
+ textPage.textLines.forEach { textLine ->
+ val lineTop = textLine.lineTop + relativeOffset
+ val lineBase = textLine.lineBase + relativeOffset
+ val lineBottom = textLine.lineBottom + relativeOffset
+ drawChars(
+ canvas,
+ textLine.textChars,
+ lineTop,
+ lineBase,
+ lineBottom,
+ textLine.isTitle,
+ textLine.isReadAloud
+ )
+ }
+ if (!ReadBookConfig.isScroll) return
+ //滚动翻页
+ val nextPage = relativePage(1)
+ relativeOffset = relativeOffset(1)
+ nextPage.textLines.forEach { textLine ->
+ val lineTop = textLine.lineTop + relativeOffset
+ val lineBase = textLine.lineBase + relativeOffset
+ val lineBottom = textLine.lineBottom + relativeOffset
+ drawChars(
+ canvas,
+ textLine.textChars,
+ lineTop,
+ lineBase,
+ lineBottom,
+ textLine.isTitle,
+ textLine.isReadAloud
+ )
+ }
+ relativeOffset = relativeOffset(2)
+ if (relativeOffset < ChapterProvider.visibleHeight) {
+ relativePage(2).textLines.forEach { textLine ->
+ val lineTop = textLine.lineTop + relativeOffset
+ val lineBase = textLine.lineBase + relativeOffset
+ val lineBottom = textLine.lineBottom + relativeOffset
+ drawChars(
+ canvas,
+ textLine.textChars,
+ lineTop,
+ lineBase,
+ lineBottom,
+ textLine.isTitle,
+ textLine.isReadAloud
+ )
+ }
}
}
- @SuppressLint("ClickableViewAccessibility")
- override fun onTouchEvent(event: MotionEvent?): Boolean {
+ /**
+ * 绘制文字
+ */
+ private fun drawChars(
+ canvas: Canvas,
+ textChars: List,
+ lineTop: Float,
+ lineBase: Float,
+ lineBottom: Float,
+ isTitle: Boolean,
+ isReadAloud: Boolean
+ ) {
+ val textPaint = if (isTitle) ChapterProvider.titlePaint else ChapterProvider.contentPaint
+ textPaint.color =
+ if (isReadAloud) context.accentColor else ReadBookConfig.durConfig.textColor()
+ textChars.forEach {
+ canvas.drawText(it.charData, it.start, lineBase, textPaint)
+ if (it.selected) {
+ canvas.drawRect(it.start, lineTop, it.end, lineBottom, selectedPaint)
+ }
+ }
+ }
+
+ /**
+ * 滚动事件
+ */
+ fun onScroll(mOffset: Float) {
+ if (mOffset == 0f) return
+ var offset = mOffset
+ if (offset > maxScrollOffset) {
+ offset = maxScrollOffset
+ } else if (offset < -maxScrollOffset) {
+ offset = -maxScrollOffset
+ }
+
+ pageOffset += offset
+ if (pageOffset > 0) {
+ if (!pageFactory.hasPrev()) {
+ pageOffset = 0f
+ } else {
+ pageFactory.moveToPrev()
+ textPage = pageFactory.currentPage
+ pageOffset -= textPage.height
+ upView?.invoke(textPage)
+ }
+ } else if (pageOffset < -textPage.height) {
+ if (!pageFactory.hasNext()) {
+ pageOffset = -textPage.height.toFloat()
+ } else {
+ pageOffset += textPage.height
+ pageFactory.moveToNext()
+ textPage = pageFactory.currentPage
+ upView?.invoke(textPage)
+ }
+ }
+ invalidate()
+ }
- return true
+ fun resetPageOffset() {
+ pageOffset = 0f
}
- override fun onDraw(canvas: Canvas) {
- super.onDraw(canvas)
- textPage?.let { textPage ->
- textPage.textLines.forEach { textLine ->
- val textPaint = if (textLine.isTitle) {
- ChapterProvider.titlePaint
- } else {
- ChapterProvider.contentPaint
+ /**
+ * 选择初始文字
+ */
+ fun selectText(
+ x: Float,
+ 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()) {
+ if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
+ for ((charIndex, textChar) in textLine.textChars.withIndex()) {
+ if (x > textChar.start && x < textChar.end) {
+ textChar.selected = true
+ invalidate()
+ selectStart[0] = 0
+ selectStart[1] = lineIndex
+ selectStart[2] = charIndex
+ selectEnd[0] = 0
+ selectEnd[1] = lineIndex
+ selectEnd[2] = charIndex
+ upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
+ upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
+ select(0, lineIndex, charIndex)
+ return
+ }
}
- textPaint.color = if (textLine.isReadAloud) {
- context.accentColor
- } else {
- ReadBookConfig.durConfig.textColor()
+ return
+ }
+ }
+ if (!ReadBookConfig.isScroll) return
+ //滚动翻页
+ relativeOffset = relativeOffset(1)
+ if (relativeOffset >= ChapterProvider.visibleHeight) return
+ val nextPage = relativePage(1)
+ for ((lineIndex, textLine) in nextPage.textLines.withIndex()) {
+ if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
+ for ((charIndex, textChar) in textLine.textChars.withIndex()) {
+ if (x > textChar.start && x < textChar.end) {
+ textChar.selected = true
+ invalidate()
+ selectStart[0] = 1
+ selectStart[1] = lineIndex
+ selectStart[2] = charIndex
+ selectEnd[0] = 1
+ selectEnd[1] = lineIndex
+ selectEnd[2] = charIndex
+ upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
+ upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
+ select(1, lineIndex, charIndex)
+ return
+ }
}
- textLine.textChars.forEach {
- canvas.drawText(
- it.charData,
- it.leftBottomPosition.x,
- it.leftBottomPosition.y.toFloat(),
- textPaint
- )
- if (it.selected) {
- canvas.drawRect(
- it.leftBottomPosition.x,
- it.rightTopPosition.y.toFloat(),
- it.rightTopPosition.x,
- it.leftBottomPosition.y.toFloat(),
- selectedPaint
- )
+ return
+ }
+ }
+ relativeOffset = relativeOffset(2)
+ if (relativeOffset >= ChapterProvider.visibleHeight) return
+ for ((lineIndex, textLine) in relativePage(2).textLines.withIndex()) {
+ if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
+ for ((charIndex, textChar) in textLine.textChars.withIndex()) {
+ if (x > textChar.start && x < textChar.end) {
+ textChar.selected = true
+ invalidate()
+ selectStart[0] = 2
+ selectStart[1] = lineIndex
+ selectStart[2] = charIndex
+ selectEnd[0] = 2
+ selectEnd[1] = lineIndex
+ selectEnd[2] = charIndex
+ upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
+ upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
+ select(2, lineIndex, charIndex)
+ return
}
}
+ return
}
}
}
- fun selectText(x: Float, y: Float): SelectPoint? {
- textPage?.let { textPage ->
- for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
- if (y > textLine.lineTop && y < textLine.lineBottom) {
- for ((charIndex, textChar) in textLine.textChars.withIndex()) {
- if (x > textChar.leftBottomPosition.x && x < textChar.rightTopPosition.x) {
- textChar.selected = true
- invalidate()
- selectStartLine = lineIndex
- selectStartChar = charIndex
- selectEndLine = lineIndex
- selectEndChar = charIndex
- return SelectPoint(
- textChar.leftBottomPosition.x,
- textChar.leftBottomPosition.y.toFloat(),
- textChar.rightTopPosition.x,
- textChar.leftBottomPosition.y.toFloat()
- )
+ /**
+ * 开始选择符移动
+ */
+ fun selectStartMove(x: Float, y: Float) {
+ if (!visibleRect.contains(x, y)) return
+ var relativeOffset = relativeOffset(0)
+ for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
+ if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
+ for ((charIndex, textChar) in textLine.textChars.withIndex()) {
+ if (x > textChar.start && x < textChar.end) {
+ if (selectStart[0] != 0 || selectStart[1] != lineIndex || selectStart[2] != charIndex) {
+ if (selectToInt(0, lineIndex, charIndex) > selectToInt(selectEnd)) {
+ return
+ }
+ selectStart[0] = 0
+ selectStart[1] = lineIndex
+ selectStart[2] = charIndex
+ upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
+ upSelectChars()
+ }
+ return
+ }
+ }
+ return
+ }
+ }
+ if (!ReadBookConfig.isScroll) return
+ //滚动翻页
+ relativeOffset = relativeOffset(1)
+ if (relativeOffset >= ChapterProvider.visibleHeight) return
+ for ((lineIndex, textLine) in relativePage(1).textLines.withIndex()) {
+ if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
+ for ((charIndex, textChar) in textLine.textChars.withIndex()) {
+ if (x > textChar.start && x < textChar.end) {
+ if (selectStart[0] != 1 || selectStart[1] != lineIndex || selectStart[2] != charIndex) {
+ if (selectToInt(1, lineIndex, charIndex) > selectToInt(selectEnd)) {
+ return
+ }
+ selectStart[0] = 1
+ selectStart[1] = lineIndex
+ selectStart[2] = charIndex
+ upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
+ upSelectChars()
}
+ return
}
- break
}
+ return
+ }
+ }
+ relativeOffset = relativeOffset(2)
+ if (relativeOffset >= ChapterProvider.visibleHeight) return
+ for ((lineIndex, textLine) in relativePage(2).textLines.withIndex()) {
+ if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
+ for ((charIndex, textChar) in textLine.textChars.withIndex()) {
+ if (x > textChar.start && x < textChar.end) {
+ if (selectStart[0] != 2 || selectStart[1] != lineIndex || selectStart[2] != charIndex) {
+ if (selectToInt(2, lineIndex, charIndex) > selectToInt(selectEnd)) {
+ return
+ }
+ selectStart[0] = 2
+ selectStart[1] = lineIndex
+ selectStart[2] = charIndex
+ upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
+ upSelectChars()
+ }
+ return
+ }
+ }
+ return
}
}
- return null
}
- fun selectStartMove(x: Float, y: Float) {
+ /**
+ * 结束选择符移动
+ */
+ fun selectEndMove(x: Float, y: Float) {
+ if (!visibleRect.contains(x, y)) return
+ var relativeOffset = relativeOffset(0)
+ for ((lineIndex, textLine) in textPage.textLines.withIndex()) {
+ if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
+ for ((charIndex, textChar) in textLine.textChars.withIndex()) {
+ if (x > textChar.start && x < textChar.end) {
+ if (selectEnd[0] != 0 || selectEnd[1] != lineIndex || selectEnd[2] != charIndex) {
+ if (selectToInt(0, lineIndex, charIndex) < selectToInt(selectStart)) {
+ return
+ }
+ selectEnd[0] = 0
+ selectEnd[1] = lineIndex
+ selectEnd[2] = charIndex
+ upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
+ upSelectChars()
+ }
+ return
+ }
+ }
+ return
+ }
+ }
+ if (!ReadBookConfig.isScroll) return
+ //滚动翻页
+ relativeOffset = relativeOffset(1)
+ if (relativeOffset >= ChapterProvider.visibleHeight) return
+ for ((lineIndex, textLine) in relativePage(1).textLines.withIndex()) {
+ if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
+ for ((charIndex, textChar) in textLine.textChars.withIndex()) {
+ if (x > textChar.start && x < textChar.end) {
+ if (selectEnd[0] != 1 || selectEnd[1] != lineIndex || selectEnd[2] != charIndex) {
+ if (selectToInt(1, lineIndex, charIndex) < selectToInt(selectStart)) {
+ return
+ }
+ selectEnd[0] = 1
+ selectEnd[1] = lineIndex
+ selectEnd[2] = charIndex
+ upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
+ upSelectChars()
+ }
+ return
+ }
+ }
+ return
+ }
+ }
+ relativeOffset = relativeOffset(2)
+ if (relativeOffset >= ChapterProvider.visibleHeight) return
+ for ((lineIndex, textLine) in relativePage(2).textLines.withIndex()) {
+ if (y > textLine.lineTop + relativeOffset && y < textLine.lineBottom + relativeOffset) {
+ for ((charIndex, textChar) in textLine.textChars.withIndex()) {
+ if (x > textChar.start && x < textChar.end) {
+ if (selectEnd[0] != 2 || selectEnd[1] != lineIndex || selectEnd[2] != charIndex) {
+ if (selectToInt(2, lineIndex, charIndex) < selectToInt(selectStart)) {
+ return
+ }
+ selectEnd[0] = 2
+ selectEnd[1] = lineIndex
+ selectEnd[2] = charIndex
+ upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
+ upSelectChars()
+ }
+ return
+ }
+ }
+ return
+ }
+ }
+ }
+ /**
+ * 选择开始文字
+ */
+ fun selectStartMoveIndex(relativePage: Int, lineIndex: Int, charIndex: Int) {
+ selectStart[0] = relativePage
+ selectStart[1] = lineIndex
+ selectStart[2] = charIndex
+ val textLine = relativePage(relativePage).textLines[lineIndex]
+ val textChar = textLine.textChars[charIndex]
+ upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset(relativePage))
+ upSelectChars()
}
- fun selectEndMove(x: Float, y: Float) {
+ /**
+ * 选择结束文字
+ */
+ fun selectEndMoveIndex(relativePage: Int, lineIndex: Int, charIndex: Int) {
+ selectEnd[0] = relativePage
+ selectEnd[1] = lineIndex
+ selectEnd[2] = charIndex
+ val textLine = relativePage(relativePage).textLines[lineIndex]
+ val textChar = textLine.textChars[charIndex]
+ upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset(relativePage))
+ upSelectChars()
+ }
+
+ private fun upSelectChars() {
+ val last = if (ReadBookConfig.isScroll) 2 else 0
+ for (relativePos in 0..last) {
+ for ((lineIndex, textLine) in relativePage(relativePos).textLines.withIndex()) {
+ for ((charIndex, textChar) in textLine.textChars.withIndex()) {
+ textChar.selected =
+ if (relativePos == selectStart[0]
+ && relativePos == selectEnd[0]
+ && lineIndex == selectStart[1]
+ && lineIndex == selectEnd[1]
+ ) {
+ charIndex in selectStart[2]..selectEnd[2]
+ } else if (relativePos == selectStart[0] && lineIndex == selectStart[1]) {
+ charIndex >= selectStart[2]
+ } else if (relativePos == selectEnd[0] && lineIndex == selectEnd[1]) {
+ charIndex <= selectEnd[2]
+ } else if (relativePos == selectStart[0] && relativePos == selectEnd[0]) {
+ lineIndex in (selectStart[1] + 1) until selectEnd[1]
+ } else if (relativePos == selectStart[0]) {
+ lineIndex > selectStart[1]
+ } else if (relativePos == selectEnd[0]) {
+ lineIndex < selectEnd[1]
+ } else {
+ relativePos in selectStart[0] + 1 until selectEnd[0]
+ }
+ }
+ }
+ }
+ invalidate()
+ }
+
+ private fun upSelectedStart(x: Float, y: Float) {
+ callBack.upSelectedStart(x, y + callBack.headerHeight)
+ }
+ private fun upSelectedEnd(x: Float, y: Float) {
+ callBack.upSelectedEnd(x, y + callBack.headerHeight)
+ }
+
+ fun cancelSelect() {
+ val last = if (ReadBookConfig.isScroll) 2 else 0
+ for (relativePos in 0..last) {
+ relativePage(relativePos).textLines.forEach { textLine ->
+ textLine.textChars.forEach {
+ it.selected = false
+ }
+ }
+ }
+ invalidate()
+ callBack.onCancelSelect()
}
+ val selectedText: String
+ get() {
+ val stringBuilder = StringBuilder()
+ for (relativePos in selectStart[0]..selectEnd[0]) {
+ val textPage = relativePage(relativePos)
+ if (relativePos == selectStart[0] && relativePos == selectEnd[0]) {
+ for (lineIndex in selectStart[1]..selectEnd[1]) {
+ if (lineIndex == selectStart[1] && lineIndex == selectEnd[1]) {
+ stringBuilder.append(
+ textPage.textLines[lineIndex].text.substring(
+ selectStart[2],
+ selectEnd[2] + 1
+ )
+ )
+ } else if (lineIndex == selectStart[1]) {
+ stringBuilder.append(
+ textPage.textLines[lineIndex].text.substring(
+ selectStart[2]
+ )
+ )
+ } else if (lineIndex == selectEnd[1]) {
+ stringBuilder.append(
+ textPage.textLines[lineIndex].text.substring(0, selectEnd[2] + 1)
+ )
+ } else {
+ stringBuilder.append(textPage.textLines[lineIndex].text)
+ }
+ }
+ } else if (relativePos == selectStart[0]) {
+ for (lineIndex in selectStart[1] until relativePage(relativePos).textLines.size) {
+ if (lineIndex == selectStart[1]) {
+ stringBuilder.append(
+ textPage.textLines[lineIndex].text.substring(
+ selectStart[2]
+ )
+ )
+ } else {
+ stringBuilder.append(textPage.textLines[lineIndex].text)
+ }
+ }
+ } else if (relativePos == selectEnd[0]) {
+ for (lineIndex in 0..selectEnd[1]) {
+ if (lineIndex == selectEnd[1]) {
+ stringBuilder.append(
+ textPage.textLines[lineIndex].text.substring(0, selectEnd[2] + 1)
+ )
+ } else {
+ stringBuilder.append(textPage.textLines[lineIndex].text)
+ }
+ }
+ } else if (relativePos in selectStart[0] + 1 until selectEnd[0]) {
+ for (lineIndex in selectStart[1]..selectEnd[1]) {
+ stringBuilder.append(textPage.textLines[lineIndex].text)
+ }
+ }
+ }
+ return stringBuilder.toString()
+ }
+
+ private fun selectToInt(page: Int, line: Int, char: Int): Int {
+ return page * 1000000 + line * 100000 + char
+ }
+
+ private fun selectToInt(select: Array): Int {
+ return select[0] * 1000000 + select[1] * 100000 + select[2]
+ }
+
+ private fun relativeOffset(relativePos: Int): Float {
+ return when (relativePos) {
+ 0 -> pageOffset
+ 1 -> pageOffset + textPage.height
+ else -> pageOffset + textPage.height + pageFactory.nextPage.height
+ }
+ }
+
+ private fun relativePage(relativePos: Int): TextPage {
+ return when (relativePos) {
+ 0 -> textPage
+ 1 -> pageFactory.nextPage
+ else -> pageFactory.nextPagePlus
+ }
+ }
+
+ interface CallBack {
+ fun upSelectedStart(x: Float, y: Float)
+ fun upSelectedEnd(x: Float, y: Float)
+ fun onCancelSelect()
+ val headerHeight: Int
+ val pageFactory: TextPageFactory
+ }
}
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 4f6a0dad4..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
@@ -3,78 +3,65 @@ package io.legado.app.ui.book.read.page
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.Drawable
-import android.util.AttributeSet
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.constant.PreferKey
+import io.legado.app.help.AppConfig
import io.legado.app.help.ReadBookConfig
-import io.legado.app.ui.book.read.page.entities.SelectPoint
import io.legado.app.ui.book.read.page.entities.TextPage
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.view_book_page.view.*
import java.util.*
-class ContentView : FrameLayout {
- var callBack: CallBack? = null
- private var headerHeight = 0
- private var pageSize: Int = 0
+class ContentView(context: Context) : FrameLayout(context) {
- constructor(context: Context) : super(context) {
- init()
- }
-
- constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
- init()
- }
-
- fun init() {
+ init {
//设置背景防止切换背景时文字重叠
setBackgroundColor(context.getCompatColor(R.color.background))
inflate(context, R.layout.view_book_page, this)
+
upStyle()
upTime()
+ content_text_view.upView = {
+ tv_bottom_left.text = it.title
+ setPageIndex(it.index, it.pageSize)
+ }
}
fun upStyle() {
- ReadBookConfig.durConfig.apply {
+ ReadBookConfig.apply {
tv_top_left.typeface = ChapterProvider.typeface
tv_top_right.typeface = ChapterProvider.typeface
tv_bottom_left.typeface = ChapterProvider.typeface
tv_bottom_right.typeface = ChapterProvider.typeface
- if (context.getPrefBoolean(PreferKey.hideStatusBar, false)) {
- //显示状态栏时隐藏header
- ll_header.visible()
- ll_header.layoutParams =
- ll_header.layoutParams.apply { height = context.getStatusBarHeight() }
+ //显示状态栏时隐藏header
+ if (hideStatusBar) {
+ ll_header.layoutParams = ll_header.layoutParams.apply {
+ height = context.statusBarHeight + headerPaddingTop.dp + headerPaddingBottom.dp
+ }
ll_header.setPadding(
headerPaddingLeft.dp,
headerPaddingTop.dp,
headerPaddingRight.dp,
headerPaddingBottom.dp
)
- headerHeight = ll_header.height
+ ll_header.visible()
page_panel.setPadding(0, 0, 0, 0)
} else {
ll_header.gone()
- headerHeight = context.getStatusBarHeight()
- page_panel.setPadding(0, headerHeight, 0, 0)
+ 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
)
- textColor().let {
+ content_text_view.upVisibleRect()
+ durConfig.textColor().let {
tv_top_left.setTextColor(it)
tv_top_right.setTextColor(it)
tv_bottom_left.setTextColor(it)
@@ -83,6 +70,15 @@ class ContentView : FrameLayout {
}
}
+ val headerHeight: Int
+ get() {
+ return if (ReadBookConfig.hideStatusBar) {
+ ll_header.height
+ } else {
+ context.statusBarHeight
+ }
+ }
+
fun setBg(bg: Drawable?) {
page_panel.background = bg
}
@@ -95,60 +91,64 @@ class ContentView : FrameLayout {
tv_top_right.text = context.getString(R.string.battery_show, battery)
}
- fun setContent(textPage: TextPage?) {
- content_text_view.setContent(textPage)
- if (textPage != null) {
- tv_bottom_left.text = textPage.title
- pageSize = textPage.pageSize
- setPageIndex(textPage.index)
+ fun setContent(textPage: TextPage) {
+ 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)
+ }
+
+ fun resetPageOffset() {
+ content_text_view.resetPageOffset()
}
@SuppressLint("SetTextI18n")
- fun setPageIndex(pageIndex: Int?) {
+ fun setPageIndex(pageIndex: Int?, pageSize: Int) {
pageIndex?.let {
tv_bottom_right.text = "${pageIndex.plus(1)}/${pageSize}"
}
}
+ fun onScroll(offset: Float) {
+ content_text_view.onScroll(offset)
+ }
+
fun upSelectAble(selectAble: Boolean) {
content_text_view.selectAble = selectAble
}
- fun selectText(e: MotionEvent): SelectPoint? {
+ fun selectText(
+ e: MotionEvent,
+ select: (relativePage: Int, lineIndex: Int, charIndex: Int) -> Unit
+ ) {
val y = e.y - headerHeight
- val selectPoint = content_text_view.selectText(e.x, y)
- selectPoint?.let {
- it.startY = it.startY + headerHeight
- it.endY = it.endY + headerHeight
- }
- return selectPoint
+ return content_text_view.selectText(e.x, y, select)
}
fun selectStartMove(x: Float, y: Float) {
- content_text_view.selectStartMove(x, y)
+ content_text_view.selectStartMove(x, y - headerHeight)
}
- fun selectEndMove(x: Float, y: Float) {
- content_text_view.selectEndMove(x, y)
+ fun selectStartMoveIndex(relativePage: Int, lineIndex: Int, charIndex: Int) {
+ content_text_view.selectStartMoveIndex(relativePage, lineIndex, charIndex)
}
- fun scrollTo(pos: Int?) {
- if (pos != null) {
- content_text_view.post {
-
- }
- }
+ fun selectEndMove(x: Float, y: Float) {
+ content_text_view.selectEndMove(x, y - headerHeight)
}
- fun scrollToBottom() {
- content_text_view.post {
-
- }
+ fun selectEndMoveIndex(relativePage: Int, lineIndex: Int, charIndex: Int) {
+ content_text_view.selectEndMoveIndex(relativePage, lineIndex, charIndex)
}
- interface CallBack {
- fun scrollToLine(line: Int)
- fun scrollToLast()
+ fun cancelSelect() {
+ content_text_view.cancelSelect()
}
+
+ val selectedText: String get() = content_text_view.selectedText
+
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/DataSource.kt b/app/src/main/java/io/legado/app/ui/book/read/page/DataSource.kt
index ed223a4cf..1d188f598 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/DataSource.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/DataSource.kt
@@ -1,24 +1,19 @@
package io.legado.app.ui.book.read.page
+import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.entities.TextChapter
interface DataSource {
- val isScrollDelegate: Boolean
- val pageIndex: Int
+ val pageIndex: Int get() = ReadBook.durChapterPos()
- fun setPageIndex(pageIndex: Int)
+ val currentChapter: TextChapter?
- fun getChapterPosition(): Int
+ val nextChapter: TextChapter?
- fun getCurrentChapter(): TextChapter?
-
- fun getNextChapter(): TextChapter?
-
- fun getPreviousChapter(): TextChapter?
+ val prevChapter: TextChapter?
fun hasNextChapter(): Boolean
fun hasPrevChapter(): Boolean
-
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt b/app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt
index 6f39f2992..85cdecbd5 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/PageFactory.kt
@@ -8,13 +8,15 @@ abstract class PageFactory(protected val dataSource: DataSource) {
abstract fun moveToNext():Boolean
- abstract fun moveToPrevious(): Boolean
+ abstract fun moveToPrev(): Boolean
- abstract fun nextPage(): DATA?
+ abstract val nextPage: DATA
- abstract fun previousPage(): DATA?
+ abstract val prevPage: DATA
- abstract fun currentPage(): DATA?
+ abstract val currentPage: DATA
+
+ abstract val nextPagePlus: DATA
abstract fun hasNext(): Boolean
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 13a3322d2..1482d309a 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
@@ -6,30 +6,26 @@ import android.graphics.Canvas
import android.util.AttributeSet
import android.view.MotionEvent
import android.widget.FrameLayout
-import io.legado.app.constant.PreferKey
import io.legado.app.help.ReadBookConfig
import io.legado.app.service.help.ReadBook
import io.legado.app.ui.book.read.page.delegate.*
-import io.legado.app.ui.book.read.page.entities.SelectPoint
import io.legado.app.ui.book.read.page.entities.TextChapter
import io.legado.app.utils.activity
-import io.legado.app.utils.getPrefInt
class PageView(context: Context, attrs: AttributeSet) :
FrameLayout(context, attrs),
- ContentView.CallBack,
DataSource {
- var callBack: CallBack? = null
- var pageFactory: TextPageFactory? = null
- private var pageDelegate: PageDelegate? = null
+ var callBack: CallBack
+ var pageFactory: TextPageFactory
+ var pageDelegate: PageDelegate? = null
var prevPage: ContentView
var curPage: ContentView
var nextPage: ContentView
init {
- callBack = activity as? CallBack
+ callBack = activity as CallBack
prevPage = ContentView(context)
addView(prevPage)
nextPage = ContentView(context)
@@ -39,8 +35,7 @@ class PageView(context: Context, attrs: AttributeSet) :
upBg()
setWillNotDraw(false)
pageFactory = TextPageFactory(this)
- upPageAnim(context.getPrefInt(PreferKey.pageAnim))
- curPage.callBack = this
+ upPageAnim()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
@@ -67,33 +62,34 @@ class PageView(context: Context, attrs: AttributeSet) :
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
- callBack?.screenOffTimerStart()
- return pageDelegate?.onTouch(event) ?: super.onTouchEvent(event)
+ pageDelegate?.onTouch(event)
+ callBack.screenOffTimerStart()
+ return true
+ }
+
+ fun onDestroy() {
+ pageDelegate?.onDestroy()
+ curPage.cancelSelect()
}
fun fillPage(direction: PageDelegate.Direction) {
when (direction) {
PageDelegate.Direction.PREV -> {
- pageFactory?.moveToPrevious()
+ pageFactory.moveToPrev()
upContent()
- if (isScrollDelegate) {
- curPage.scrollToBottom()
- }
}
PageDelegate.Direction.NEXT -> {
- pageFactory?.moveToNext()
+ pageFactory.moveToNext()
upContent()
- if (isScrollDelegate) {
- curPage.scrollTo(0)
- }
}
else -> Unit
}
}
- fun upPageAnim(pageAnim: Int) {
+ fun upPageAnim() {
+ pageDelegate?.onDestroy()
pageDelegate = null
- pageDelegate = when (pageAnim) {
+ pageDelegate = when (ReadBookConfig.pageAnim) {
0 -> CoverPageDelegate(this)
1 -> SlidePageDelegate(this)
2 -> SimulationPageDelegate(this)
@@ -103,36 +99,26 @@ class PageView(context: Context, attrs: AttributeSet) :
upContent()
}
- fun upContent(position: Int = 0) {
- pageFactory?.let {
- when (position) {
- -1 -> prevPage.setContent(it.previousPage())
- 1 -> nextPage.setContent(it.nextPage())
+ fun upContent(relativePosition: Int = 0) {
+ if (ReadBookConfig.isScroll) {
+ curPage.setContent(pageFactory.currentPage)
+ } else {
+ when (relativePosition) {
+ -1 -> prevPage.setContent(pageFactory.prevPage)
+ 1 -> nextPage.setContent(pageFactory.nextPage)
else -> {
- curPage.setContent(it.currentPage())
- nextPage.setContent(it.nextPage())
- prevPage.setContent(it.previousPage())
- if (isScrollDelegate) {
- curPage.scrollTo(ReadBook.textChapter()?.getStartLine(ReadBook.durChapterPos()))
- }
+ curPage.setContent(pageFactory.currentPage)
+ nextPage.setContent(pageFactory.nextPage)
+ prevPage.setContent(pageFactory.prevPage)
}
}
- if (isScrollDelegate) {
- prevPage.scrollToBottom()
- }
}
- callBack?.screenOffTimerStart()
+ callBack.screenOffTimerStart()
}
fun moveToPrevPage(noAnim: Boolean = true) {
if (noAnim) {
- if (isScrollDelegate) {
- ReadBook.textChapter()?.let {
- curPage.scrollTo(it.getStartLine(pageIndex - 1))
- }
- } else {
- fillPage(PageDelegate.Direction.PREV)
- }
+ fillPage(PageDelegate.Direction.PREV)
} else {
pageDelegate?.start(PageDelegate.Direction.PREV)
}
@@ -140,24 +126,14 @@ class PageView(context: Context, attrs: AttributeSet) :
fun moveToNextPage(noAnim: Boolean = true) {
if (noAnim) {
- if (isScrollDelegate) {
- ReadBook.textChapter()?.let {
- curPage.scrollTo(it.getStartLine(pageIndex + 1))
- }
- } else {
- fillPage(PageDelegate.Direction.NEXT)
- }
+ fillPage(PageDelegate.Direction.NEXT)
} else {
pageDelegate?.start(PageDelegate.Direction.NEXT)
}
}
- fun upSelectAble(selectAble: Boolean) {
- pageDelegate?.upSelectAble(selectAble)
- curPage.upSelectAble(selectAble)
- }
-
fun upStyle() {
+ ChapterProvider.upStyle()
curPage.upStyle()
prevPage.upStyle()
nextPage.upStyle()
@@ -184,38 +160,19 @@ class PageView(context: Context, attrs: AttributeSet) :
nextPage.upBattery(battery)
}
- fun selectStartMove(x: Float, y: Float) {
- curPage.selectStartMove(x, y)
- }
-
- fun selectEndMove(x: Float, y: Float) {
- curPage.selectEndMove(x, y)
- }
-
- override val isScrollDelegate: Boolean
- get() = pageDelegate is ScrollPageDelegate
-
- override val pageIndex: Int
- get() = ReadBook.durChapterPos()
-
- override fun setPageIndex(pageIndex: Int) {
- callBack?.setPageIndex(pageIndex)
+ override val currentChapter: TextChapter?
+ get() {
+ return if (callBack.isInitFinish) ReadBook.textChapter(0) else null
}
- override fun getChapterPosition(): Int {
- return ReadBook.durChapterIndex
+ override val nextChapter: TextChapter?
+ get() {
+ return if (callBack.isInitFinish) ReadBook.textChapter(1) else null
}
- override fun getCurrentChapter(): TextChapter? {
- return if (callBack?.isInitFinish == true) ReadBook.textChapter(0) else null
- }
-
- override fun getNextChapter(): TextChapter? {
- return if (callBack?.isInitFinish == true) ReadBook.textChapter(1) else null
- }
-
- override fun getPreviousChapter(): TextChapter? {
- return if (callBack?.isInitFinish == true) ReadBook.textChapter(-1) else null
+ override val prevChapter: TextChapter?
+ get() {
+ return if (callBack.isInitFinish) ReadBook.textChapter(-1) else null
}
override fun hasNextChapter(): Boolean {
@@ -223,47 +180,12 @@ class PageView(context: Context, attrs: AttributeSet) :
}
override fun hasPrevChapter(): Boolean {
- callBack?.let {
- return ReadBook.durChapterIndex > 0
- }
- return false
- }
-
- override fun scrollToLine(line: Int) {
- if (isScrollDelegate) {
- ReadBook.textChapter()?.let {
- val pageIndex = it.getPageIndex(line)
- curPage.setPageIndex(pageIndex)
- callBack?.setPageIndex(pageIndex)
- }
- }
- }
-
- override fun scrollToLast() {
- if (isScrollDelegate) {
- ReadBook.textChapter()?.let {
- callBack?.setPageIndex(it.lastIndex())
- curPage.setPageIndex(it.lastIndex())
- }
- }
+ return ReadBook.durChapterIndex > 0
}
interface CallBack {
-
val isInitFinish: Boolean
-
- /**
- * 保存页数
- */
- fun setPageIndex(pageIndex: Int)
-
- /**
- * 点击屏幕中间
- */
fun clickCenter()
-
fun screenOffTimerStart()
-
- fun selectText(selectPoint: SelectPoint)
}
}
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt b/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt
index 03a222b94..fe18f1b26 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/TextPageFactory.kt
@@ -6,91 +6,107 @@ import io.legado.app.ui.book.read.page.entities.TextPage
class TextPageFactory(dataSource: DataSource) : PageFactory(dataSource) {
override fun hasPrev(): Boolean = with(dataSource) {
- return if (isScrollDelegate) {
- hasPrevChapter()
- } else {
- hasPrevChapter() || pageIndex > 0
- }
+ return hasPrevChapter() || pageIndex > 0
}
override fun hasNext(): Boolean = with(dataSource) {
- return if (isScrollDelegate) {
- hasNextChapter()
- } else {
- hasNextChapter()
- || getCurrentChapter()?.isLastIndex(pageIndex) != true
- }
+ return hasNextChapter() || currentChapter?.isLastIndex(pageIndex) != true
}
override fun moveToFirst() {
- dataSource.setPageIndex(0)
+ ReadBook.setPageIndex(0)
}
override fun moveToLast() = with(dataSource) {
- getCurrentChapter()?.let {
+ currentChapter?.let {
if (it.pageSize() == 0) {
- setPageIndex(0)
+ ReadBook.setPageIndex(0)
} else {
- setPageIndex(it.pageSize().minus(1))
+ ReadBook.setPageIndex(it.pageSize().minus(1))
}
- } ?: setPageIndex(0)
+ } ?: ReadBook.setPageIndex(0)
}
override fun moveToNext(): Boolean = with(dataSource) {
return if (hasNext()) {
- if (getCurrentChapter()?.isLastIndex(pageIndex) == true
- || isScrollDelegate
- ) {
+ if (currentChapter?.isLastIndex(pageIndex) == true) {
ReadBook.moveToNextChapter(false)
} else {
- setPageIndex(pageIndex.plus(1))
+ ReadBook.setPageIndex(pageIndex.plus(1))
}
true
} else
false
}
- override fun moveToPrevious(): Boolean = with(dataSource) {
+ override fun moveToPrev(): Boolean = with(dataSource) {
return if (hasPrev()) {
- if (pageIndex <= 0 || isScrollDelegate) {
+ if (pageIndex <= 0) {
ReadBook.moveToPrevChapter(false)
} else {
- setPageIndex(pageIndex.minus(1))
+ ReadBook.setPageIndex(pageIndex.minus(1))
}
true
} else
false
}
- override fun currentPage(): TextPage? = with(dataSource) {
- return if (isScrollDelegate) {
- getCurrentChapter()?.scrollPage()
- } else {
- getCurrentChapter()?.page(pageIndex)
+ override val currentPage: TextPage
+ get() = with(dataSource) {
+ currentChapter?.let {
+ return@with it.page(pageIndex)
+ ?: TextPage(title = it.title).format()
+ }
+ return TextPage().format()
}
- }
- override fun nextPage(): TextPage? = with(dataSource) {
- if (isScrollDelegate) {
- return getNextChapter()?.scrollPage()
- }
- getCurrentChapter()?.let {
- if (pageIndex < it.pageSize() - 1) {
- return getCurrentChapter()?.page(pageIndex + 1)?.removePageAloudSpan()
+ override val nextPage: TextPage
+ get() = with(dataSource) {
+ currentChapter?.let {
+ if (pageIndex < it.pageSize() - 1) {
+ return@with it.page(pageIndex + 1)?.removePageAloudSpan()
+ ?: TextPage(title = it.title).format()
+ }
+ }
+ nextChapter?.let {
+ return@with it.page(0)?.removePageAloudSpan()
+ ?: TextPage(title = it.title).format()
}
+ return TextPage().format()
}
- return getNextChapter()?.page(0)?.removePageAloudSpan()
- }
- override fun previousPage(): TextPage? = with(dataSource) {
- if (isScrollDelegate) {
- return getPreviousChapter()?.scrollPage()
- }
- if (pageIndex > 0) {
- return getCurrentChapter()?.page(pageIndex - 1)?.removePageAloudSpan()
+ override val prevPage: TextPage
+ get() = with(dataSource) {
+ if (pageIndex > 0) {
+ currentChapter?.let {
+ return@with it.page(pageIndex - 1)?.removePageAloudSpan()
+ ?: TextPage(title = it.title).format()
+ }
+ }
+ prevChapter?.let {
+ return@with it.lastPage()?.removePageAloudSpan()
+ ?: TextPage(title = it.title).format()
+ }
+ return TextPage().format()
}
- return getPreviousChapter()?.lastPage()?.removePageAloudSpan()
- }
+ override val nextPagePlus: TextPage
+ get() = with(dataSource) {
+ currentChapter?.let {
+ if (pageIndex < it.pageSize() - 2) {
+ return@with it.page(pageIndex + 2)?.removePageAloudSpan()
+ ?: TextPage(title = it.title).format()
+ }
+ nextChapter?.let { nc ->
+ if (pageIndex < it.pageSize() - 1) {
+ return@with nc.page(0)?.removePageAloudSpan()
+ ?: TextPage(title = nc.title).format()
+ }
+ return@with nc.page(1)?.removePageAloudSpan()
+ ?: TextPage(title = nc.title).format()
+ }
+ }
+ return TextPage().format()
+ }
}
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 f129d41ff..586b3d88e 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
@@ -6,19 +6,30 @@ import kotlin.math.abs
abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageView) {
- override fun onScroll(
- e1: MotionEvent,
- e2: MotionEvent,
- distanceX: Float,
- distanceY: Float
- ): Boolean {
+ override fun onTouch(event: MotionEvent) {
+ when (event.action) {
+ MotionEvent.ACTION_MOVE -> {
+ if (isTextSelected) {
+ selectText(event)
+ } else {
+ onScroll(event)
+ }
+ }
+ }
+ super.onTouch(event)
+ }
+
+ private fun onScroll(event: MotionEvent) {
+ //判断是否移动了
if (!isMoved) {
- if (abs(distanceX) > abs(distanceY)) {
- if (distanceX < 0) {
+ isMoved = abs(startX - event.x) > slop
+ || abs(startX - event.x) > abs(startY - event.y)
+ if (isMoved) {
+ if (event.x - startX > 0) {
//如果上一页不存在
if (!hasPrev()) {
noNext = true
- return true
+ return
}
setDirection(Direction.PREV)
setBitmap()
@@ -26,21 +37,19 @@ abstract class HorizontalPageDelegate(pageView: PageView) : PageDelegate(pageVie
//如果不存在表示没有下一页了
if (!hasNext()) {
noNext = true
- return true
+ return
}
setDirection(Direction.NEXT)
setBitmap()
}
- isMoved = true
}
}
if (isMoved) {
- isCancel = if (mDirection == Direction.NEXT) distanceX < 0 else distanceX > 0
+ isCancel = if (mDirection == Direction.NEXT) touchX > lastX else touchX < lastX
isRunning = true
//设置触摸点
- setTouchPoint(e2.x, e2.y)
+ setTouchPoint(event.x, event.y)
}
- return isMoved
}
}
\ 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 dc3b9dddd..2b26714c4 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
@@ -6,15 +6,14 @@ import android.graphics.Canvas
import android.graphics.RectF
import android.view.GestureDetector
import android.view.MotionEvent
+import android.view.ViewConfiguration
+import android.view.animation.DecelerateInterpolator
import android.widget.Scroller
import androidx.annotation.CallSuper
-import androidx.interpolator.view.animation.FastOutLinearInInterpolator
import com.google.android.material.snackbar.Snackbar
-import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig
import io.legado.app.ui.book.read.page.ContentView
import io.legado.app.ui.book.read.page.PageView
-import io.legado.app.utils.getPrefBoolean
import io.legado.app.utils.screenshot
import kotlin.math.abs
@@ -25,42 +24,36 @@ abstract class PageDelegate(protected val pageView: PageView) :
pageView.width * 0.66f, pageView.height * 0.66f
)
protected val context: Context = pageView.context
+ protected val slop = ViewConfiguration.get(context).scaledTouchSlop
//起始点
- protected var startX: Float = 0.toFloat()
- protected var startY: Float = 0.toFloat()
+ protected var startX: Float = 0f
+ protected var startY: Float = 0f
+ //上一个触碰点
+ protected var lastX: Float = 0f
+ protected var lastY: Float = 0f
//触碰点
- protected var touchX: Float = 0.toFloat()
- protected var touchY: Float = 0.toFloat()
+ protected var touchX: Float = 0f
+ protected var touchY: Float = 0f
- protected val nextPage: ContentView
- get() = pageView.nextPage
-
- protected val curPage: ContentView
- get() = pageView.curPage
-
- protected val prevPage: ContentView
- get() = pageView.prevPage
+ protected val nextPage: ContentView get() = pageView.nextPage
+ protected val curPage: ContentView get() = pageView.curPage
+ protected val prevPage: ContentView get() = pageView.prevPage
protected var bitmap: Bitmap? = null
protected var viewWidth: Int = pageView.width
protected var viewHeight: Int = pageView.height
- //textView在顶端或低端
- protected var atTop: Boolean = false
- protected var atBottom: Boolean = false
private val snackBar: Snackbar by lazy {
Snackbar.make(pageView, "", Snackbar.LENGTH_SHORT)
}
private val scroller: Scroller by lazy {
- Scroller(pageView.context, FastOutLinearInInterpolator())
+ Scroller(pageView.context, DecelerateInterpolator())
}
private val detector: GestureDetector by lazy {
- GestureDetector(pageView.context, this).apply {
- setIsLongpressEnabled(context.getPrefBoolean(PreferKey.textSelectAble))
- }
+ GestureDetector(pageView.context, this)
}
var isMoved = false
@@ -71,32 +64,50 @@ abstract class PageDelegate(protected val pageView: PageView) :
var isCancel = false
var isRunning = false
var isStarted = false
+ var isTextSelected = false
+ var selectedOnDown = false
+
+ var firstRelativePage = 0
+ var firstLineIndex: Int = 0
+ var firstCharIndex: Int = 0
+
+ init {
+ curPage.resetPageOffset()
+ }
open fun setStartPoint(x: Float, y: Float, invalidate: Boolean = true) {
startX = x
startY = y
+ lastX = x
+ lastY = y
+ touchX = x
+ touchY = y
if (invalidate) {
- invalidate()
+ pageView.invalidate()
}
}
open fun setTouchPoint(x: Float, y: Float, invalidate: Boolean = true) {
+ lastX = touchX
+ lastY = touchY
touchX = x
touchY = y
if (invalidate) {
- invalidate()
+ pageView.invalidate()
}
onScroll()
}
- fun upSelectAble(selectAble: Boolean) {
- detector.setIsLongpressEnabled(selectAble)
- }
-
- protected fun invalidate() {
+ open fun fling(
+ startX: Int, startY: Int, velocityX: Int, velocityY: Int,
+ minX: Int, maxX: Int, minY: Int, maxY: Int
+ ) {
+ scroller.fling(startX, startY, velocityX, velocityY, minX, maxX, minY, maxY)
+ isRunning = true
+ isStarted = true
pageView.invalidate()
}
@@ -110,28 +121,21 @@ abstract class PageDelegate(protected val pageView: PageView) :
)
isRunning = true
isStarted = true
- invalidate()
+ pageView.invalidate()
}
- protected fun stopScroll() {
+ private fun stopScroll() {
isRunning = false
isStarted = false
- invalidate()
- if (pageView.isScrollDelegate) {
- pageView.postDelayed({
- bitmap?.recycle()
- bitmap = null
- }, 100)
- } else {
- bitmap?.recycle()
- bitmap = null
- }
+ pageView.invalidate()
+ bitmap?.recycle()
+ bitmap = null
}
- fun setViewSize(width: Int, height: Int) {
+ open fun setViewSize(width: Int, height: Int) {
viewWidth = width
viewHeight = height
- invalidate()
+ pageView.invalidate()
centerRectF.set(
width * 0.33f, height * 0.33f,
width * 0.66f, height * 0.66f
@@ -142,7 +146,6 @@ abstract class PageDelegate(protected val pageView: PageView) :
if (scroller.computeScrollOffset()) {
setTouchPoint(scroller.currX.toFloat(), scroller.currY.toFloat())
} else if (isStarted) {
- setTouchPoint(scroller.finalX.toFloat(), scroller.finalY.toFloat(), false)
onAnimStop()
stopScroll()
}
@@ -182,14 +185,13 @@ abstract class PageDelegate(protected val pageView: PageView) :
onAnimStart()
}
- abstract fun onAnimStart()//scroller start
+ open fun onAnimStart() {}//scroller start
- abstract fun onDraw(canvas: Canvas)//绘制
+ open fun onDraw(canvas: Canvas) {}//绘制
- abstract fun onAnimStop()//scroller finish
+ open fun onAnimStop() {}//scroller finish
- open fun onScroll() {//移动contentView, slidePage
- }
+ open fun onScroll() {}//移动contentView, slidePage
@CallSuper
open fun setDirection(direction: Direction) {
@@ -208,17 +210,28 @@ abstract class PageDelegate(protected val pageView: PageView) :
* 触摸事件处理
*/
@CallSuper
- open fun onTouch(event: MotionEvent): Boolean {
- if (isStarted) return false
- curPage.dispatchTouchEvent(event)
- return detector.onTouchEvent(event)
+ open fun onTouch(event: MotionEvent) {
+ if (isStarted) return
+ if (!detector.onTouchEvent(event)) {
+ //GestureDetector.onFling小幅移动不会触发,所以要自己判断
+ if (event.action == MotionEvent.ACTION_UP && isMoved) {
+ if (selectedOnDown) {
+ selectedOnDown = false
+ }
+ if (!noNext) onAnimStart()
+ }
+ }
}
/**
* 按下
*/
override fun onDown(e: MotionEvent): Boolean {
-// abort()
+ if (isTextSelected) {
+ curPage.cancelSelect()
+ isTextSelected = false
+ selectedOnDown = true
+ }
//是否移动
isMoved = false
//是否存在下一章
@@ -238,25 +251,25 @@ abstract class PageDelegate(protected val pageView: PageView) :
* 单击
*/
override fun onSingleTapUp(e: MotionEvent): Boolean {
+ if (selectedOnDown) {
+ selectedOnDown = false
+ return true
+ }
val x = e.x
val y = e.y
if (centerRectF.contains(x, y)) {
- pageView.callBack?.clickCenter()
+ pageView.callBack.clickCenter()
setTouchPoint(x, y)
} else {
if (x > viewWidth / 2 ||
AppConfig.clickAllNext
) {
//设置动画方向
- if (!hasNext()) {
- return true
- }
+ if (!hasNext()) return true
setDirection(Direction.NEXT)
setBitmap()
} else {
- if (!hasPrev()) {
- return true
- }
+ if (!hasPrev()) return true
setDirection(Direction.PREV)
setBitmap()
}
@@ -270,30 +283,50 @@ abstract class PageDelegate(protected val pageView: PageView) :
* 长按选择
*/
override fun onLongPress(e: MotionEvent) {
- val textChar = curPage.selectText(e)
- textChar?.let {
- pageView.callBack?.selectText(it)
+ curPage.selectText(e) { relativePage, lineIndex, charIndex ->
+ isTextSelected = true
+ firstRelativePage = relativePage
+ firstLineIndex = lineIndex
+ firstCharIndex = charIndex
}
}
- /**
- * 移动结束
- */
- override fun onFling(
- e1: MotionEvent?,
- e2: MotionEvent?,
- velocityX: Float,
- velocityY: Float
- ): Boolean {
- if (!noNext) onAnimStart()
- return true
+ protected fun selectText(event: MotionEvent) {
+ curPage.selectText(event) { relativePage, lineIndex, charIndex ->
+ when {
+ relativePage > firstRelativePage -> {
+ curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
+ curPage.selectEndMoveIndex(relativePage, lineIndex, charIndex)
+ }
+ relativePage < firstRelativePage -> {
+ curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
+ curPage.selectStartMoveIndex(relativePage, lineIndex, charIndex)
+ }
+ lineIndex > firstLineIndex -> {
+ curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
+ curPage.selectEndMoveIndex(relativePage, lineIndex, charIndex)
+ }
+ lineIndex < firstLineIndex -> {
+ curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
+ curPage.selectStartMoveIndex(relativePage, lineIndex, charIndex)
+ }
+ charIndex > firstCharIndex -> {
+ curPage.selectStartMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
+ curPage.selectEndMoveIndex(relativePage, lineIndex, charIndex)
+ }
+ else -> {
+ curPage.selectEndMoveIndex(firstRelativePage, firstLineIndex, firstCharIndex)
+ curPage.selectStartMoveIndex(relativePage, lineIndex, charIndex)
+ }
+ }
+ }
}
/**
* 判断是否有上一页
*/
fun hasPrev(): Boolean {
- val hasPrev = pageView.pageFactory?.hasPrev() == true
+ val hasPrev = pageView.pageFactory.hasPrev()
if (!hasPrev) {
if (!snackBar.isShown) {
snackBar.setText("没有上一页")
@@ -307,7 +340,7 @@ abstract class PageDelegate(protected val pageView: PageView) :
* 判断是否有下一页
*/
fun hasNext(): Boolean {
- val hasNext = pageView.pageFactory?.hasNext() == true
+ val hasNext = pageView.pageFactory.hasNext()
if (!hasNext) {
if (!snackBar.isShown) {
snackBar.setText("没有下一页")
@@ -317,24 +350,12 @@ abstract class PageDelegate(protected val pageView: PageView) :
return hasNext
}
+ open fun onDestroy() {
+ bitmap?.recycle()
+ }
+
enum class Direction {
NONE, PREV, NEXT
}
- fun MotionEvent.toAction(action: Int): MotionEvent {
- return MotionEvent.obtain(
- downTime,
- eventTime,
- action,
- x,
- y,
- pressure,
- size,
- metaState,
- xPrecision,
- yPrecision,
- deviceId,
- edgeFlags
- )
- }
}
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 9c9aade48..d90ee8649 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
@@ -1,119 +1,61 @@
package io.legado.app.ui.book.read.page.delegate
-import android.graphics.Canvas
-import android.graphics.Matrix
import android.view.MotionEvent
+import android.view.VelocityTracker
import io.legado.app.ui.book.read.page.PageView
import kotlin.math.abs
class ScrollPageDelegate(pageView: PageView) : PageDelegate(pageView) {
- private val bitmapMatrix = Matrix()
+ // 滑动追踪的时间
+ private val velocityDuration = 1000
+ //速度追踪器
+ private val mVelocity: VelocityTracker = VelocityTracker.obtain()
override fun onAnimStart() {
- if (!atTop && !atBottom) {
- stopScroll()
- return
- }
- val distanceY: Float
- when (mDirection) {
- Direction.NEXT -> distanceY =
- if (isCancel) {
- var dis = viewHeight - startY + touchY
- if (dis > viewHeight) {
- dis = viewHeight.toFloat()
- }
- viewHeight - dis
- } else {
- -(touchY + (viewHeight - startY))
- }
- else -> distanceY =
- if (isCancel) {
- -(touchY - startY)
- } else {
- viewHeight - (touchY - startY)
- }
- }
-
- startScroll(0, touchY.toInt(), 0, distanceY.toInt())
+ //惯性滚动
+ fling(
+ 0, touchY.toInt(), 0, mVelocity.yVelocity.toInt(),
+ 0, 0, -10 * viewHeight, 10 * viewHeight
+ )
}
- override fun onDraw(canvas: Canvas) {
- if (atTop || atBottom) {
- val offsetY = touchY - startY
-
- if ((mDirection == Direction.NEXT && offsetY > 0)
- || (mDirection == Direction.PREV && offsetY < 0)
- ) return
+ override fun onScroll() {
+ curPage.onScroll(touchY - lastY)
+ }
- val distanceY = if (offsetY > 0) offsetY - viewHeight else offsetY + viewHeight
- if (atTop && mDirection == Direction.PREV) {
- bitmap?.let {
- bitmapMatrix.setTranslate(0.toFloat(), distanceY)
- canvas.drawBitmap(it, bitmapMatrix, null)
- }
- } else if (atBottom && mDirection == Direction.NEXT) {
- bitmap?.let {
- bitmapMatrix.setTranslate(0.toFloat(), distanceY)
- canvas.drawBitmap(it, bitmapMatrix, null)
+ override fun onTouch(event: MotionEvent) {
+ when (event.action) {
+ MotionEvent.ACTION_DOWN -> {
+ setStartPoint(event.x, event.y)
+ abort()
+ mVelocity.clear()
+ }
+ MotionEvent.ACTION_MOVE -> {
+ if (isTextSelected) {
+ selectText(event)
+ } else {
+ onScroll(event)
}
}
}
+ super.onTouch(event)
}
- override fun onAnimStop() {
- if (!isCancel) {
- pageView.fillPage(mDirection)
- }
- }
-
- override fun onScroll(
- e1: MotionEvent,
- e2: MotionEvent,
- distanceX: Float,
- distanceY: Float
- ): Boolean {
- if (!isMoved && abs(distanceX) < abs(distanceY)) {
- if (distanceY < 0) {
- if (atTop) {
- val event = e1.toAction(MotionEvent.ACTION_UP)
- curPage.dispatchTouchEvent(event)
- event.recycle()
- //如果上一页不存在
- if (!hasPrev()) {
- noNext = true
- return true
- }
- setDirection(Direction.PREV)
- setBitmap()
- }
- } else {
- if (atBottom) {
- val event = e1.toAction(MotionEvent.ACTION_UP)
- curPage.dispatchTouchEvent(event)
- event.recycle()
- //如果不存在表示没有下一页了
- if (!hasNext()) {
- noNext = true
- return true
- }
- setDirection(Direction.NEXT)
- setBitmap()
- }
- }
- isMoved = true
- }
- if ((atTop && mDirection != Direction.PREV) || (atBottom && mDirection != Direction.NEXT) || mDirection == Direction.NONE) {
- //传递触摸事件到textView
- curPage.dispatchTouchEvent(e2)
+ private fun onScroll(event: MotionEvent) {
+ mVelocity.addMovement(event)
+ mVelocity.computeCurrentVelocity(velocityDuration)
+ setTouchPoint(event.x, event.y)
+ if (!isMoved) {
+ isMoved = abs(startX - event.x) > slop || abs(startY - event.y) > slop
}
if (isMoved) {
- isCancel = if (mDirection == Direction.NEXT) distanceY < 0 else distanceY > 0
isRunning = true
- //设置触摸点
- setTouchPoint(e2.x, e2.y)
}
- return isMoved
}
+ override fun onDestroy() {
+ super.onDestroy()
+ mVelocity.recycle()
+ }
}
\ 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 789fb61e1..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)
@@ -115,13 +113,18 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
mFrontShadowDrawableHBT.gradientType = GradientDrawable.LINEAR_GRADIENT
}
+ override fun setViewSize(width: Int, height: Int) {
+ super.setViewSize(width, height)
+ mMaxLength = hypot(viewWidth.toDouble(), viewHeight.toDouble()).toFloat()
+ }
+
override fun setStartPoint(x: Float, y: Float, invalidate: Boolean) {
super.setStartPoint(x, y, invalidate)
calcCornerXY(x, y)
}
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
@@ -134,6 +137,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
) {
touchY = 1f
}
+ pageView.invalidate()
}
override fun setDirection(direction: Direction) {
@@ -171,7 +175,6 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
override fun onAnimStart() {
var dx: Float
val dy: Float
- // dx 水平方向滑动的距离,负值会使滚动向左滚动
// dy 垂直方向滑动的距离,负值会使滚动向上滚动
if (isCancel) {
dx = if (mCornerX > 0 && mDirection == Direction.NEXT) {
@@ -215,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
}
}
@@ -254,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 {
@@ -333,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(),
@@ -368,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(
@@ -396,6 +403,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
canvas.restore()
}
+ //
private fun drawNextPageAreaAndShadow(
canvas: Canvas,
bitmap: Bitmap?
@@ -443,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()
@@ -457,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()
@@ -480,8 +489,10 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
private fun calcPoints() {
mTouchX = touchX
mTouchY = touchY
+
mMiddleX = (mTouchX + mCornerX) / 2
mMiddleY = (mTouchY + mCornerY) / 2
+
mBezierControl1.x =
mMiddleX - (mCornerY - mMiddleY) * (mCornerY - mMiddleY) / (mCornerX - mMiddleX)
mBezierControl1.y = mCornerY.toFloat()
@@ -492,6 +503,7 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
} else {
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / (mCornerY - mMiddleY)
}
+
mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2
mBezierStart1.y = mCornerY.toFloat()
//固定左边上下两个点
@@ -500,24 +512,27 @@ class SimulationPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageVi
if (mBezierStart1.x < 0)
mBezierStart1.x = viewWidth - mBezierStart1.x
- val f1: Float = abs(mCornerX - mTouchX)
- val f2: Float = viewWidth * f1 / mBezierStart1.x
+ val f1 = abs(mCornerX - mTouchX)
+ val f2 = viewWidth * f1 / mBezierStart1.x
mTouchX = abs(mCornerX - f2)
- val f3: Float = abs(mCornerX - mTouchX) * abs(mCornerY - mTouchY) / f1
+ val f3 = abs(mCornerX - mTouchX) * abs(mCornerY - mTouchY) / f1
mTouchY = abs(mCornerY - f3)
mMiddleX = (mTouchX + mCornerX) / 2
mMiddleY = (mTouchY + mCornerY) / 2
+
mBezierControl1.x =
mMiddleX - (mCornerY - mMiddleY) * (mCornerY - mMiddleY) / (mCornerX - mMiddleX)
mBezierControl1.y = mCornerY.toFloat()
+
mBezierControl2.x = mCornerX.toFloat()
mBezierControl2.y = if ((mCornerY - mMiddleY).toInt() == 0) {
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / 0.1f
} else {
mMiddleY - (mCornerX - mMiddleX) * (mCornerX - mMiddleX) / (mCornerY - mMiddleY)
}
- mBezierStart1.x = (mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2)
+
+ mBezierStart1.x = mBezierControl1.x - (mCornerX - mBezierControl1.x) / 2
}
}
mBezierStart2.x = mCornerX.toFloat()
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/SelectPoint.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/SelectPoint.kt
deleted file mode 100644
index 0be88dae6..000000000
--- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/SelectPoint.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.legado.app.ui.book.read.page.entities
-
-data class SelectPoint(
- var startX: Float,
- var startY: Float,
- var endX: Float,
- var endY: Float
-)
\ No newline at end of file
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 9883e6dab..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 leftBottomPosition: TextPoint,
- val rightTopPosition: TextPoint
+ 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 c5282aba3..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
@@ -3,8 +3,26 @@ package io.legado.app.ui.book.read.page.entities
data class TextLine(
var text: String = "",
val textChars: ArrayList = arrayListOf(),
- var lineTop: Int = 0,
- var lineBottom: Int = 0,
+ var lineTop: Float = 0f,
+ var lineBase: Float = 0f,
+ 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/read/page/entities/TextPage.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
index 653f352e5..4e6089ddd 100644
--- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
+++ b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
@@ -1,7 +1,10 @@
package io.legado.app.ui.book.read.page.entities
+import android.text.Layout
+import android.text.StaticLayout
import io.legado.app.App
import io.legado.app.R
+import io.legado.app.ui.book.read.page.ChapterProvider
data class TextPage(
var index: Int = 0,
@@ -10,9 +13,44 @@ data class TextPage(
val textLines: ArrayList = arrayListOf(),
var pageSize: Int = 0,
var chapterSize: Int = 0,
- var chapterIndex: Int = 0
+ var chapterIndex: Int = 0,
+ var height: Int = 0
) {
+ @Suppress("DEPRECATION")
+ fun format(): TextPage {
+ if (textLines.isEmpty() && ChapterProvider.visibleWidth > 0) {
+ val layout = StaticLayout(
+ text, ChapterProvider.contentPaint, ChapterProvider.visibleWidth,
+ Layout.Alignment.ALIGN_NORMAL, 1f, 0f, false
+ )
+ var y = (ChapterProvider.visibleHeight - layout.height) / 2f
+ if (y < 0) y = 0f
+ for (lineIndex in 0 until layout.lineCount) {
+ val textLine = TextLine()
+ textLine.lineTop = (ChapterProvider.paddingTop + y -
+ (layout.getLineBottom(lineIndex) - layout.getLineTop(lineIndex)))
+ textLine.lineBase = (ChapterProvider.paddingTop + y -
+ (layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex)))
+ textLine.lineBottom =
+ textLine.lineBase + ChapterProvider.contentPaint.fontMetrics.descent
+ var x = (ChapterProvider.visibleWidth - layout.getLineMax(lineIndex)) / 2
+ textLine.text =
+ text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
+ for (i in textLine.text.indices) {
+ val char = textLine.text[i].toString()
+ val cw = StaticLayout.getDesiredWidth(char, ChapterProvider.contentPaint)
+ val x1 = x + cw
+ textLine.textChars.add(TextChar(charData = char, start = x, end = x1))
+ x = x1
+ }
+ textLines.add(textLine)
+ }
+ height = ChapterProvider.visibleHeight
+ }
+ return this
+ }
+
fun removePageAloudSpan(): TextPage {
textLines.forEach { textLine ->
textLine.isReadAloud = false
diff --git a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPoint.kt b/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPoint.kt
deleted file mode 100644
index 44b81c2a9..000000000
--- a/app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPoint.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package io.legado.app.ui.book.read.page.entities
-
-data class TextPoint(
- val x: Float,
- val y: Int
-)
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/ui/book/search/BookAdapter.kt b/app/src/main/java/io/legado/app/ui/book/search/BookAdapter.kt
index 070837520..05ea3c782 100644
--- a/app/src/main/java/io/legado/app/ui/book/search/BookAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/book/search/BookAdapter.kt
@@ -5,11 +5,11 @@ import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.data.entities.Book
-import kotlinx.android.synthetic.main.item_text.view.*
+import kotlinx.android.synthetic.main.item_fillet_text.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
class BookAdapter(context: Context, val callBack: CallBack) :
- SimpleRecyclerAdapter(context, R.layout.item_text) {
+ SimpleRecyclerAdapter(context, R.layout.item_fillet_text) {
override fun convert(holder: ItemViewHolder, item: Book, payloads: MutableList) {
with(holder.itemView) {
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/HistoryKeyAdapter.kt b/app/src/main/java/io/legado/app/ui/book/search/HistoryKeyAdapter.kt
index cfe7699a8..e59605495 100644
--- a/app/src/main/java/io/legado/app/ui/book/search/HistoryKeyAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/book/search/HistoryKeyAdapter.kt
@@ -6,7 +6,7 @@ import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.data.entities.SearchKeyword
import io.legado.app.ui.widget.anima.explosion_field.ExplosionField
-import kotlinx.android.synthetic.main.item_text.view.*
+import kotlinx.android.synthetic.main.item_fillet_text.view.*
import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
@@ -15,7 +15,7 @@ import org.jetbrains.anko.sdk27.listeners.onLongClick
class HistoryKeyAdapter(activity: SearchActivity, val callBack: CallBack) :
- SimpleRecyclerAdapter(activity, R.layout.item_text) {
+ SimpleRecyclerAdapter(activity, R.layout.item_fillet_text) {
private val explosionField = ExplosionField.attach2Window(activity)
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 082faa675..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)
}
@@ -353,18 +355,16 @@ class BookSourceEditActivity :
}
private fun showKeyboardTopPopupWindow() {
- mSoftKeyboardTool?.isShowing?.let { if (it) return }
- if (!isFinishing) {
- mSoftKeyboardTool?.showAtLocation(ll_content, Gravity.BOTTOM, 0, 0)
+ mSoftKeyboardTool?.let {
+ if (it.isShowing) return
+ if (!isFinishing) {
+ it.showAtLocation(ll_content, Gravity.BOTTOM, 0, 0)
+ }
}
}
private fun closePopupWindow() {
- mSoftKeyboardTool?.let {
- if (it.isShowing) {
- it.dismiss()
- }
- }
+ mSoftKeyboardTool?.dismiss()
}
private inner class KeyboardOnGlobalChangeListener : ViewTreeObserver.OnGlobalLayoutListener {
diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
index 5b375b4cf..c94b59d7c 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
@@ -30,6 +30,7 @@ import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.ui.widget.SelectActionBar
+import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_book_source.*
@@ -109,7 +110,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity
private fun initRecyclerView() {
ATH.applyEdgeEffectColor(recycler_view)
recycler_view.layoutManager = LinearLayoutManager(this)
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(this))
adapter = BookSourceAdapter(this, this)
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()
diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt
index 314b45d08..d4a466948 100644
--- a/app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/book/source/manage/GroupManageDialog.kt
@@ -22,7 +22,11 @@ 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.yesButton
-import io.legado.app.utils.*
+import io.legado.app.ui.widget.recycler.VerticalDivider
+import io.legado.app.utils.applyTint
+import io.legado.app.utils.getViewModelOfActivity
+import io.legado.app.utils.requestInputMethod
+import io.legado.app.utils.splitNotBlank
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_group_manage.view.*
@@ -60,7 +64,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
tool_bar.setOnMenuItemClickListener(this)
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
App.db.bookSourceDao().liveGroup().observe(viewLifecycleOwner, Observer {
val groups = linkedSetOf()
diff --git a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceDialog.kt b/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceDialog.kt
index 4b129acea..d1ce27f2e 100644
--- a/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/changesource/ChangeSourceDialog.kt
@@ -18,8 +18,8 @@ import io.legado.app.R
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Book
import io.legado.app.data.entities.SearchBook
+import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.getPrefBoolean
-import io.legado.app.utils.getVerticalDivider
import io.legado.app.utils.getViewModel
import io.legado.app.utils.putPrefBoolean
import kotlinx.android.synthetic.main.dialog_change_source.*
@@ -92,7 +92,7 @@ class ChangeSourceDialog : DialogFragment(),
private fun initRecyclerView() {
adapter = ChangeSourceAdapter(requireContext(), this)
recycler_view.layoutManager = LinearLayoutManager(context)
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
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/chapterlist/BookmarkFragment.kt b/app/src/main/java/io/legado/app/ui/chapterlist/BookmarkFragment.kt
index c32c8bc42..b2337d0d9 100644
--- a/app/src/main/java/io/legado/app/ui/chapterlist/BookmarkFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/chapterlist/BookmarkFragment.kt
@@ -14,7 +14,7 @@ import io.legado.app.R
import io.legado.app.base.VMBaseFragment
import io.legado.app.data.entities.Bookmark
import io.legado.app.lib.theme.ATH
-import io.legado.app.utils.getVerticalDivider
+import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.getViewModelOfActivity
import kotlinx.android.synthetic.main.fragment_bookmark.*
@@ -38,7 +38,7 @@ class BookmarkFragment : VMBaseFragment(R.layout.fragment_
ATH.applyEdgeEffectColor(recycler_view)
adapter = BookmarkAdapter(this)
recycler_view.layoutManager = LinearLayoutManager(requireContext())
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
}
diff --git a/app/src/main/java/io/legado/app/ui/chapterlist/ChapterListFragment.kt b/app/src/main/java/io/legado/app/ui/chapterlist/ChapterListFragment.kt
index 0d9234e36..0bfc9a765 100644
--- a/app/src/main/java/io/legado/app/ui/chapterlist/ChapterListFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/chapterlist/ChapterListFragment.kt
@@ -15,7 +15,7 @@ import io.legado.app.data.entities.BookChapter
import io.legado.app.help.BookHelp
import io.legado.app.lib.theme.backgroundColor
import io.legado.app.ui.widget.recycler.UpLinearLayoutManager
-import io.legado.app.utils.getVerticalDivider
+import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.getViewModelOfActivity
import io.legado.app.utils.observeEvent
import kotlinx.android.synthetic.main.fragment_chapter_list.*
@@ -48,7 +48,7 @@ class ChapterListFragment : VMBaseFragment(R.layout.fragme
adapter = ChapterListAdapter(requireContext(), this)
mLayoutManager = UpLinearLayoutManager(requireContext())
recycler_view.layoutManager = mLayoutManager
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
}
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/config/ThemeConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
index ed5e8cf9a..1a1851ca1 100644
--- a/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
+++ b/app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
@@ -141,6 +141,7 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar
AppConfig.isNightTheme = true
}
}
+ putPrefInt("defaultTheme", which)
App.INSTANCE.applyDayNight()
recreateActivities()
}
diff --git a/app/src/main/java/io/legado/app/ui/explore/ExploreShowActivity.kt b/app/src/main/java/io/legado/app/ui/explore/ExploreShowActivity.kt
index 8cc5d7c7e..08a96a340 100644
--- a/app/src/main/java/io/legado/app/ui/explore/ExploreShowActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/explore/ExploreShowActivity.kt
@@ -10,7 +10,7 @@ import io.legado.app.data.entities.Book
import io.legado.app.data.entities.SearchBook
import io.legado.app.ui.book.info.BookInfoActivity
import io.legado.app.ui.widget.recycler.LoadMoreView
-import io.legado.app.utils.getVerticalDivider
+import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_explore_show.*
import org.jetbrains.anko.startActivity
@@ -34,7 +34,7 @@ class ExploreShowActivity : VMBaseActivity(R.layout.activi
private fun initRecyclerView() {
adapter = ExploreShowAdapter(this, this)
recycler_view.layoutManager = LinearLayoutManager(this)
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(this))
recycler_view.adapter = adapter
loadMoreView = LoadMoreView(this)
adapter.addFooterView(loadMoreView)
diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt b/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt
index 46c1271fc..af8cf50f4 100644
--- a/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/filechooser/FileChooserDialog.kt
@@ -15,7 +15,11 @@ import io.legado.app.R
import io.legado.app.constant.Theme
import io.legado.app.ui.filechooser.adapter.FileAdapter
import io.legado.app.ui.filechooser.adapter.PathAdapter
-import io.legado.app.utils.*
+import io.legado.app.ui.widget.recycler.VerticalDivider
+import io.legado.app.utils.FileUtils
+import io.legado.app.utils.applyTint
+import io.legado.app.utils.gone
+import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.dialog_file_chooser.*
@@ -133,7 +137,7 @@ class FileChooserDialog : DialogFragment(),
fileAdapter = FileAdapter(requireContext(), this)
pathAdapter = PathAdapter(requireContext(), this)
- rv_file.addItemDecoration(rv_file.getVerticalDivider())
+ rv_file.addItemDecoration(VerticalDivider(requireContext()))
rv_file.layoutManager = LinearLayoutManager(activity)
rv_file.adapter = fileAdapter
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/main/explore/ExploreAdapter.kt b/app/src/main/java/io/legado/app/ui/main/explore/ExploreAdapter.kt
index 6c4691284..3ceeca8b6 100644
--- a/app/src/main/java/io/legado/app/ui/main/explore/ExploreAdapter.kt
+++ b/app/src/main/java/io/legado/app/ui/main/explore/ExploreAdapter.kt
@@ -14,8 +14,8 @@ import io.legado.app.lib.theme.accentColor
import io.legado.app.utils.ACache
import io.legado.app.utils.gone
import io.legado.app.utils.visible
+import kotlinx.android.synthetic.main.item_fillet_text.view.*
import kotlinx.android.synthetic.main.item_find_book.view.*
-import kotlinx.android.synthetic.main.item_text.view.*
import kotlinx.coroutines.CoroutineScope
import org.jetbrains.anko.sdk27.listeners.onClick
import org.jetbrains.anko.sdk27.listeners.onLongClick
@@ -46,7 +46,7 @@ class ExploreAdapter(context: Context, private val scope: CoroutineScope, val ca
gl_child.removeAllViews()
kindList.map { kind ->
val tv = LayoutInflater.from(context)
- .inflate(R.layout.item_text, gl_child, false)
+ .inflate(R.layout.item_fillet_text, gl_child, false)
gl_child.addView(tv)
tv.text_view.text = kind.title
tv.text_view.onClick {
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/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/replacerule/GroupManageDialog.kt
index 6f9a61f4c..df39b9a1d 100644
--- a/app/src/main/java/io/legado/app/ui/replacerule/GroupManageDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/replacerule/GroupManageDialog.kt
@@ -22,7 +22,11 @@ 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.yesButton
-import io.legado.app.utils.*
+import io.legado.app.ui.widget.recycler.VerticalDivider
+import io.legado.app.utils.applyTint
+import io.legado.app.utils.getViewModelOfActivity
+import io.legado.app.utils.requestInputMethod
+import io.legado.app.utils.splitNotBlank
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_group_manage.view.*
@@ -60,7 +64,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
tool_bar.setOnMenuItemClickListener(this)
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
App.db.replaceRuleDao().liveGroup().observe(viewLifecycleOwner, Observer {
val groups = linkedSetOf()
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 4bcaa7945..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
@@ -28,6 +28,7 @@ import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.replacerule.edit.ReplaceEditDialog
import io.legado.app.ui.widget.SelectActionBar
+import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_replace_rule.*
@@ -54,6 +55,7 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi
private var dataInit = false
override fun onActivityCreated(savedInstanceState: Bundle?) {
+ initUriScheme()
initRecyclerView()
initSearchView()
initSelectActionView()
@@ -72,12 +74,28 @@ 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)
adapter = ReplaceRuleAdapter(this, this)
recycler_view.adapter = adapter
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(this))
val itemTouchCallback = ItemTouchCallback()
itemTouchCallback.onItemTouchCallbackListener = adapter
itemTouchCallback.isCanDrag = true
@@ -306,9 +324,7 @@ class ReplaceRuleActivity : VMBaseActivity(R.layout.activi
}
override fun edit(rule: ReplaceRule) {
- ReplaceEditDialog
- .newInstance(rule.id)
- .show(supportFragmentManager, "editReplace")
+ ReplaceEditDialog.show(supportFragmentManager, rule.id)
}
override fun toTop(rule: ReplaceRule) {
diff --git a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt
index 5470b4c5f..de5002931 100644
--- a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt
@@ -9,6 +9,7 @@ import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.DialogFragment
+import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Observer
import io.legado.app.R
import io.legado.app.constant.Theme
@@ -22,14 +23,13 @@ class ReplaceEditDialog : DialogFragment(),
companion object {
- fun newInstance(id: Long? = null): ReplaceEditDialog {
+ fun show(fragmentManager: FragmentManager, id: Long = -1, pattern: String? = null) {
val dialog = ReplaceEditDialog()
- id?.let {
- val bundle = Bundle()
- bundle.putLong("id", id)
- dialog.arguments = bundle
- }
- return dialog
+ val bundle = Bundle()
+ bundle.putLong("id", id)
+ bundle.putString("pattern", pattern)
+ dialog.arguments = bundle
+ dialog.show(fragmentManager, "editReplace")
}
}
@@ -56,7 +56,7 @@ class ReplaceEditDialog : DialogFragment(),
tool_bar.inflateMenu(R.menu.replace_edit)
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
tool_bar.setOnMenuItemClickListener(this)
- viewModel.replaceRuleData.observe(this, Observer {
+ viewModel.replaceRuleData.observe(viewLifecycleOwner, Observer {
upReplaceView(it)
})
arguments?.let {
diff --git a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt
index 281a0ac2d..f326c88c3 100644
--- a/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt
+++ b/app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditViewModel.kt
@@ -19,6 +19,12 @@ class ReplaceEditViewModel(application: Application) : BaseViewModel(application
App.db.replaceRuleDao().findById(id)?.let {
replaceRuleData.postValue(it)
}
+ } else {
+ bundle.getString("pattern")?.let { pattern ->
+ replaceRuleData.postValue(
+ ReplaceRule(pattern = pattern)
+ )
+ }
}
}
}
diff --git a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesActivity.kt b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesActivity.kt
index 5476657a4..3b53a6475 100644
--- a/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/article/RssArticlesActivity.kt
@@ -15,7 +15,7 @@ import io.legado.app.lib.theme.ATH
import io.legado.app.ui.rss.read.ReadRssActivity
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
import io.legado.app.ui.widget.recycler.LoadMoreView
-import io.legado.app.utils.getVerticalDivider
+import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.activity_rss_artivles.*
import kotlinx.android.synthetic.main.view_load_more.view.*
@@ -70,7 +70,7 @@ class RssArticlesActivity : VMBaseActivity(R.layout.activi
private fun initView() {
ATH.applyEdgeEffectColor(recycler_view)
recycler_view.layoutManager = LinearLayoutManager(this)
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(this))
adapter = RssArticlesAdapter(this, this)
recycler_view.adapter = adapter
loadMoreView = LoadMoreView(this)
diff --git a/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt b/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt
index a8eb39cea..f50329296 100644
--- a/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/favorites/RssFavoritesActivity.kt
@@ -10,7 +10,7 @@ import io.legado.app.base.BaseActivity
import io.legado.app.data.entities.RssStar
import io.legado.app.lib.theme.ATH
import io.legado.app.ui.rss.read.ReadRssActivity
-import io.legado.app.utils.getVerticalDivider
+import io.legado.app.ui.widget.recycler.VerticalDivider
import kotlinx.android.synthetic.main.view_refresh_recycler.*
import org.jetbrains.anko.startActivity
@@ -29,7 +29,7 @@ class RssFavoritesActivity : BaseActivity(R.layout.activity_rss_favorites),
private fun initView() {
ATH.applyEdgeEffectColor(recycler_view)
recycler_view.layoutManager = LinearLayoutManager(this)
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(this))
adapter = RssFavoritesAdapter(this, this)
recycler_view.adapter = adapter
}
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 09d5d6b2f..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
}
@@ -192,37 +197,46 @@ class RssSourceEditActivity :
}
private fun showKeyboardTopPopupWindow() {
- mSoftKeyboardTool?.isShowing?.let { if (it) return }
- if (!isFinishing) {
- mSoftKeyboardTool?.showAtLocation(ll_content, Gravity.BOTTOM, 0, 0)
+ mSoftKeyboardTool?.let {
+ if (it.isShowing) return
+ if (!isFinishing) {
+ it.showAtLocation(ll_content, Gravity.BOTTOM, 0, 0)
+ }
}
}
private fun closePopupWindow() {
- mSoftKeyboardTool?.let {
- if (it.isShowing) {
- it.dismiss()
+ mSoftKeyboardTool?.dismiss()
+ }
+
+ 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()
}
}
}
- 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 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/GroupManageDialog.kt b/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt
index 1ba2a567b..391281a4e 100644
--- a/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt
+++ b/app/src/main/java/io/legado/app/ui/rss/source/manage/GroupManageDialog.kt
@@ -22,7 +22,11 @@ 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.yesButton
-import io.legado.app.utils.*
+import io.legado.app.ui.widget.recycler.VerticalDivider
+import io.legado.app.utils.applyTint
+import io.legado.app.utils.getViewModelOfActivity
+import io.legado.app.utils.requestInputMethod
+import io.legado.app.utils.splitNotBlank
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
import kotlinx.android.synthetic.main.dialog_recycler_view.*
import kotlinx.android.synthetic.main.item_group_manage.view.*
@@ -60,7 +64,7 @@ class GroupManageDialog : DialogFragment(), Toolbar.OnMenuItemClickListener {
tool_bar.setOnMenuItemClickListener(this)
adapter = GroupAdapter(requireContext())
recycler_view.layoutManager = LinearLayoutManager(requireContext())
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(requireContext()))
recycler_view.adapter = adapter
App.db.rssSourceDao().liveGroup().observe(viewLifecycleOwner, Observer {
val groups = linkedSetOf()
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 e83a15c19..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
@@ -29,6 +29,7 @@ import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.ui.rss.source.edit.RssSourceEditActivity
import io.legado.app.ui.widget.SelectActionBar
+import io.legado.app.ui.widget.recycler.VerticalDivider
import io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_rss_source.*
@@ -49,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()
@@ -101,10 +103,26 @@ 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)
- recycler_view.addItemDecoration(recycler_view.getVerticalDivider())
+ recycler_view.addItemDecoration(VerticalDivider(this))
adapter = RssSourceAdapter(this, this)
recycler_view.adapter = adapter
val itemTouchCallback = ItemTouchCallback()
diff --git a/app/src/main/java/io/legado/app/ui/widget/DetailSeekBar.kt b/app/src/main/java/io/legado/app/ui/widget/DetailSeekBar.kt
index a959ce515..504837afd 100644
--- a/app/src/main/java/io/legado/app/ui/widget/DetailSeekBar.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/DetailSeekBar.kt
@@ -34,11 +34,11 @@ class DetailSeekBar(context: Context, attrs: AttributeSet?) : FrameLayout(contex
seek_bar.max = typedArray.getInteger(R.styleable.DetailSeekBar_max, 0)
typedArray.recycle()
- iv_seek_add.onClick {
+ iv_seek_plus.onClick {
seek_bar.progressAdd(1)
onChanged?.invoke(seek_bar.progress)
}
- iv_seek_remove.onClick {
+ iv_seek_reduce.onClick {
seek_bar.progressAdd(-1)
onChanged?.invoke(seek_bar.progress)
}
diff --git a/app/src/main/java/io/legado/app/ui/widget/KeyboardToolPop.kt b/app/src/main/java/io/legado/app/ui/widget/KeyboardToolPop.kt
index 01d36ca4a..56dff107c 100644
--- a/app/src/main/java/io/legado/app/ui/widget/KeyboardToolPop.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/KeyboardToolPop.kt
@@ -10,7 +10,7 @@ import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter
-import kotlinx.android.synthetic.main.item_text.view.*
+import kotlinx.android.synthetic.main.item_fillet_text.view.*
import kotlinx.android.synthetic.main.popup_keyboard_tool.view.*
import org.jetbrains.anko.sdk27.listeners.onClick
@@ -23,7 +23,7 @@ class KeyboardToolPop(
init {
@SuppressLint("InflateParams")
- this.contentView = LayoutInflater.from(context).inflate(R.layout.popup_keyboard_tool, null)
+ contentView = LayoutInflater.from(context).inflate(R.layout.popup_keyboard_tool, null)
isTouchable = true
isOutsideTouchable = false
@@ -40,7 +40,7 @@ class KeyboardToolPop(
}
inner class Adapter(context: Context) :
- SimpleRecyclerAdapter(context, R.layout.item_text) {
+ SimpleRecyclerAdapter(context, R.layout.item_fillet_text) {
override fun convert(holder: ItemViewHolder, item: String, payloads: MutableList) {
with(holder.itemView) {
diff --git a/app/src/main/java/io/legado/app/ui/widget/LabelsBar.kt b/app/src/main/java/io/legado/app/ui/widget/LabelsBar.kt
index ab933edbb..2d830ca55 100644
--- a/app/src/main/java/io/legado/app/ui/widget/LabelsBar.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/LabelsBar.kt
@@ -12,6 +12,7 @@ class LabelsBar(context: Context, attrs: AttributeSet?) : LinearLayout(context,
private val unUsedViews = arrayListOf()
private val usedViews = arrayListOf()
+ var textSize = 12f
fun setLabels(labels: Array) {
clear()
@@ -51,6 +52,7 @@ class LabelsBar(context: Context, attrs: AttributeSet?) : LinearLayout(context,
unUsedViews.removeAt(unUsedViews.lastIndex)
}
}
+ tv.textSize = textSize
tv.text = label
addView(tv)
}
diff --git a/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt b/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt
index ab931f3ab..d19c8929f 100644
--- a/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/TitleBar.kt
@@ -12,8 +12,8 @@ import com.google.android.material.appbar.AppBarLayout
import io.legado.app.R
import io.legado.app.lib.theme.primaryColor
import io.legado.app.utils.activity
-import io.legado.app.utils.getNavigationBarHeight
-import io.legado.app.utils.getStatusBarHeight
+import io.legado.app.utils.navigationBarHeight
+import io.legado.app.utils.statusBarHeight
import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.bottomPadding
import org.jetbrains.anko.topPadding
@@ -132,11 +132,11 @@ class TitleBar(context: Context, attrs: AttributeSet?) : AppBarLayout(context, a
}
if (a.getBoolean(R.styleable.TitleBar_fitStatusBar, true)) {
- topPadding = context.getStatusBarHeight()
+ topPadding = context.statusBarHeight
}
if (a.getBoolean(R.styleable.TitleBar_fitNavigationBar, false)) {
- bottomPadding = context.getNavigationBarHeight()
+ bottomPadding = context.navigationBarHeight
}
backgroundColor = context.primaryColor
diff --git a/app/src/main/java/io/legado/app/ui/widget/checkbox/SmoothCheckBox.kt b/app/src/main/java/io/legado/app/ui/widget/checkbox/SmoothCheckBox.kt
index 7179d8a38..b18e59332 100644
--- a/app/src/main/java/io/legado/app/ui/widget/checkbox/SmoothCheckBox.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/checkbox/SmoothCheckBox.kt
@@ -40,12 +40,12 @@ class SmoothCheckBox @JvmOverloads constructor(
private var mFloorUnCheckedColor = 0
private var mChecked = false
private var mTickDrawing = false
- private var mListener: OnCheckedChangeListener? = null
+ var onCheckedChangeListener: ((checkBox: SmoothCheckBox, isChecked: Boolean) -> Unit)? = null
init {
val ta = context.obtainStyledAttributes(attrs, R.styleable.SmoothCheckBox)
var tickColor = ThemeStore.accentColor(context)
- mCheckedColor = context.getCompatColor(R.color.background_card)
+ mCheckedColor = context.getCompatColor(R.color.background_menu)
mUnCheckedColor = context.getCompatColor(R.color.background_menu)
mFloorColor = context.getCompatColor(R.color.transparent30)
tickColor = ta.getColor(R.styleable.SmoothCheckBox_color_tick, tickColor)
@@ -89,9 +89,7 @@ class SmoothCheckBox @JvmOverloads constructor(
mChecked = checked
reset()
invalidate()
- if (mListener != null) {
- mListener!!.onCheckedChanged(this@SmoothCheckBox, mChecked)
- }
+ onCheckedChangeListener?.invoke(this@SmoothCheckBox, mChecked)
}
override fun toggle() {
@@ -114,9 +112,7 @@ class SmoothCheckBox @JvmOverloads constructor(
} else {
startUnCheckedAnimation()
}
- if (mListener != null) {
- mListener!!.onCheckedChanged(this@SmoothCheckBox, mChecked)
- }
+ onCheckedChangeListener?.invoke(this@SmoothCheckBox, mChecked)
} else {
this.isChecked = checked
}
@@ -308,14 +304,6 @@ class SmoothCheckBox @JvmOverloads constructor(
}, mAnimDuration.toLong())
}
- fun setOnCheckedChangeListener(l: OnCheckedChangeListener?) {
- mListener = l
- }
-
- interface OnCheckedChangeListener {
- fun onCheckedChanged(checkBox: SmoothCheckBox?, isChecked: Boolean)
- }
-
companion object {
private const val DEF_DRAW_SIZE = 25
private const val DEF_ANIM_DURATION = 300
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/ui/widget/prefs/NameListPreference.kt b/app/src/main/java/io/legado/app/ui/widget/prefs/NameListPreference.kt
index b51db7a19..d8b51e90f 100644
--- a/app/src/main/java/io/legado/app/ui/widget/prefs/NameListPreference.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/prefs/NameListPreference.kt
@@ -11,7 +11,7 @@ import io.legado.app.R
class NameListPreference(context: Context, attrs: AttributeSet) : ListPreference(context, attrs) {
init {
- widgetLayoutResource = R.layout.item_text
+ widgetLayoutResource = R.layout.item_fillet_text
}
override fun onBindViewHolder(holder: PreferenceViewHolder?) {
diff --git a/app/src/main/java/io/legado/app/ui/widget/recycler/DividerNoLast.kt b/app/src/main/java/io/legado/app/ui/widget/recycler/DividerNoLast.kt
new file mode 100644
index 000000000..5c9e38e4d
--- /dev/null
+++ b/app/src/main/java/io/legado/app/ui/widget/recycler/DividerNoLast.kt
@@ -0,0 +1,169 @@
+package io.legado.app.ui.widget.recycler
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.util.Log
+import android.view.View
+import android.widget.LinearLayout
+import androidx.recyclerview.widget.RecyclerView
+import kotlin.math.roundToInt
+
+
+/**
+ * 不画最后一条分隔线
+ */
+class DividerNoLast(context: Context, orientation: Int) :
+ RecyclerView.ItemDecoration() {
+
+ companion object {
+ const val HORIZONTAL = LinearLayout.HORIZONTAL
+ const val VERTICAL = LinearLayout.VERTICAL
+ }
+
+ private val tag = "DividerItem"
+ private val attrs = intArrayOf(android.R.attr.listDivider)
+
+ private var mDivider: Drawable? = null
+
+ /**
+ * Current orientation. Either [.HORIZONTAL] or [.VERTICAL].
+ */
+ private var mOrientation = 0
+
+ private val mBounds = Rect()
+
+ init {
+ val a = context.obtainStyledAttributes(attrs)
+ mDivider = a.getDrawable(0)
+ if (mDivider == null) {
+ Log.w(
+ tag, "@android:attr/listDivider was not set in the theme used for this "
+ + "DividerItemDecoration. Please set that attribute all call setDrawable()"
+ )
+ }
+ a.recycle()
+ setOrientation(orientation)
+ }
+
+ /**
+ * Sets the orientation for this divider. This should be called if
+ * [RecyclerView.LayoutManager] changes orientation.
+ *
+ * @param orientation [.HORIZONTAL] or [.VERTICAL]
+ */
+ fun setOrientation(orientation: Int) {
+ require(!(orientation != HORIZONTAL && orientation != VERTICAL)) { "Invalid orientation. It should be either HORIZONTAL or VERTICAL" }
+ mOrientation = orientation
+ }
+
+ /**
+ * Sets the [Drawable] for this divider.
+ *
+ * @param drawable Drawable that should be used as a divider.
+ */
+ fun setDrawable(drawable: Drawable) {
+ requireNotNull(drawable) { "Drawable cannot be null." }
+ mDivider = drawable
+ }
+
+ /**
+ * @return the [Drawable] for this divider.
+ */
+ fun getDrawable(): Drawable? {
+ return mDivider
+ }
+
+ override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
+ if (parent.layoutManager == null || mDivider == null) {
+ return
+ }
+ if (mOrientation == VERTICAL) {
+ drawVertical(c, parent)
+ } else {
+ drawHorizontal(c, parent)
+ }
+ }
+
+ private fun drawVertical(
+ canvas: Canvas,
+ parent: RecyclerView
+ ) {
+ canvas.save()
+ val left: Int
+ val right: Int
+ if (parent.clipToPadding) {
+ left = parent.paddingLeft
+ right = parent.width - parent.paddingRight
+ canvas.clipRect(
+ left, parent.paddingTop, right,
+ parent.height - parent.paddingBottom
+ )
+ } else {
+ left = 0
+ right = parent.width
+ }
+ val childCount = parent.childCount
+ for (i in 0 until childCount - 1) {
+ val child = parent.getChildAt(i)
+ parent.getDecoratedBoundsWithMargins(child, mBounds)
+ val bottom = mBounds.bottom + child.translationY.roundToInt()
+ val top = bottom - mDivider!!.intrinsicHeight
+ mDivider!!.setBounds(left, top, right, bottom)
+ mDivider!!.draw(canvas)
+ }
+ canvas.restore()
+ }
+
+ private fun drawHorizontal(canvas: Canvas, parent: RecyclerView) {
+ canvas.save()
+ val top: Int
+ val bottom: Int
+ if (parent.clipToPadding) {
+ top = parent.paddingTop
+ bottom = parent.height - parent.paddingBottom
+ canvas.clipRect(
+ parent.paddingLeft, top,
+ parent.width - parent.paddingRight, bottom
+ )
+ } else {
+ top = 0
+ bottom = parent.height
+ }
+ val childCount = parent.childCount
+ for (i in 0 until childCount - 1) {
+ val child = parent.getChildAt(i)
+ parent.layoutManager!!.getDecoratedBoundsWithMargins(child, mBounds)
+ val right = mBounds.right + child.translationX.roundToInt()
+ val left = right - mDivider!!.intrinsicWidth
+ mDivider!!.setBounds(left, top, right, bottom)
+ mDivider!!.draw(canvas)
+ }
+ canvas.restore()
+ }
+
+ override fun getItemOffsets(
+ outRect: Rect, view: View, parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ if (mDivider == null) {
+ outRect[0, 0, 0] = 0
+ return
+ }
+
+ if (mOrientation == VERTICAL) {
+ outRect[0, 0, 0] = mDivider!!.intrinsicHeight
+ } else {
+ val childAdapterPosition = parent.getChildAdapterPosition(view)
+ val lastCount = parent.adapter!!.itemCount - 1
+ //如果不是最后一条 正常赋值 如果是最后一条 赋值为0
+ if (childAdapterPosition != lastCount) {
+ outRect[0, 0, mDivider!!.intrinsicWidth] = 0
+ } else {
+ outRect[0, 0, 0] = 0
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/utils/RecyclerViewExtensions.kt b/app/src/main/java/io/legado/app/ui/widget/recycler/VerticalDivider.kt
similarity index 52%
rename from app/src/main/java/io/legado/app/utils/RecyclerViewExtensions.kt
rename to app/src/main/java/io/legado/app/ui/widget/recycler/VerticalDivider.kt
index 7dbed87ce..2ea450d4c 100644
--- a/app/src/main/java/io/legado/app/utils/RecyclerViewExtensions.kt
+++ b/app/src/main/java/io/legado/app/ui/widget/recycler/VerticalDivider.kt
@@ -1,15 +1,16 @@
-package io.legado.app.utils
+package io.legado.app.ui.widget.recycler
+import android.content.Context
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.DividerItemDecoration
-import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
+class VerticalDivider(context: Context) : DividerItemDecoration(context, VERTICAL) {
-fun RecyclerView.getVerticalDivider(): DividerItemDecoration {
- return DividerItemDecoration(context, DividerItemDecoration.VERTICAL).apply {
+ init {
ContextCompat.getDrawable(context, R.drawable.ic_divider)?.let {
this.setDrawable(it)
}
}
+
}
\ No newline at end of file
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 00f1d4c58..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()
@@ -147,7 +152,7 @@ object BitmapUtils {
//图片不被压缩
fun convertViewToBitmap(view: View, bitmapWidth: Int, bitmapHeight: Int): Bitmap {
- val bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888)
+ val bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Config.ARGB_8888)
view.draw(Canvas(bitmap))
return bitmap
}
diff --git a/app/src/main/java/io/legado/app/utils/ColorUtils.kt b/app/src/main/java/io/legado/app/utils/ColorUtils.kt
index 3ca7ab5bc..9eceb6724 100644
--- a/app/src/main/java/io/legado/app/utils/ColorUtils.kt
+++ b/app/src/main/java/io/legado/app/utils/ColorUtils.kt
@@ -4,6 +4,9 @@ import android.graphics.Color
import androidx.annotation.ColorInt
import androidx.annotation.FloatRange
+import kotlin.math.max
+import kotlin.math.min
+import kotlin.math.roundToInt
@Suppress("unused")
object ColorUtils {
@@ -53,7 +56,7 @@ object ColorUtils {
@ColorInt
fun adjustAlpha(@ColorInt color: Int, @FloatRange(from = 0.0, to = 1.0) factor: Float): Int {
- val alpha = Math.round(Color.alpha(color) * factor)
+ val alpha = (Color.alpha(color) * factor).roundToInt()
val red = Color.red(color)
val green = Color.green(color)
val blue = Color.blue(color)
@@ -62,7 +65,7 @@ object ColorUtils {
@ColorInt
fun withAlpha(@ColorInt baseColor: Int, @FloatRange(from = 0.0, to = 1.0) alpha: Float): Int {
- val a = Math.min(255, Math.max(0, (alpha * 255).toInt())) shl 24
+ val a = min(255, max(0, (alpha * 255).toInt())) shl 24
val rgb = 0x00ffffff and baseColor
return a + rgb
}
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 1ad897272..3436c9d94 100644
--- a/app/src/main/java/io/legado/app/utils/ContextExtensions.kt
+++ b/app/src/main/java/io/legado/app/utils/ContextExtensions.kt
@@ -1,13 +1,13 @@
+@file:Suppress("unused")
package io.legado.app.utils
import android.annotation.SuppressLint
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
+import android.content.*
import android.content.res.ColorStateList
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
+import android.net.Uri
import android.os.BatteryManager
import android.provider.Settings
import androidx.annotation.ColorRes
@@ -31,7 +31,6 @@ fun Context.getPrefBoolean(key: String, defValue: Boolean = false) =
fun Context.putPrefBoolean(key: String, value: Boolean = false) =
defaultSharedPreferences.edit { putBoolean(key, value) }
-
fun Context.getPrefInt(key: String, defValue: Int = 0) =
defaultSharedPreferences.getInt(key, defValue)
@@ -70,35 +69,27 @@ fun Context.getCompatColorStateList(@ColorRes id: Int): ColorStateList? =
/**
* 系统息屏时间
*/
-fun Context.getScreenOffTime(): Int {
- var screenOffTime = 0
- try {
- screenOffTime = Settings.System.getInt(contentResolver, Settings.System.SCREEN_OFF_TIMEOUT)
- } catch (e: Exception) {
- e.printStackTrace()
- }
- return screenOffTime
+val Context.sysScreenOffTime: Int
+ get() {
+ var screenOffTime = 0
+ try {
+ screenOffTime = Settings.System.getInt(contentResolver, Settings.System.SCREEN_OFF_TIMEOUT)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ return screenOffTime
}
-fun Context.getStatusBarHeight(): Int {
- val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
- return resources.getDimensionPixelSize(resourceId)
+val Context.statusBarHeight: Int
+ get() {
+ val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
+ return resources.getDimensionPixelSize(resourceId)
}
-fun Context.getNavigationBarHeight(): Int {
- val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
- 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)
- }
+val Context.navigationBarHeight: Int
+ get() {
+ val resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android")
+ return resources.getDimensionPixelSize(resourceId)
}
@SuppressLint("SetWorldReadable")
@@ -132,6 +123,19 @@ fun Context.shareWithQr(title: String, text: String) {
}
}
+fun Context.sendToClip(text: String) {
+ val clipboard =
+ getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
+ val clipData = ClipData.newPlainText(null, text)
+ clipboard?.let {
+ clipboard.setPrimaryClip(clipData)
+ toast(R.string.copy_complete)
+ }
+}
+
+/**
+ * 系统是否暗色主题
+ */
fun Context.sysIsDarkMode(): Boolean {
val mode = resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
return mode == Configuration.UI_MODE_NIGHT_YES
@@ -140,8 +144,31 @@ fun Context.sysIsDarkMode(): Boolean {
/**
* 获取电量
*/
-fun Context.getBettery(): Int {
- val iFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
- val batteryStatus = registerReceiver(null, iFilter)
- return batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
+val Context.sysBattery: Int
+ get() {
+ val iFilter = IntentFilter(Intent.ACTION_BATTERY_CHANGED)
+ val batteryStatus = registerReceiver(null, iFilter)
+ return batteryStatus?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
+}
+
+fun Context.openUrl(url: String) {
+ openUrl(Uri.parse(url))
+}
+
+fun Context.openUrl(uri: Uri) {
+ val intent = Intent(Intent.ACTION_VIEW)
+ intent.data = uri
+ if (intent.resolveActivity(packageManager) != null) {
+ try {
+ startActivity(intent)
+ } catch (e: Exception) {
+ toast(e.localizedMessage ?: "open url error")
+ }
+ } else {
+ try {
+ startActivity(Intent.createChooser(intent, "请选择浏览器"))
+ } catch (e: Exception) {
+ toast(e.localizedMessage ?: "open url error")
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/io/legado/app/utils/FloatExtensions.kt b/app/src/main/java/io/legado/app/utils/FloatExtensions.kt
index af9576c00..4e3c7f9ed 100644
--- a/app/src/main/java/io/legado/app/utils/FloatExtensions.kt
+++ b/app/src/main/java/io/legado/app/utils/FloatExtensions.kt
@@ -2,14 +2,13 @@ package io.legado.app.utils
import android.content.res.Resources
-
-val Float.dp: Float // [xxhdpi](360 -> 1080)
+val Float.dp: Float
get() = android.util.TypedValue.applyDimension(
android.util.TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics
)
-val Float.sp: Float // [xxhdpi](360 -> 1080)
+val Float.sp: Float
get() = android.util.TypedValue.applyDimension(
android.util.TypedValue.COMPLEX_UNIT_SP, this, Resources.getSystem().displayMetrics
)
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 02faf53c3..3f4f40ee2 100644
--- a/app/src/main/java/io/legado/app/utils/StringExtensions.kt
+++ b/app/src/main/java/io/legado/app/utils/StringExtensions.kt
@@ -1,7 +1,5 @@
package io.legado.app.utils
-// import org.apache.commons.text.StringEscapeUtils
-
fun String?.safeTrim() = if (this.isNullOrBlank()) null else this.trim()
fun String?.isContentPath(): Boolean = this?.startsWith("content://") == true
@@ -50,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 5d191609f..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
@@ -50,10 +52,10 @@ fun View.visible() {
}
fun View.visible(visible: Boolean) {
- visibility = if (visible) {
- VISIBLE
- } else {
- INVISIBLE
+ if (visible && visibility != VISIBLE) {
+ visibility = VISIBLE
+ } else if (!visible && visibility == VISIBLE) {
+ visibility = INVISIBLE
}
}
@@ -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/drawable/recyclerview_divider_horizontal.xml b/app/src/main/res/drawable/recyclerview_divider_horizontal.xml
new file mode 100644
index 000000000..2d2d95362
--- /dev/null
+++ b/app/src/main/res/drawable/recyclerview_divider_horizontal.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/recyclerview_item_divider.xml b/app/src/main/res/drawable/recyclerview_divider_vertical.xml
similarity index 100%
rename from app/src/main/res/drawable/recyclerview_item_divider.xml
rename to app/src/main/res/drawable/recyclerview_divider_vertical.xml
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_aloud.xml b/app/src/main/res/layout/dialog_read_aloud.xml
index 37c5cc0d6..ea04696d4 100644
--- a/app/src/main/res/layout/dialog_read_aloud.xml
+++ b/app/src/main/res/layout/dialog_read_aloud.xml
@@ -23,7 +23,7 @@
android:contentDescription="@string/chapter_list"
android:src="@drawable/ic_toc"
android:tint="@color/tv_text_default"
- android:tooltipText="@string/auto_next_page"
+ android:tooltipText="@string/chapter_list"
app:backgroundTint="@color/background_menu"
app:elevation="2dp"
app:fabSize="mini"
@@ -43,7 +43,7 @@
android:contentDescription="@string/to_backstage"
android:src="@drawable/ic_visibility_off"
android:tint="@color/tv_text_default"
- android:tooltipText="@string/replace_rule_title"
+ android:tooltipText="@string/to_backstage"
app:backgroundTint="@color/background_menu"
app:elevation="2dp"
app:fabSize="mini"
@@ -79,9 +79,11 @@
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
+ android:tooltipText="@string/menu"
android:contentDescription="@string/menu"
android:src="@drawable/ic_menu"
- android:tint="@color/tv_text_default" />
+ android:tint="@color/tv_text_default"
+ tools:ignore="UnusedAttribute" />
+ android:tint="@color/tv_text_default"
+ tools:ignore="UnusedAttribute" />
+ android:tint="@color/tv_text_default"
+ tools:ignore="UnusedAttribute" />
+ android:tint="@color/tv_text_default"
+ tools:ignore="UnusedAttribute" />
+ android:tint="@color/tv_text_default"
+ tools:ignore="UnusedAttribute" />
+ android:tint="@color/tv_text_default"
+ tools:ignore="UnusedAttribute" />
@@ -160,8 +172,11 @@
+ android:tint="@color/tv_text_default"
+ tools:ignore="UnusedAttribute" />
+
+
+
@@ -77,7 +92,6 @@
android:paddingBottom="3dp"
android:gravity="center"
android:textSize="14sp"
- app:layout_constraintLeft_toRightOf="@+id/tv_padding"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
@@ -133,8 +147,8 @@
android:id="@+id/tv_page_anim"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingStart="10dp"
- android:paddingEnd="10dp"
+ android:paddingStart="6dp"
+ android:paddingEnd="6dp"
android:text="@string/page_anim"
app:layout_constraintTop_toBottomOf="@+id/vw_bg_fg" />
@@ -142,8 +156,8 @@
android:id="@+id/rg_page_anim"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingLeft="16dp"
- android:paddingRight="16dp"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
android:orientation="horizontal"
android:gravity="center"
app:layout_constraintTop_toBottomOf="@id/tv_page_anim">
@@ -219,17 +233,43 @@
+ app:layout_constraintTop_toBottomOf="@+id/vw_bg_fg1"
+ app:layout_constraintLeft_toLeftOf="parent"
+ app:layout_constraintRight_toLeftOf="@+id/tv_share_layout" />
+
+
+
+
@@ -25,6 +28,7 @@
android:id="@+id/dsb_header_padding_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:visibility="gone"
app:title="@string/padding_bottom"
app:max="100" />
@@ -32,6 +36,7 @@
android:id="@+id/dsb_header_padding_left"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:visibility="gone"
app:title="@string/padding_left"
app:max="100" />
@@ -39,13 +44,14 @@
android:id="@+id/dsb_header_padding_right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:visibility="gone"
app:title="@string/padding_right"
app:max="100" />
diff --git a/app/src/main/res/layout/dialog_toc_regex.xml b/app/src/main/res/layout/dialog_toc_regex.xml
new file mode 100644
index 000000000..43d0c5d3b
--- /dev/null
+++ b/app/src/main/res/layout/dialog_toc_regex.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_toc_regex_edit.xml b/app/src/main/res/layout/dialog_toc_regex_edit.xml
new file mode 100644
index 000000000..07c0816a4
--- /dev/null
+++ b/app/src/main/res/layout/dialog_toc_regex_edit.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_book_source.xml b/app/src/main/res/layout/item_book_source.xml
index ee1174355..3ae65e3b9 100644
--- a/app/src/main/res/layout/item_book_source.xml
+++ b/app/src/main/res/layout/item_book_source.xml
@@ -62,6 +62,7 @@
android:id="@+id/iv_explore"
android:layout_width="8dp"
android:layout_height="8dp"
+ android:contentDescription="@string/more_menu"
android:src="@color/md_green_600"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
diff --git a/app/src/main/res/layout/item_fillet_text.xml b/app/src/main/res/layout/item_fillet_text.xml
new file mode 100644
index 000000000..331bd8a2c
--- /dev/null
+++ b/app/src/main/res/layout/item_fillet_text.xml
@@ -0,0 +1,15 @@
+
+
diff --git a/app/src/main/res/layout/item_group_manage.xml b/app/src/main/res/layout/item_group_manage.xml
index d7d85630a..763a19acd 100644
--- a/app/src/main/res/layout/item_group_manage.xml
+++ b/app/src/main/res/layout/item_group_manage.xml
@@ -4,6 +4,7 @@
android:layout_height="wrap_content"
android:background="@color/background"
android:padding="8dp"
+ android:gravity="center_vertical"
android:orientation="horizontal">
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/popup_action_menu.xml b/app/src/main/res/layout/popup_action_menu.xml
new file mode 100644
index 000000000..c6b9067d7
--- /dev/null
+++ b/app/src/main/res/layout/popup_action_menu.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/view_detail_seek_bar.xml b/app/src/main/res/layout/view_detail_seek_bar.xml
index 57261a37a..af87a83bb 100644
--- a/app/src/main/res/layout/view_detail_seek_bar.xml
+++ b/app/src/main/res/layout/view_detail_seek_bar.xml
@@ -15,9 +15,10 @@
tools:ignore="HardcodedText" />
@@ -31,9 +32,10 @@
android:max="10" />
diff --git a/app/src/main/res/layout/view_select_action_bar.xml b/app/src/main/res/layout/view_select_action_bar.xml
index 97d775dc3..e725a57b7 100644
--- a/app/src/main/res/layout/view_select_action_bar.xml
+++ b/app/src/main/res/layout/view_select_action_bar.xml
@@ -46,10 +46,12 @@
android:layout_width="36dp"
android:layout_height="36dp"
android:background="?attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/more_menu"
+ android:tooltipText="@string/more_menu"
android:padding="6dp"
android:src="@drawable/ic_more_vert"
android:tint="@color/tv_text_default"
android:visibility="gone"
- tools:ignore="RtlHardcoded" />
+ tools:ignore="RtlHardcoded,UnusedAttribute" />
\ No newline at end of file
diff --git a/app/src/main/res/menu/content_select_action.xml b/app/src/main/res/menu/content_select_action.xml
index d83b82ef3..d11ee6154 100644
--- a/app/src/main/res/menu/content_select_action.xml
+++ b/app/src/main/res/menu/content_select_action.xml
@@ -1,9 +1,19 @@
-