pull/149/head
yangyxd 5 years ago
commit fb72f0b437
  1. 1
      app/src/main/AndroidManifest.xml
  2. 68
      app/src/main/assets/txtTocRule.json
  3. 5
      app/src/main/assets/updateLog.md
  4. 1
      app/src/main/java/io/legado/app/constant/PreferKey.kt
  5. 5
      app/src/main/java/io/legado/app/help/AppConfig.kt
  6. 8
      app/src/main/java/io/legado/app/help/BookHelp.kt
  7. 14
      app/src/main/java/io/legado/app/model/localBook/LocalBook.kt
  8. 4
      app/src/main/java/io/legado/app/service/TTSReadAloudService.kt
  9. 13
      app/src/main/java/io/legado/app/ui/book/read/Help.kt
  10. 45
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  11. 7
      app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt
  12. 5
      app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt
  13. 53
      app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
  14. 16
      app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
  15. 7
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt
  16. 16
      app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt
  17. 2
      app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt
  18. 8
      app/src/main/java/io/legado/app/ui/widget/TitleBar.kt
  19. 7
      app/src/main/java/io/legado/app/utils/ContextExtensions.kt
  20. 6
      app/src/main/res/layout/activity_book_read.xml
  21. 2
      app/src/main/res/layout/popup_action_menu.xml
  22. 6
      app/src/main/res/values/array_values.xml
  23. 2
      app/src/main/res/values/arrays.xml
  24. 2
      app/src/main/res/values/pref_key_value.xml
  25. 1
      app/src/main/res/values/strings.xml
  26. 8
      app/src/main/res/xml/pref_config_read.xml

@ -185,6 +185,7 @@
android:launchMode="singleTop" /> android:launchMode="singleTop" />
<activity <activity
android:name="io.legado.app.ui.book.chapterlist.ChapterListActivity" android:name="io.legado.app.ui.book.chapterlist.ChapterListActivity"
android:screenOrientation="behind"
android:launchMode="singleTop" /> android:launchMode="singleTop" />
<!--RSS阅读--> <!--RSS阅读-->
<activity <activity

