Merge pull request #27 from gedoor/master

up
pull/379/head
口口吕 5 years ago committed by GitHub
commit 44fd594a2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      app/src/main/AndroidManifest.xml
  2. 68
      app/src/main/assets/txtTocRule.json
  3. 8
      app/src/main/assets/updateLog.md
  4. 1
      app/src/main/java/io/legado/app/constant/PreferKey.kt
  5. 10
      app/src/main/java/io/legado/app/help/AppConfig.kt
  6. 8
      app/src/main/java/io/legado/app/help/BookHelp.kt
  7. 12
      app/src/main/java/io/legado/app/lib/theme/ATHUtils.kt
  8. 3
      app/src/main/java/io/legado/app/lib/theme/MaterialValueHelper.kt
  9. 8
      app/src/main/java/io/legado/app/lib/theme/ThemeStore.kt
  10. 2
      app/src/main/java/io/legado/app/lib/theme/ThemeStorePrefKeys.kt
  11. 14
      app/src/main/java/io/legado/app/model/localBook/LocalBook.kt
  12. 4
      app/src/main/java/io/legado/app/service/TTSReadAloudService.kt
  13. 4
      app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
  14. 13
      app/src/main/java/io/legado/app/ui/book/read/Help.kt
  15. 45
      app/src/main/java/io/legado/app/ui/book/read/ReadBookActivity.kt
  16. 7
      app/src/main/java/io/legado/app/ui/book/read/TextActionMenu.kt
  17. 5
      app/src/main/java/io/legado/app/ui/book/read/config/MoreConfigDialog.kt
  18. 1
      app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt
  19. 53
      app/src/main/java/io/legado/app/ui/book/read/page/ContentTextView.kt
  20. 1
      app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
  21. 16
      app/src/main/java/io/legado/app/ui/book/read/page/PageView.kt
  22. 7
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt
  23. 16
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
  24. 16
      app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt
  25. 28
      app/src/main/java/io/legado/app/ui/config/ThemeConfigFragment.kt
  26. 2
      app/src/main/java/io/legado/app/ui/welcome/WelcomeActivity.kt
  27. 68
      app/src/main/java/io/legado/app/ui/widget/BatteryView.kt
  28. 9
      app/src/main/java/io/legado/app/ui/widget/TitleBar.kt
  29. 11
      app/src/main/java/io/legado/app/ui/widget/number/NumberPickerDialog.kt
  30. 7
      app/src/main/java/io/legado/app/utils/ContextExtensions.kt
  31. 419
      app/src/main/res/layout-land/activity_book_info.xml
  32. 172
      app/src/main/res/layout/activity_book_info.xml
  33. 6
      app/src/main/res/layout/activity_book_read.xml
  34. 2
      app/src/main/res/layout/popup_action_menu.xml
  35. 3
      app/src/main/res/layout/view_book_page.xml
  36. 6
      app/src/main/res/values/array_values.xml
  37. 2
      app/src/main/res/values/arrays.xml
  38. 2
      app/src/main/res/values/pref_key_value.xml
  39. 6
      app/src/main/res/values/strings.xml
  40. 8
      app/src/main/res/xml/pref_config_read.xml
  41. 7
      app/src/main/res/xml/pref_config_theme.xml

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