@ -2,73 +2,97 @@
{ {
"enable": true, "enable": true,
"name": "目录", "name": "目录",
"rule": "^[  \\t]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$", "rule": "^[  \\t]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
"serialNumber": 0 "serialNumber": 0
}, },
{ {
"enable": false, "enable": false,
"name": "目录(不匹配行前空白)", "name": "目录(空白)",
"rule": "^(?<= |\\s)(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$", "rule": "(?<=[ \\s])(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
"serialNumber": 1 "serialNumber": 1
}, },
{ {
"enable": false, "enable": false,
"name": "目录(去简介)", "name": "目录(去简介)",
"rule": "^(?<= |\\s)(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$", "rule": "(?<=[ \\s])(?:前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$",
"serialNumber": 2 "serialNumber": 2
}, },
{ {
"enable": false, "enable": false,
"name": "目录(古典、轻小说备用)", "name": "目录(古典、轻小说备用)",
"rule": "^[  \\t]{0,4}(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$", "rule": "^[  \\t]{0,4}(?:前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$",
"serialNumber": 3 "serialNumber": 3
}, },
{
"enable": false,
"name": "数字(纯数字标题)",
"rule": "(?<=[ \\s])\\d+[  \\t]{0,4}$",
"serialNumber": 4
},
{ {
"enable": true, "enable": true,
"name": "数字 分隔符 标题名称", "name": "数字 分隔符 标题名称",
"rule": "^[  \\t]{0,4}\\d{1,5}[\\,\\., 、\\-].{1,30}$", "rule": "^[  \\t]{0,4}\\d{1,5}[\\,\\., 、\\-].{1,30}$",
"serialNumber": 4 "serialNumber": 5
}, },
{ {
"enable": true, "enable": true,
"name": "正文 标题/序号", "name": "正文 标题/序号",
"rule": "^[  \\t]{0,4}正文\\s{1,4}.{0,20}$", "rule": "^[  \\t]{0,4}正文[  ]{1,4}.{0,20}$",
"serialNumber": 5 "serialNumber": 6
}, },
{ {
"enable": true, "enable": true,
"name": "Chapter/Section/Part/Episode 序号 标题", "name": "Chapter/Section/Part/Episode 序号 标题",
"rule": "^[  \\t]{0,4}(?:[Cc]hapter|[Ss]ection|[Pp]art|PART|[Ee]pisode|(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外)\\s{0,4}\\d{1,4}.{0,30}$",
"serialNumber": 7
},
{
"enable": false,
"name": "Chapter(去简介)",
"rule": "^[  \\t]{0,4}(?:[Cc]hapter|[Ss]ection|[Pp]art|PART|[Ee]pisode)\\s{0,4}\\d{1,4}.{0,30}$", "rule": "^[  \\t]{0,4}(?:[Cc]hapter|[Ss]ection|[Pp]art|PART|[Ee]pisode)\\s{0,4}\\d{1,4}.{0,30}$",
"serialNumber": 6 "serialNumber": 8
}, },
{ {
"enable": true, "enable": true,
"name": "特殊符号 序号 标题", "name": "特殊符号 序号 标题",
"rule": "^[  \\t]{0,4}[〈〖〔【][第卷][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10}[章节][\\.:: \f\t].{0,30}$", "rule": "(?<=[\\s ]{0,4}).{1,3}(?:第|卷|[Cc]hapter)[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10}[章节]?[\\.:: \f\t].{0,20}$",
"serialNumber": 7 "serialNumber": 9
}, },
{ {
"enable": true, "enable": false,
"name": "特殊符号 标题", "name": "特殊符号 标题(成对)",
"rule": "^[  \\t]{0,4}[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$", "rule": "(?<=[\\s ]{0,4})(?:[\\[〈「『〖〔《(【\\(].{1,30}[\\)】)》〕〗』」〉\\]]?|(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外)[  ]{0,4}$",
"serialNumber": 8 "serialNumber": 10
}, },
{ {
"enable":false, "enable":true,
"name": "特殊符号 标题(不匹配空白字符)", "name": "特殊符号 标题(单个)",
"rule": "^(?<= |\\s)[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$", "rule": "(?<=[\\s ]{0,4})(?:[☆★✦✧].{1,30}|(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外)[  ]{0,4}$",
"serialNumber": 9 "serialNumber": 11
}, },
{ {
"enable": true, "enable": true,
"name": "章/卷 序号 标题", "name": "章/卷 序号 标题",
"rule": "^[  \\t]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|[卷章][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,8})\\s{0,4}.{0,30}$", "rule": "^[ \\t ]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|[卷章][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,8})[  ]{0,4}.{0,30}$",
"serialNumber": 10 "serialNumber": 12
}, },
{ {
"enable":false, "enable":false,
"name": "顶格标题", "name": "顶格标题",
"rule": "^\\S.{1,20}$", "rule": "^\\S.{1,20}$",
"serialNumber": 11 "serialNumber": 13
},
{
"enable":false,
"name": "双标题(前向)",
"rule": "(?m)(?<=[ \\t ]{0,4})第[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,8}章.{0,30}$(?=[\\s ]{0,8}第[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,8}章)",
"serialNumber": 14
},
{
"enable":false,
"name": "双标题(后向)",
"rule": "(?m)(?<=[ \\t ]{0,4}第[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,8}章.{0,30}$[\\s ]{0,8})第[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,8}章.{0,30}$",
"serialNumber": 15
} }
] ]

@ -2,8 +2,13 @@
* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。 * 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
* 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。 * 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。
**2020/03/10**
* 优化文字选择菜单弹出位置
* 添加屏幕方向控制
**2020/03/09** **2020/03/09**
* 底部文字对齐 * 底部文字对齐
* 主题添加阴影调节 by yangyxd
**2020/03/08** **2020/03/08**
* 订阅长按保存图片 * 订阅长按保存图片

@ -3,7 +3,6 @@ package io.legado.app.constant
object PreferKey { object PreferKey {
const val versionCode = "versionCode" const val versionCode = "versionCode"
const val themeMode = "themeMode" const val themeMode = "themeMode"
const val downloadPath = "downloadPath"
const val hideStatusBar = "hideStatusBar" const val hideStatusBar = "hideStatusBar"
const val clickTurnPage = "clickTurnPage" const val clickTurnPage = "clickTurnPage"
const val clickAllNext = "clickAllNext" const val clickAllNext = "clickAllNext"

@ -32,6 +32,9 @@ object AppConfig {
App.INSTANCE.putPrefBoolean("transparentStatusBar", value) App.INSTANCE.putPrefBoolean("transparentStatusBar", value)
} }
val requestedDirection: String?
get() = App.INSTANCE.getPrefString(R.string.pk_requested_direction)
var backupPath: String? var backupPath: String?
get() = App.INSTANCE.getPrefString(PreferKey.backupPath) get() = App.INSTANCE.getPrefString(PreferKey.backupPath)
set(value) { set(value) {
@ -49,7 +52,7 @@ object AppConfig {
} }
val autoRefreshBook: Boolean val autoRefreshBook: Boolean
get() = App.INSTANCE.getPrefBoolean(App.INSTANCE.getString(R.string.pk_auto_refresh)) get() = App.INSTANCE.getPrefBoolean(R.string.pk_auto_refresh)
var threadCount: Int var threadCount: Int
get() = App.INSTANCE.getPrefInt(PreferKey.threadCount, 16) get() = App.INSTANCE.getPrefInt(PreferKey.threadCount, 16)

@ -4,8 +4,8 @@ import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import com.github.houbb.opencc4j.core.impl.ZhConvertBootstrap import com.github.houbb.opencc4j.core.impl.ZhConvertBootstrap
import io.legado.app.App import io.legado.app.App
import io.legado.app.R
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.data.entities.BookChapter import io.legado.app.data.entities.BookChapter
import io.legado.app.data.entities.ReplaceRule import io.legado.app.data.entities.ReplaceRule
@ -21,7 +21,7 @@ import kotlin.math.min
object BookHelp { object BookHelp {
private const val cacheFolderName = "book_cache" private const val cacheFolderName = "book_cache"
val downloadPath: String val downloadPath: String
get() = App.INSTANCE.getPrefString(PreferKey.downloadPath) get() = App.INSTANCE.getPrefString(R.string.pk_download_path)
?: App.INSTANCE.getExternalFilesDir(null)?.absolutePath ?: App.INSTANCE.getExternalFilesDir(null)?.absolutePath
?: App.INSTANCE.cacheDir.absolutePath ?: App.INSTANCE.cacheDir.absolutePath
@ -300,6 +300,8 @@ object BookHelp {
1 -> c = ZhConvertBootstrap.newInstance().toSimple(c) 1 -> c = ZhConvertBootstrap.newInstance().toSimple(c)
2 -> c = ZhConvertBootstrap.newInstance().toTraditional(c) 2 -> c = ZhConvertBootstrap.newInstance().toTraditional(c)
} }
return c.replace("\\s*\\n+\\s*".toRegex(), "\n${ReadBookConfig.bodyIndent}") return c
.replace("\\s*\\n+\\s*".toRegex(), "\n${ReadBookConfig.bodyIndent}")
.replace("[\\n\\s]+$".toRegex(), "") //移除尾部空行
} }
} }