@ -2,73 +2,97 @@
{
"enable": true,
"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
},
{
"enable": false,
"name": "目录(不匹配行前空白)",
"rule": "^(?<= |\\s)(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
"name": "目录(空白)",
"rule": "(?<=[ \\s])(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|篇(?!张))).{0,30}$",
"serialNumber": 1
},
{
"enable": false,
"name": "目录(去简介)",
"rule": "^(?<= |\\s)(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$",
"rule": "(?<=[ \\s])(?:前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$",
"serialNumber": 2
},
{
"enable": false,
"name": "目录(古典、轻小说备用)",
"rule": "^[  \\t]{0,4}(?:前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$",
"rule": "^[  \\t]{0,4}(?:前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|第?\\s{0,4}[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]+?\\s{0,4}(?:章|节(?!课)|卷|集(?![合和])|部(?!分)|回(?![合来事去])|场(?![和合比电是])|篇(?!张))).{0,30}$",
"serialNumber": 3
},
{
"enable": false,
"name": "数字(纯数字标题)",
"rule": "(?<=[ \\s])\\d+[  \\t]{0,4}$",
"serialNumber": 4
},
{
"enable": true,
"name": "数字 分隔符 标题名称",
"rule": "^[  \\t]{0,4}\\d{1,5}[\\,\\., 、\\-].{1,30}$",
"serialNumber": 4
"serialNumber": 5
},
{
"enable": true,
"name": "正文 标题/序号",
"rule": "^[  \\t]{0,4}正文\\s{1,4}.{0,20}$",
"serialNumber": 5
"rule": "^[  \\t]{0,4}正文[  ]{1,4}.{0,20}$",
"serialNumber": 6
},
{
"enable": true,
"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}$",
"serialNumber": 6
"serialNumber": 8
},
{
"enable": true,
"name": "特殊符号 序号 标题",
"rule": "^[  \\t]{0,4}[〈〖〔【][第卷][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10}[章节][\\.:: \f\t].{0,30}$",
"serialNumber": 7
"rule": "(?<=[\\s ]{0,4}).{1,3}(?:第|卷|[Cc]hapter)[\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,10}[章节]?[\\.:: \f\t].{0,20}$",
"serialNumber": 9
},
{
"enable": true,
"name": "特殊符号 标题",
"rule": "^[  \\t]{0,4}[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$",
"serialNumber": 8
"enable": false,
"name": "特殊符号 标题(成对)",
"rule": "(?<=[\\s ]{0,4})(?:[\\[〈「『〖〔《(【\\(].{1,30}[\\)】)》〕〗』」〉\\]]?|(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外)[  ]{0,4}$",
"serialNumber": 10
},
{
"enable":false,
"name": "特殊符号 标题(不匹配空白字符)",
"rule": "^(?<= |\\s)[\\[〈「『〖〔《(【\\(☆★].{1,30}[\\)】)》〕〗』」〉\\]]?\\s{0,4}$",
"serialNumber": 9
"enable":true,
"name": "特殊符号 标题(单个)",
"rule": "(?<=[\\s ]{0,4})(?:[☆★✦✧].{1,30}|(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外)[  ]{0,4}$",
"serialNumber": 11
},
{
"enable": true,
"name": "章/卷 序号 标题",
"rule": "^[  \\t]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完)|终章|后记|尾声|番外|[卷章][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,8})\\s{0,4}.{0,30}$",
"serialNumber": 10
"rule": "^[ \\t ]{0,4}(?:(?:内容|文章)?简介|文案|前言|序章|楔子|正文(?!完|结)|终章|后记|尾声|番外|[卷章][\\d零一二两三四五六七八九十百千万壹贰叁肆伍陆柒捌玖拾佰仟]{1,8})[  ]{0,4}.{0,30}$",
"serialNumber": 12
},
{
"enable":false,
"name": "顶格标题",
"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,6 +2,14 @@
* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
* 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。
**2020/03/10**
* 优化文字选择菜单弹出位置
* 添加屏幕方向控制
**2020/03/09**
* 底部文字对齐
* 主题添加阴影调节 by yangyxd
**2020/03/08**
* 订阅长按保存图片
* 订阅全屏播放

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

@ -32,6 +32,9 @@ object AppConfig {
App.INSTANCE.putPrefBoolean("transparentStatusBar", value)
}
val requestedDirection: String?
get() = App.INSTANCE.getPrefString(R.string.pk_requested_direction)
var backupPath: String?
get() = App.INSTANCE.getPrefString(PreferKey.backupPath)
set(value) {
@ -49,7 +52,7 @@ object AppConfig {
}
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
get() = App.INSTANCE.getPrefInt(PreferKey.threadCount, 16)
@ -111,4 +114,9 @@ object AppConfig {
App.INSTANCE.putPrefBoolean("bookGroupAudio", value)
}
var elevation: Int
get() = App.INSTANCE.getPrefInt("elevation", -1)
set(value) {
App.INSTANCE.putPrefInt("elevation", value)
}
}

@ -4,8 +4,8 @@ import android.net.Uri
import androidx.documentfile.provider.DocumentFile
import com.github.houbb.opencc4j.core.impl.ZhConvertBootstrap
import io.legado.app.App
import io.legado.app.R
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.BookChapter
import io.legado.app.data.entities.ReplaceRule
@ -21,7 +21,7 @@ import kotlin.math.min
object BookHelp {
private const val cacheFolderName = "book_cache"
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.cacheDir.absolutePath
@ -300,6 +300,8 @@ object BookHelp {
1 -> c = ZhConvertBootstrap.newInstance().toSimple(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(), "") //移除尾部空行
}
}

@ -19,4 +19,16 @@ object ATHUtils {
a.recycle()
}
}
@JvmOverloads
fun resolveFloat(context: Context, @AttrRes attr: Int, fallback: Float = 0.0f): Float {
val a = context.theme.obtainStyledAttributes(intArrayOf(attr))
return try {
a.getFloat(0, fallback)
} catch (e: Exception) {
fallback
} finally {
a.recycle()
}
}
}