@ -4,6 +4,7 @@ import android.net.Uri
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import io.legado.app.App import io.legado.app.App
import io.legado.app.data.entities.Book import io.legado.app.data.entities.Book
import io.legado.app.help.BookHelp
import io.legado.app.utils.FileUtils import io.legado.app.utils.FileUtils
@ -12,8 +13,17 @@ object LocalBook {
fun importFile(doc: DocumentFile) { fun importFile(doc: DocumentFile) {
doc.name?.let { fileName -> doc.name?.let { fileName ->
val str = fileName.substringBeforeLast(".") val str = fileName.substringBeforeLast(".")
var name = str.substringBefore("作者") val authorIndex = str.indexOf("作者")
val author = str.substringAfter("作者", "") var name: String
var author: String
if (authorIndex == -1) {
name = str
author = ""
} else {
name = str.substring(0, authorIndex)
author = str.substring(authorIndex)
author = BookHelp.formatAuthor(author)
}
val smhStart = name.indexOf("") val smhStart = name.indexOf("")
val smhEnd = name.indexOf("") val smhEnd = name.indexOf("")
if (smhStart != -1 && smhEnd != -1) { if (smhStart != -1 && smhEnd != -1) {

@ -175,9 +175,7 @@ class TTSReadAloudService : BaseReadAloudService(), TextToSpeech.OnInitListener
} }
override fun onError(s: String) { override fun onError(s: String) {
launch { pauseReadAloud(true)
toast(s)
}
} }
} }

@ -3,6 +3,7 @@ package io.legado.app.ui.book.read
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.pm.ActivityInfo
import android.os.AsyncTask import android.os.AsyncTask
import android.os.Build import android.os.Build
import android.view.* import android.view.*
@ -62,6 +63,18 @@ object Help {
} }
} }
@SuppressLint("SourceLockedOrientationActivity")
fun setOrientation(activity: Activity) = activity.apply {
when (AppConfig.requestedDirection) {
"0" -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
"1" -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
"2" -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
"3" -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
}
}
/** /**
* 返回NavigationBar是否存在 * 返回NavigationBar是否存在
* 该方法需要在View完全被绘制出来之后调用否则判断不了 * 该方法需要在View完全被绘制出来之后调用否则判断不了

@ -3,6 +3,7 @@ package io.legado.app.ui.book.read
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.res.Configuration
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.os.Handler import android.os.Handler
@ -13,6 +14,7 @@ import androidx.core.view.isVisible
import androidx.core.view.size import androidx.core.view.size
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
import io.legado.app.BuildConfig
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.EventBus import io.legado.app.constant.EventBus
@ -88,6 +90,11 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
override val pageFactory: TextPageFactory get() = page_view.pageFactory override val pageFactory: TextPageFactory get() = page_view.pageFactory
override val headerHeight: Int get() = page_view.curPage.headerHeight override val headerHeight: Int get() = page_view.curPage.headerHeight
override fun onCreate(savedInstanceState: Bundle?) {
Help.setOrientation(this)
super.onCreate(savedInstanceState)
}
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
Help.upLayoutInDisplayCutoutMode(window) Help.upLayoutInDisplayCutoutMode(window)
initView() initView()
@ -106,6 +113,11 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
upSystemUiVisibility() upSystemUiVisibility()
} }
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
ReadBook.loadContent()
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
upSystemUiVisibility() upSystemUiVisibility()
@ -338,7 +350,8 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
override fun onTouch(v: View, event: MotionEvent): Boolean { override fun onTouch(v: View, event: MotionEvent): Boolean {
when (event.action) { when (event.action) {
MotionEvent.ACTION_MOVE, MotionEvent.ACTION_UP -> { MotionEvent.ACTION_DOWN -> textActionMenu?.dismiss()
MotionEvent.ACTION_MOVE -> {
when (v.id) { when (v.id) {
R.id.cursor_left -> page_view.curPage.selectStartMove( R.id.cursor_left -> page_view.curPage.selectStartMove(
event.rawX + cursor_left.width, event.rawX + cursor_left.width,
@ -350,6 +363,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
) )
} }
} }
MotionEvent.ACTION_UP -> showTextActionMenu()
} }
return true return true
} }
@ -357,11 +371,12 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
/** /**
* 更新文字选择开始位置 * 更新文字选择开始位置
*/ */
override fun upSelectedStart(x: Float, y: Float) { override fun upSelectedStart(x: Float, y: Float, top: Float) {
cursor_left.x = x - cursor_left.width cursor_left.x = x - cursor_left.width
cursor_left.y = y cursor_left.y = y
cursor_left.visible(true) cursor_left.visible(true)
showTextActionMenu() text_menu_position.x = x
text_menu_position.y = top
} }
/** /**
@ -371,7 +386,6 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
cursor_right.x = x cursor_right.x = x
cursor_right.y = y cursor_right.y = y
cursor_right.visible(true) cursor_right.visible(true)
showTextActionMenu()
} }
/** /**
@ -386,19 +400,24 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
/** /**
* 显示文本操作菜单 * 显示文本操作菜单
*/ */
private fun showTextActionMenu() { override fun showTextActionMenu() {
textActionMenu ?: let { textActionMenu ?: let {
textActionMenu = TextActionMenu(this, this) textActionMenu = TextActionMenu(this, this).apply {
contentView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
} }
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 -> textActionMenu?.let { popup ->
val popupHeight = popup.contentView.measuredHeight
val x = text_menu_position.x.toInt()
var y = text_menu_position.y.toInt() - popupHeight
if (y < statusBarHeight) {
y = (cursor_left.y + cursor_left.height).toInt()
}
if (cursor_right.y > y && cursor_right.y < y + popupHeight) {
y = (cursor_right.y + cursor_right.height).toInt()
}
if (!popup.isShowing) { if (!popup.isShowing) {
popup.showAtLocation(cursor_left, Gravity.BOTTOM or Gravity.START, x, y) popup.showAtLocation(text_menu_position, Gravity.TOP or Gravity.START, x, y)
} else { } else {
popup.update(x, y, WRAP_CONTENT, WRAP_CONTENT) popup.update(x, y, WRAP_CONTENT, WRAP_CONTENT)
} }
@ -663,8 +682,10 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
mHandler.removeCallbacks(keepScreenRunnable) mHandler.removeCallbacks(keepScreenRunnable)
textActionMenu?.dismiss() textActionMenu?.dismiss()
page_view.onDestroy() page_view.onDestroy()
if (!BuildConfig.DEBUG) {
SyncBookProgress.uploadBookProgress() SyncBookProgress.uploadBookProgress()
} }
}
override fun observeLiveBus() { override fun observeLiveBus() {
super.observeLiveBus() super.observeLiveBus()

@ -151,9 +151,7 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
*/ */
@RequiresApi(Build.VERSION_CODES.M) @RequiresApi(Build.VERSION_CODES.M)
private fun onInitializeMenu(menu: Menu) { private fun onInitializeMenu(menu: Menu) {
// Start with a menu Item order value that is high enough try {
// so that your "PROCESS_TEXT" menu items appear after the
// standard selection menu items like Cut, Copy, Paste.
var menuItemOrder = 100 var menuItemOrder = 100
for (resolveInfo in getSupportedActivities()) { for (resolveInfo in getSupportedActivities()) {
menu.add( menu.add(
@ -161,6 +159,9 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
menuItemOrder++, resolveInfo.loadLabel(context.packageManager) menuItemOrder++, resolveInfo.loadLabel(context.packageManager)
).intent = createProcessTextIntentForResolveInfo(resolveInfo) ).intent = createProcessTextIntentForResolveInfo(resolveInfo)
} }
} catch (e: Exception) {
context.toast("获取文字操作菜单出错:${e.localizedMessage}")
}
} }
interface CallBack { interface CallBack {

@ -104,6 +104,11 @@ class MoreConfigDialog : DialogFragment() {
} }
PreferKey.keepLight -> postEvent(key, true) PreferKey.keepLight -> postEvent(key, true)
PreferKey.textSelectAble -> postEvent(key, getPrefBoolean(key)) PreferKey.textSelectAble -> postEvent(key, getPrefBoolean(key))
getString(R.string.pk_requested_direction) -> {
activity?.let {
Help.setOrientation(it)
}
}
} }
} }

@ -31,6 +31,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
private val selectStart = arrayOf(0, 0, 0) private val selectStart = arrayOf(0, 0, 0)
private val selectEnd = arrayOf(0, 0, 0) private val selectEnd = arrayOf(0, 0, 0)
private var textPage: TextPage = TextPage() private var textPage: TextPage = TextPage()
//滚动参数 //滚动参数
private val pageFactory: TextPageFactory get() = callBack.pageFactory private val pageFactory: TextPageFactory get() = callBack.pageFactory
private val maxScrollOffset = 100f private val maxScrollOffset = 100f
@ -210,7 +211,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectEnd[0] = 0 selectEnd[0] = 0
selectEnd[1] = lineIndex selectEnd[1] = lineIndex
selectEnd[2] = charIndex selectEnd[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset) upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset) upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(0, lineIndex, charIndex) select(0, lineIndex, charIndex)
return return
@ -236,7 +241,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectEnd[0] = 1 selectEnd[0] = 1
selectEnd[1] = lineIndex selectEnd[1] = lineIndex
selectEnd[2] = charIndex selectEnd[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset) upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset) upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(1, lineIndex, charIndex) select(1, lineIndex, charIndex)
return return
@ -259,7 +268,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectEnd[0] = 2 selectEnd[0] = 2
selectEnd[1] = lineIndex selectEnd[1] = lineIndex
selectEnd[2] = charIndex selectEnd[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset) upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset) upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(2, lineIndex, charIndex) select(2, lineIndex, charIndex)
return return
@ -287,7 +300,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectStart[0] = 0 selectStart[0] = 0
selectStart[1] = lineIndex selectStart[1] = lineIndex
selectStart[2] = charIndex selectStart[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset) upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectChars() upSelectChars()
} }
return return
@ -311,7 +328,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectStart[0] = 1 selectStart[0] = 1
selectStart[1] = lineIndex selectStart[1] = lineIndex
selectStart[2] = charIndex selectStart[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset) upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectChars() upSelectChars()
} }
return return
@ -333,7 +354,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectStart[0] = 2 selectStart[0] = 2
selectStart[1] = lineIndex selectStart[1] = lineIndex
selectStart[2] = charIndex selectStart[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset) upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectChars() upSelectChars()
} }
return return
@ -427,7 +452,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectStart[2] = charIndex selectStart[2] = charIndex
val textLine = relativePage(relativePage).textLines[lineIndex] val textLine = relativePage(relativePage).textLines[lineIndex]
val textChar = textLine.textChars[charIndex] val textChar = textLine.textChars[charIndex]
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset(relativePage)) upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset(relativePage),
textLine.lineTop
)
upSelectChars() upSelectChars()
} }
@ -475,12 +504,12 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
invalidate() invalidate()
} }
private fun upSelectedStart(x: Float, y: Float) { private fun upSelectedStart(x: Float, y: Float, top: Float) = callBack.apply {
callBack.upSelectedStart(x, y + callBack.headerHeight) upSelectedStart(x, y + headerHeight, top + headerHeight)
} }
private fun upSelectedEnd(x: Float, y: Float) { private fun upSelectedEnd(x: Float, y: Float) = callBack.apply {
callBack.upSelectedEnd(x, y + callBack.headerHeight) upSelectedEnd(x, y + headerHeight)
} }
fun cancelSelect() { fun cancelSelect() {
@ -580,7 +609,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
} }
interface CallBack { interface CallBack {
fun upSelectedStart(x: Float, y: Float) fun upSelectedStart(x: Float, y: Float, top: Float)
fun upSelectedEnd(x: Float, y: Float) fun upSelectedEnd(x: Float, y: Float)
fun onCancelSelect() fun onCancelSelect()
val headerHeight: Int val headerHeight: Int

@ -16,25 +16,20 @@ class PageView(context: Context, attrs: AttributeSet) :
FrameLayout(context, attrs), FrameLayout(context, attrs),
DataSource { DataSource {
var callBack: CallBack val callBack: CallBack get() = activity as CallBack
var pageFactory: TextPageFactory var pageFactory: TextPageFactory = TextPageFactory(this)
var pageDelegate: PageDelegate? = null var pageDelegate: PageDelegate? = null
var prevPage: ContentView var prevPage: ContentView = ContentView(context)
var curPage: ContentView var curPage: ContentView = ContentView(context)
var nextPage: ContentView var nextPage: ContentView = ContentView(context)
init { init {
callBack = activity as CallBack
nextPage = ContentView(context)
addView(nextPage) addView(nextPage)
curPage = ContentView(context)
addView(curPage) addView(curPage)
prevPage = ContentView(context)
addView(prevPage) addView(prevPage)
upBg() upBg()
setWillNotDraw(false) setWillNotDraw(false)
pageFactory = TextPageFactory(this)
upPageAnim() upPageAnim()
} }
@ -170,5 +165,6 @@ class PageView(context: Context, attrs: AttributeSet) :
val isInitFinish: Boolean val isInitFinish: Boolean
fun clickCenter() fun clickCenter()
fun screenOffTimerStart() fun screenOffTimerStart()
fun showTextActionMenu()
} }
} }