@ -108,3 +108,6 @@ val Context.isDarkTheme: Boolean
val Fragment.isDarkTheme: Boolean
get() = requireContext().isDarkTheme
val Context.elevation: Float
get() = ThemeStore.elevation(this)

@ -284,6 +284,14 @@ private constructor(private val mContext: Context) : ThemeStoreInterface {
)
}
@CheckResult
fun elevation(context: Context): Float {
return prefs(context).getFloat(
ThemeStorePrefKeys.KEY_ELEVATION,
ATHUtils.resolveFloat(context, android.R.attr.elevation, context.resources.getDimension(R.dimen.design_appbar_elevation))
)
}
@CheckResult
@ColorInt
fun bottomBackground(context: Context): Int {

@ -27,4 +27,6 @@ object ThemeStorePrefKeys {
const val KEY_APPLY_PRIMARYDARK_STATUSBAR = "apply_primarydark_statusbar"
const val KEY_APPLY_PRIMARY_NAVBAR = "apply_primary_navbar"
const val KEY_AUTO_GENERATE_PRIMARYDARK = "auto_generate_primarydark"
const val KEY_ELEVATION = "elevation"
}

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

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

@ -207,7 +207,7 @@ class BookInfoActivity :
ChangeSourceDialog.show(supportFragmentManager, it.name, it.author)
}
}
tv_toc.onClick {
tv_toc_view.onClick {
if (!viewModel.inBookshelf) {
viewModel.saveBook {
viewModel.saveChapterList {
@ -218,7 +218,7 @@ class BookInfoActivity :
openChapterList()
}
}
tv_group.onClick {
tv_change_group.onClick {
viewModel.bookData.value?.let {
GroupSelectDialog.show(supportFragmentManager, it.group)
}

@ -3,6 +3,7 @@ package io.legado.app.ui.book.read
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.pm.ActivityInfo
import android.os.AsyncTask
import android.os.Build
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是否存在
* 该方法需要在View完全被绘制出来之后调用否则判断不了

@ -3,6 +3,7 @@ package io.legado.app.ui.book.read
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Bundle
import android.os.Handler
@ -13,6 +14,7 @@ import androidx.core.view.isVisible
import androidx.core.view.size
import androidx.lifecycle.Observer
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener
import io.legado.app.BuildConfig
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
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 headerHeight: Int get() = page_view.curPage.headerHeight
override fun onCreate(savedInstanceState: Bundle?) {
Help.setOrientation(this)
super.onCreate(savedInstanceState)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
Help.upLayoutInDisplayCutoutMode(window)
initView()
@ -106,6 +113,11 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
upSystemUiVisibility()
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
ReadBook.loadContent()
}
override fun onResume() {
super.onResume()
upSystemUiVisibility()
@ -338,7 +350,8 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
@SuppressLint("ClickableViewAccessibility")
override fun onTouch(v: View, event: MotionEvent): Boolean {
when (event.action) {
MotionEvent.ACTION_MOVE, MotionEvent.ACTION_UP -> {
MotionEvent.ACTION_DOWN -> textActionMenu?.dismiss()
MotionEvent.ACTION_MOVE -> {
when (v.id) {
R.id.cursor_left -> page_view.curPage.selectStartMove(
event.rawX + cursor_left.width,
@ -350,6 +363,7 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
)
}
}
MotionEvent.ACTION_UP -> showTextActionMenu()
}
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.y = y
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.y = y
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 = 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 ->
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) {
popup.showAtLocation(cursor_left, Gravity.BOTTOM or Gravity.START, x, y)
popup.showAtLocation(text_menu_position, Gravity.TOP or Gravity.START, x, y)
} else {
popup.update(x, y, WRAP_CONTENT, WRAP_CONTENT)
}
@ -663,8 +682,10 @@ class ReadBookActivity : VMBaseActivity<ReadBookViewModel>(R.layout.activity_boo
mHandler.removeCallbacks(keepScreenRunnable)
textActionMenu?.dismiss()
page_view.onDestroy()
if (!BuildConfig.DEBUG) {
SyncBookProgress.uploadBookProgress()
}
}
override fun observeLiveBus() {
super.observeLiveBus()

@ -151,9 +151,7 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
*/
@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.
try {
var menuItemOrder = 100
for (resolveInfo in getSupportedActivities()) {
menu.add(
@ -161,6 +159,9 @@ class TextActionMenu(private val context: Context, private val callBack: CallBac
menuItemOrder++, resolveInfo.loadLabel(context.packageManager)
).intent = createProcessTextIntentForResolveInfo(resolveInfo)
}
} catch (e: Exception) {
context.toast("获取文字操作菜单出错:${e.localizedMessage}")
}
}
interface CallBack {

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

@ -75,6 +75,7 @@ object ChapterProvider {
item.chapterIndex = bookChapter.index
item.chapterSize = chapterSize
item.title = bookChapter.title
item.upLinesPosition(visibleHeight)
}
return TextChapter(
bookChapter.index,

@ -31,6 +31,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
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
@ -210,7 +211,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectEnd[0] = 0
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(0, lineIndex, charIndex)
return
@ -236,7 +241,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectEnd[0] = 1
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(1, lineIndex, charIndex)
return
@ -259,7 +268,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectEnd[0] = 2
selectEnd[1] = lineIndex
selectEnd[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectedEnd(textChar.end, textLine.lineBottom + relativeOffset)
select(2, lineIndex, charIndex)
return
@ -287,7 +300,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectStart[0] = 0
selectStart[1] = lineIndex
selectStart[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectChars()
}
return
@ -311,7 +328,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectStart[0] = 1
selectStart[1] = lineIndex
selectStart[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectChars()
}
return
@ -333,7 +354,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectStart[0] = 2
selectStart[1] = lineIndex
selectStart[2] = charIndex
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset)
upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset,
textLine.lineTop + relativeOffset
)
upSelectChars()
}
return
@ -427,7 +452,11 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
selectStart[2] = charIndex
val textLine = relativePage(relativePage).textLines[lineIndex]
val textChar = textLine.textChars[charIndex]
upSelectedStart(textChar.start, textLine.lineBottom + relativeOffset(relativePage))
upSelectedStart(
textChar.start,
textLine.lineBottom + relativeOffset(relativePage),
textLine.lineTop
)
upSelectChars()
}
@ -475,12 +504,12 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
invalidate()
}
private fun upSelectedStart(x: Float, y: Float) {
callBack.upSelectedStart(x, y + callBack.headerHeight)
private fun upSelectedStart(x: Float, y: Float, top: Float) = callBack.apply {
upSelectedStart(x, y + headerHeight, top + headerHeight)
}
private fun upSelectedEnd(x: Float, y: Float) {
callBack.upSelectedEnd(x, y + callBack.headerHeight)
private fun upSelectedEnd(x: Float, y: Float) = callBack.apply {
upSelectedEnd(x, y + headerHeight)
}
fun cancelSelect() {
@ -580,7 +609,7 @@ class ContentTextView(context: Context, attrs: AttributeSet?) : View(context, at
}
interface CallBack {
fun upSelectedStart(x: Float, y: Float)
fun upSelectedStart(x: Float, y: Float, top: Float)
fun upSelectedEnd(x: Float, y: Float)
fun onCancelSelect()
val headerHeight: Int

@ -109,7 +109,6 @@ class ContentView(context: Context) : FrameLayout(context) {
fun setContent(textPage: TextPage) {
setProgress(textPage)
content_text_view.resetPageOffset()
content_text_view.setContent(textPage)
}

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

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

@ -18,6 +18,21 @@ data class TextPage(
var height: Float = 0f
) {
fun upLinesPosition(visibleHeight: Int) {
if (textLines.size <= 1) return
if (visibleHeight - height > with(textLines.last()) { lineBottom - lineTop }) return
val surplus = (visibleHeight - textLines.last().lineBottom)
if (surplus == 0f) return
height += surplus
val tj = surplus / (textLines.size - 1)
for (i in 1 until textLines.size) {
val line = textLines[i]
line.lineTop = line.lineTop + tj * i
line.lineBase = line.lineBase + tj * i
line.lineBottom = line.lineBottom + tj * i
}
}
@Suppress("DEPRECATION")
fun format(): TextPage {
if (textLines.isEmpty() && ChapterProvider.visibleWidth > 0) {
@ -101,4 +116,5 @@ data class TextPage(
}
return percent
}
}

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

@ -1,6 +1,7 @@
package io.legado.app.ui.config
import android.content.SharedPreferences
import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.preference.Preference
@ -16,6 +17,8 @@ import io.legado.app.lib.dialogs.noButton
import io.legado.app.lib.dialogs.yesButton
import io.legado.app.lib.theme.ATH
import io.legado.app.lib.theme.ColorUtils
import io.legado.app.ui.widget.number.NumberPickerDialog
import io.legado.app.ui.widget.prefs.IconListPreference
import io.legado.app.utils.*
@ -25,6 +28,12 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.pref_config_theme)
if (Build.VERSION.SDK_INT < 26) {
findPreference<IconListPreference>(PreferKey.launcherIcon)?.let {
preferenceScreen.removePreference(it)
}
}
upPreferenceSummary("barElevation", AppConfig.elevation.toString())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -145,6 +154,19 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar
recreateActivities()
}
}.show().applyTint()
"barElevation" -> NumberPickerDialog(requireContext())
.setTitle(getString(R.string.bar_elevation))
.setMaxValue(32)
.setMinValue(0)
.setValue(AppConfig.elevation)
.setCustomButton((R.string.btn_default_s)) {
AppConfig.elevation = App.INSTANCE.resources.getDimension(R.dimen.design_appbar_elevation).toInt()
recreateActivities()
}
.show {
AppConfig.elevation = it
recreateActivities()
}
}
return super.onPreferenceTreeClick(preference)
}
@ -180,4 +202,10 @@ class ThemeConfigFragment : PreferenceFragmentCompat(), SharedPreferences.OnShar
postEvent(EventBus.RECREATE, "")
}
private fun upPreferenceSummary(preferenceKey: String, value: String?) {
val preference = findPreference<Preference>(preferenceKey) ?: return
when (preferenceKey) {
"barElevation" -> preference.summary = getString(R.string.bar_elevation_s, value)
}
}
}

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

@ -1,67 +1,57 @@
package io.legado.app.ui.widget
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.Typeface
import android.text.StaticLayout
import android.text.TextPaint
import android.util.AttributeSet
import android.view.View
import androidx.annotation.ColorInt
import io.legado.app.R
import androidx.appcompat.widget.AppCompatTextView
import io.legado.app.utils.dp
import io.legado.app.utils.getCompatColor
import io.legado.app.utils.sp
class BatteryView(context: Context, attrs: AttributeSet?) : View(context, attrs) {
private var battery = 100
private val textPaint = TextPaint()
private var batteryHeight: Int = 0
private var batteryWidth: Int = 0
class BatteryView(context: Context, attrs: AttributeSet?) : AppCompatTextView(context, attrs) {
private val batteryPaint = Paint()
private val outFrame = Rect()
private val polar = Rect()
init {
textPaint.textSize = 10.sp.toFloat()
textPaint.strokeWidth = 1.dp.toFloat()
textPaint.isAntiAlias = true
textPaint.textAlign = Paint.Align.CENTER
textPaint.color = context.getCompatColor(R.color.tv_text_default)
textPaint.typeface = Typeface.createFromAsset(context.assets, "number.ttf")
batteryHeight = with(textPaint.fontMetrics) { descent - ascent + leading }.toInt()
batteryWidth = StaticLayout.getDesiredWidth("100", textPaint).toInt() + 10.dp
outFrame.set(1.dp, 1.dp, batteryWidth - 3.dp, batteryHeight - 1.dp)
polar.set(outFrame.right, batteryHeight / 3, batteryWidth, batteryHeight * 2 / 3)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(
MeasureSpec.makeMeasureSpec(batteryWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(batteryHeight, MeasureSpec.EXACTLY)
)
setPadding(4.dp, 0, 6.dp, 0)
batteryPaint.strokeWidth = 1.dp.toFloat()
batteryPaint.isAntiAlias = true
batteryPaint.color = paint.color
}
fun setColor(@ColorInt color: Int) {
textPaint.color = color
setTextColor(color)
batteryPaint.color = color
invalidate()
}
@SuppressLint("SetTextI18n")
fun setBattery(battery: Int) {
this.battery = battery
invalidate()
text = "$battery"
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
textPaint.style = Paint.Style.STROKE
canvas.drawRect(outFrame, textPaint)
textPaint.style = Paint.Style.FILL
canvas.drawRect(polar, textPaint)
val text = battery.toString()
val baseHeight = batteryHeight - textPaint.fontMetrics.descent
canvas.drawText(text, outFrame.right / 2.toFloat(), baseHeight, textPaint)
outFrame.set(
1.dp,
layout.getLineTop(0) + 2.dp,
width - 3.dp,
layout.getLineBottom(0) - 2.dp
)
val dj = (outFrame.bottom - outFrame.top) / 3
polar.set(
outFrame.right,
outFrame.top + dj,
width - 1.dp,
outFrame.bottom - dj
)
batteryPaint.style = Paint.Style.STROKE
canvas.drawRect(outFrame, batteryPaint)
batteryPaint.style = Paint.Style.FILL
canvas.drawRect(polar, batteryPaint)
}
}

@ -10,6 +10,8 @@ import androidx.annotation.StyleRes
import androidx.appcompat.widget.Toolbar
import com.google.android.material.appbar.AppBarLayout
import io.legado.app.R
import io.legado.app.help.AppConfig
import io.legado.app.lib.theme.elevation
import io.legado.app.lib.theme.primaryColor
import io.legado.app.utils.activity
import io.legado.app.utils.navigationBarHeight
@ -141,6 +143,13 @@ class TitleBar(context: Context, attrs: AttributeSet?) : AppBarLayout(context, a
backgroundColor = context.primaryColor
stateListAnimator = null
elevation = if (AppConfig.elevation < 0) {
context.elevation
} else {
AppConfig.elevation.toFloat()
}
a.recycle()
}

@ -40,6 +40,17 @@ class NumberPickerDialog(context: Context) {
return this
}
fun setCustomButton(textId: Int, listener: (() -> Unit)?): NumberPickerDialog {
builder.setNeutralButton(textId) { _, _ ->
numberPicker?.let {
it.clearFocus()
it.hideSoftInput()
listener?.invoke()
}
}
return this;
}
fun show(callBack: ((value: Int) -> Unit)?) {
builder.setPositiveButton(R.string.ok) { _, _ ->
numberPicker?.let {

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

@ -0,0 +1,419 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/bg_book"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:contentDescription="@string/bg_image"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="?attr/actionBarStyle"
android:fitsSystemWindows="true"
app:popupTheme="@style/AppTheme.PopupOverlay"
app:layout_constraintTop_toTopOf="parent"
app:title="@string/book_info" />
<LinearLayout
android:id="@+id/vw_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#50000000"
android:orientation="horizontal"
android:fitsSystemWindows="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1.0"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="68dp">
<io.legado.app.ui.widget.ArcView
android:layout_width="match_parent"
android:layout_height="78dp"
android:layout_marginTop="120dp"
app:arcDirectionTop="true"
app:arcHeight="36dp"
app:bgColor="@color/background" />
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_margin="3dp"
app:cardCornerRadius="5dp"
app:cardElevation="8dp">
<io.legado.app.ui.widget.image.CoverImageView
android:id="@+id/iv_cover"
android:layout_width="130dp"
android:layout_height="200dp"
android:contentDescription="@string/img_cover"
android:scaleType="centerCrop"
android:src="@drawable/image_cover_default" />
</androidx.cardview.widget.CardView>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background"
android:orientation="vertical"
android:paddingLeft="10dp"
android:paddingTop="8dp"
android:paddingRight="16dp"
android:paddingBottom="3dp">
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:gravity="center"
android:includeFontPadding="false"
android:singleLine="true"
android:text="@string/book_name"
android:textColor="@color/tv_text_default"
android:textSize="18sp"
tools:ignore="RtlHardcoded" />
<io.legado.app.ui.widget.LabelsBar
android:id="@+id/lb_kind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="6dp"
android:gravity="center"
android:visibility="gone" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:layout_gravity="center"
android:orientation="horizontal"
android:paddingTop="3dp"
android:paddingBottom="3dp">
<ImageView
android:layout_width="18sp"
android:layout_height="18sp"
android:contentDescription="@string/origin_format"
android:paddingRight="4dp"
android:src="@drawable/ic_author"
app:tint="@color/tv_text_summary"
tools:ignore="RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/tv_author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:includeFontPadding="false"
android:singleLine="true"
android:text="@string/author"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
tools:ignore="NestedWeights" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_weight="1.0"
android:orientation="vertical"
android:layout_marginTop="65dp"
android:layout_marginLeft="1px"
android:fitsSystemWindows="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@color/background"
android:fillViewport="true"
android:fitsSystemWindows="true"
android:focusable="true"
android:padding="0dp"
android:scrollbarStyle="outsideInset"
android:scrollbars="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="16dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingBottom="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/background"
android:orientation="vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingBottom="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="3dp"
android:paddingBottom="3dp">
<ImageView
android:id="@+id/iv_web"
android:layout_width="18sp"
android:layout_height="18sp"
android:contentDescription="@string/origin_format"
android:paddingRight="2dp"
android:src="@drawable/ic_web_outline"
app:tint="@color/tv_text_summary"
tools:ignore="RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/tv_origin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:includeFontPadding="false"
android:paddingRight="6dp"
android:singleLine="true"
android:text="@string/origin_format"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
tools:ignore="NestedWeights" />
<io.legado.app.ui.widget.text.AccentBgTextView
android:id="@+id/tv_change_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:text="@string/change_origin"
android:textSize="13sp"
app:radius="2dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="3dp"
android:paddingBottom="3dp">
<ImageView
android:id="@+id/ic_book_last"
android:layout_width="18sp"
android:layout_height="18sp"
android:contentDescription="@string/read_dur_progress"
android:paddingRight="2dp"
android:src="@drawable/ic_book_last"
app:tint="@color/tv_text_summary"
tools:ignore="RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/tv_lasted"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:includeFontPadding="false"
android:paddingRight="6dp"
android:singleLine="true"
android:text="@string/read_dur_progress"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
tools:ignore="NestedWeights" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="3dp"
android:paddingBottom="3dp">
<ImageView
android:layout_width="18sp"
android:layout_height="18sp"
android:contentDescription="@string/read_dur_progress"
android:paddingRight="2dp"
android:src="@drawable/ic_groups"
app:tint="@color/tv_text_summary"
tools:ignore="RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/tv_group"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:includeFontPadding="false"
android:paddingRight="6dp"
android:singleLine="true"
android:text="@string/group_s"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
tools:ignore="NestedWeights" />
<io.legado.app.ui.widget.text.AccentBgTextView
android:id="@+id/tv_change_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:text="@string/change_group"
android:textSize="13sp"
app:radius="2dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="3dp"
android:paddingBottom="3dp">
<ImageView
android:layout_width="18sp"
android:layout_height="18sp"
android:contentDescription="@string/read_dur_progress"
android:paddingRight="2dp"
android:src="@drawable/ic_folder_open"
app:tint="@color/tv_text_summary"
tools:ignore="RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/tv_toc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:includeFontPadding="false"
android:paddingRight="6dp"
android:singleLine="true"
android:text="@string/toc_s"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
tools:ignore="NestedWeights" />
<io.legado.app.ui.widget.text.AccentBgTextView
android:id="@+id/tv_toc_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:text="@string/view_toc"
android:textSize="13sp"
app:radius="2dp" />
</LinearLayout>
</LinearLayout>
<io.legado.app.ui.widget.text.ScrollTextView
android:id="@+id/tv_intro"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:clickable="true"
android:focusable="true"
android:paddingLeft="8dp"
android:paddingBottom="8dp"
android:text="@string/book_intro"
android:textColor="@color/tv_text_secondary"
android:textSize="14sp"
android:visibility="visible" />
</LinearLayout>
</ScrollView>
<View
android:layout_marginBottom="1px"
android:layout_width="match_parent"
android:layout_height="8dp"
android:background="@color/background"></View>
<LinearLayout
android:id="@+id/fl_action"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@color/background_menu"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent">
<TextView
android:id="@+id/tv_shelf"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="center"
android:includeFontPadding="false"
android:text="@string/remove_from_bookshelf"
android:textColor="@color/tv_text_default"
android:textSize="15sp" />
<io.legado.app.ui.widget.text.AccentBgTextView
android:id="@+id/tv_read"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/selector_btn_accent_bg"
android:gravity="center"
android:includeFontPadding="false"
android:text="@string/reading"
android:textColor="@color/tv_text_button_nor"
android:textSize="15sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -64,6 +64,27 @@
</RelativeLayout>
<ScrollView
android:background="@color/background"
android:layout_weight="1.0"
android:padding="0dp"
android:layout_height="0dp"
android:layout_width="match_parent"
android:scrollbarStyle="outsideInset"
android:scrollbars="vertical"
android:fillViewport="true"
android:fitsSystemWindows="false"
android:focusable="true" >
<LinearLayout
android:orientation="vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingBottom="8dp"
android:background="@color/background"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -91,34 +112,13 @@
android:id="@+id/lb_kind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:layout_marginBottom="6dp"
android:layout_gravity="center"
android:gravity="center"
android:visibility="gone" />
</LinearLayout>
<ScrollView
android:background="@color/background"
android:layout_weight="1.0"
android:padding="0dp"
android:layout_height="0dp"
android:layout_width="match_parent"
android:scrollbarStyle="outsideInset"
android:scrollbars="vertical"
android:fillViewport="true"
android:fitsSystemWindows="false"
android:focusable="true" >
<LinearLayout
android:orientation="vertical"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingBottom="8dp"
android:background="@color/background"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:paddingLeft="8dp"
@ -202,7 +202,7 @@
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:text="@string/change_origin"
android:textSize="16sp"
android:textSize="13sp"
app:radius="2dp" />
</LinearLayout>
@ -241,6 +241,94 @@
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="3dp"
android:paddingBottom="3dp">
<ImageView
android:layout_width="18sp"
android:layout_height="18sp"
android:contentDescription="@string/read_dur_progress"
android:paddingRight="2dp"
android:src="@drawable/ic_groups"
app:tint="@color/tv_text_summary"
tools:ignore="RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/tv_group"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:includeFontPadding="false"
android:paddingRight="6dp"
android:singleLine="true"
android:text="@string/group_s"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
tools:ignore="NestedWeights" />
<io.legado.app.ui.widget.text.AccentBgTextView
android:id="@+id/tv_change_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:text="@string/change_group"
android:textSize="13sp"
app:radius="2dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="3dp"
android:paddingBottom="3dp">
<ImageView
android:layout_width="18sp"
android:layout_height="18sp"
android:contentDescription="@string/read_dur_progress"
android:paddingRight="2dp"
android:src="@drawable/ic_folder_open"
app:tint="@color/tv_text_summary"
tools:ignore="RtlHardcoded,RtlSymmetry" />
<TextView
android:id="@+id/tv_toc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:includeFontPadding="false"
android:paddingRight="6dp"
android:singleLine="true"
android:text="@string/toc_s"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
tools:ignore="NestedWeights" />
<io.legado.app.ui.widget.text.AccentBgTextView
android:id="@+id/tv_toc_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:text="@string/view_toc"
android:textSize="13sp"
app:radius="2dp" />
</LinearLayout>
</LinearLayout>
<io.legado.app.ui.widget.text.ScrollTextView
@ -254,47 +342,17 @@
android:textColor="@color/tv_text_secondary"
android:textSize="14sp"
android:paddingLeft="8dp"
android:paddingBottom="8dp"
android:visibility="visible" />
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="@+id/view_other"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/background"
android:orientation="vertical"
android:paddingTop="12dp"
android:paddingBottom="12dp"
app:layout_constraintBottom_toTopOf="@+id/fl_action"
app:layout_constraintTop_toBottomOf="@+id/view_info">
<TextView
android:id="@+id/tv_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:singleLine="true"
android:clickable="true"
android:text="@string/group_s"
android:textColor="@color/tv_text_default"
android:textSize="16sp" />
<TextView
android:id="@+id/tv_toc"
<View
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:background="?attr/selectableItemBackground"
android:singleLine="true"
android:clickable="true"
android:text="@string/toc_s"
android:textColor="@color/tv_text_default"
android:textSize="16sp" />
</LinearLayout>
android:layout_height="8dp"
android:background="@color/background"></View>
<View
android:layout_width="match_parent"

@ -9,6 +9,12 @@
android:layout_width="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
android:id="@+id/cursor_left"
android:layout_width="wrap_content"

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

@ -78,7 +78,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
tools:ignore="RtlHardcoded,RtlSymmetry" />
android:textSize="10sp"
tools:ignore="RtlHardcoded,RtlSymmetry,SmallSp" />
</LinearLayout>

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

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

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<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_threads_num">threads_num</string>
<string name="pk_user_agent">user_agent</string>

@ -641,4 +641,10 @@
<string name="add_to_group">加入分组</string>
<string name="save_image">保存图片</string>
<string name="no_default_path">没有默认路径</string>
<string name="change_group">设置分组</string>
<string name="view_toc">查看目录</string>
<string name="bar_elevation">导航栏阴影</string>
<string name="bar_elevation_s">当前阴影大小(elevation): %s</string>
<string name="btn_default_s">默认</string>
</resources>

@ -2,6 +2,14 @@
<androidx.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
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
android:key="keep_light"
android:defaultValue="0"

@ -26,6 +26,13 @@
android:title="@string/navigation_bar_color_change"
app:iconSpaceReserved="false" />
<androidx.preference.Preference
android:defaultValue="true"
android:key="barElevation"
android:summary="@string/bar_elevation_s"
android:title="@string/bar_elevation"
app:iconSpaceReserved="false" />
<androidx.preference.Preference
android:key="defaultTheme"
android:summary="@string/restore_default_theme"

Loading…
Cancel
Save