@ -200,7 +200,11 @@ abstract class PageDelegate(protected val pageView: PageView) :
//GestureDetector.onFling小幅移动不会触发,所以要自己判断 //GestureDetector.onFling小幅移动不会触发,所以要自己判断
when (event.action) { when (event.action) {
MotionEvent.ACTION_UP, MotionEvent.ACTION_UP,
MotionEvent.ACTION_CANCEL -> if (isMoved) { MotionEvent.ACTION_CANCEL -> {
if (isTextSelected) {
pageView.callBack.showTextActionMenu()
}
if (isMoved) {
if (selectedOnDown) { if (selectedOnDown) {
selectedOnDown = false selectedOnDown = false
} }
@ -209,6 +213,7 @@ abstract class PageDelegate(protected val pageView: PageView) :
} }
} }
} }
}
/** /**
* 按下 * 按下

@ -40,7 +40,7 @@ class OtherConfigFragment : PreferenceFragmentCompat(),
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
putPrefBoolean(PreferKey.processText, isProcessTextEnabled()) putPrefBoolean(PreferKey.processText, isProcessTextEnabled())
addPreferencesFromResource(R.xml.pref_config_other) addPreferencesFromResource(R.xml.pref_config_other)
upPreferenceSummary(PreferKey.downloadPath, BookHelp.downloadPath) upPreferenceSummary(getString(R.string.pk_download_path), BookHelp.downloadPath)
upPreferenceSummary(PreferKey.threadCount, AppConfig.threadCount.toString()) upPreferenceSummary(PreferKey.threadCount, AppConfig.threadCount.toString())
upPreferenceSummary(PreferKey.webPort, webPort.toString()) upPreferenceSummary(PreferKey.webPort, webPort.toString())
} }
@ -74,7 +74,7 @@ class OtherConfigFragment : PreferenceFragmentCompat(),
.show { .show {
putPrefInt(PreferKey.webPort, it) putPrefInt(PreferKey.webPort, it)
} }
PreferKey.downloadPath -> selectDownloadPath() getString(R.string.pk_download_path) -> selectDownloadPath()
PreferKey.cleanCache -> { PreferKey.cleanCache -> {
BookHelp.clearCache() BookHelp.clearCache()
toast(R.string.clear_cache_success) toast(R.string.clear_cache_success)
@ -85,7 +85,7 @@ class OtherConfigFragment : PreferenceFragmentCompat(),
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
when (key) { when (key) {
PreferKey.downloadPath -> { getString(R.string.pk_download_path) -> {
upPreferenceSummary(key, BookHelp.downloadPath) upPreferenceSummary(key, BookHelp.downloadPath)
} }
PreferKey.threadCount -> upPreferenceSummary( PreferKey.threadCount -> upPreferenceSummary(
@ -141,13 +141,17 @@ class OtherConfigFragment : PreferenceFragmentCompat(),
private fun selectDownloadPath() { private fun selectDownloadPath() {
FilePicker.selectFolder(this, requestCodeDownloadPath) { FilePicker.selectFolder(this, requestCodeDownloadPath) {
removePref(PreferKey.downloadPath) removePref(getString(R.string.pk_download_path))
} }
} }
private fun putDownloadPath(path: String) {
putPrefString(getString(R.string.pk_download_path), path)
}
override fun onFilePicked(requestCode: Int, currentPath: String) { override fun onFilePicked(requestCode: Int, currentPath: String) {
if (requestCode == requestCodeDownloadPath) { if (requestCode == requestCodeDownloadPath) {
putPrefString(PreferKey.downloadPath, currentPath) putDownloadPath(currentPath)
} }
} }
@ -160,7 +164,7 @@ class OtherConfigFragment : PreferenceFragmentCompat(),
uri, 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
) )
putPrefString(PreferKey.downloadPath, uri.toString()) putDownloadPath(uri.toString())
} }
} }
} }

@ -48,7 +48,7 @@ open class WelcomeActivity : BaseActivity(R.layout.activity_welcome) {
private fun startMainActivity() { private fun startMainActivity() {
startActivity<MainActivity>() startActivity<MainActivity>()
if (getPrefBoolean(getString(R.string.pk_default_read))) { if (getPrefBoolean(R.string.pk_default_read)) {
startActivity<ReadBookActivity>() startActivity<ReadBookActivity>()
} }
finish() finish()

@ -142,12 +142,12 @@ class TitleBar(context: Context, attrs: AttributeSet?) : AppBarLayout(context, a
} }
backgroundColor = context.primaryColor backgroundColor = context.primaryColor
// targetElevation = context.elevation
stateListAnimator = null stateListAnimator = null
if (AppConfig.elevation < 0) { elevation = if (AppConfig.elevation < 0) {
elevation = context.elevation context.elevation
} else { } else {
elevation = AppConfig.elevation.toFloat() AppConfig.elevation.toFloat()
} }
a.recycle() a.recycle()

@ -12,6 +12,7 @@ import android.os.BatteryManager
import android.provider.Settings import android.provider.Settings
import androidx.annotation.ColorRes import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.core.content.edit import androidx.core.content.edit
@ -28,6 +29,9 @@ import java.io.FileOutputStream
fun Context.getPrefBoolean(key: String, defValue: Boolean = false) = fun Context.getPrefBoolean(key: String, defValue: Boolean = false) =
defaultSharedPreferences.getBoolean(key, defValue) defaultSharedPreferences.getBoolean(key, defValue)
fun Context.getPrefBoolean(@StringRes keyId: Int, defValue: Boolean = false) =
defaultSharedPreferences.getBoolean(getString(keyId), defValue)
fun Context.putPrefBoolean(key: String, value: Boolean = false) = fun Context.putPrefBoolean(key: String, value: Boolean = false) =
defaultSharedPreferences.edit { putBoolean(key, value) } defaultSharedPreferences.edit { putBoolean(key, value) }
@ -46,6 +50,9 @@ fun Context.putPrefLong(key: String, value: Long) =
fun Context.getPrefString(key: String, defValue: String? = null) = fun Context.getPrefString(key: String, defValue: String? = null) =
defaultSharedPreferences.getString(key, defValue) defaultSharedPreferences.getString(key, defValue)
fun Context.getPrefString(@StringRes keyId: Int, defValue: String? = null) =
defaultSharedPreferences.getString(getString(keyId), defValue)
fun Context.putPrefString(key: String, value: String) = fun Context.putPrefString(key: String, value: String) =
defaultSharedPreferences.edit { putString(key, value) } defaultSharedPreferences.edit { putString(key, value) }

@ -9,6 +9,12 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" /> android:layout_height="match_parent" />
<View
android:id="@+id/text_menu_position"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="invisible" />
<ImageView <ImageView
android:id="@+id/cursor_left" android:id="@+id/cursor_left"
android:layout_width="wrap_content" android:layout_width="wrap_content"

@ -16,7 +16,7 @@
android:id="@+id/iv_menu_more" android:id="@+id/iv_menu_more"
android:layout_width="24dp" android:layout_width="24dp"
android:layout_height="24dp" android:layout_height="24dp"
android:background="?attr/selectableItemBackground" android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_more_vert" android:src="@drawable/ic_more_vert"
android:tint="@color/tv_text_default" android:tint="@color/tv_text_default"
android:visibility="gone" android:visibility="gone"

@ -38,5 +38,11 @@
<item>9</item> <item>9</item>
</string-array> </string-array>
<string-array name="screen_direction_value">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
</resources> </resources>

@ -67,7 +67,7 @@
<item>@string/app_folder_picker</item> <item>@string/app_folder_picker</item>
</string-array> </string-array>
<string-array name="screen_direction_list_title"> <string-array name="screen_direction_title">
<item>@string/screen_unspecified</item> <item>@string/screen_unspecified</item>
<item>@string/screen_portrait</item> <item>@string/screen_portrait</item>
<item>@string/screen_landscape</item> <item>@string/screen_landscape</item>

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="pk_auto_refresh">auto_refresh</string> <string name="pk_auto_refresh">auto_refresh</string>
<string name="pk_screen_direction">list_screen_direction</string> <string name="pk_requested_direction">list_screen_direction</string>
<string name="pk_full_screen">full_screen</string> <string name="pk_full_screen">full_screen</string>
<string name="pk_threads_num">threads_num</string> <string name="pk_threads_num">threads_num</string>
<string name="pk_user_agent">user_agent</string> <string name="pk_user_agent">user_agent</string>

@ -646,4 +646,5 @@
<string name="bar_elevation">导航栏阴影</string> <string name="bar_elevation">导航栏阴影</string>
<string name="bar_elevation_s">当前阴影大小(elevation): %s</string> <string name="bar_elevation_s">当前阴影大小(elevation): %s</string>
<string name="btn_default_s">默认</string> <string name="btn_default_s">默认</string>
</resources> </resources>

@ -2,6 +2,14 @@
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<io.legado.app.ui.widget.prefs.NameListPreference
android:key="@string/pk_requested_direction"
android:defaultValue="0"
android:title="@string/screen_direction"
android:entries="@array/screen_direction_title"
android:entryValues="@array/screen_direction_value"
app:iconSpaceReserved="false" />
<io.legado.app.ui.widget.prefs.NameListPreference <io.legado.app.ui.widget.prefs.NameListPreference
android:key="keep_light" android:key="keep_light"
android:defaultValue="0" android:defaultValue="0"

Loading…
Cancel
Save