Merge pull request #5 from gedoor/master

merge
pull/134/head
Celeter 5 years ago committed by GitHub
commit d3ffa2d93d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 9
      app/src/main/AndroidManifest.xml
  2. 8
      app/src/main/assets/updateLog.md
  3. 1
      app/src/main/java/io/legado/app/constant/PreferKey.kt
  4. 3
      app/src/main/java/io/legado/app/data/dao/TxtTocRuleDao.kt
  5. 19
      app/src/main/java/io/legado/app/data/entities/ReplaceRule.kt
  6. 13
      app/src/main/java/io/legado/app/help/BlurTransformation.kt
  7. 30
      app/src/main/java/io/legado/app/help/ReadBookConfig.kt
  8. 6
      app/src/main/java/io/legado/app/help/storage/ImportOldData.kt
  9. 6
      app/src/main/java/io/legado/app/service/WebService.kt
  10. 8
      app/src/main/java/io/legado/app/ui/audio/AudioPlayActivity.kt
  11. 63
      app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverDialog.kt
  12. 53
      app/src/main/java/io/legado/app/ui/book/changecover/ChangeCoverViewModel.kt
  13. 52
      app/src/main/java/io/legado/app/ui/book/changecover/DiffCallBack.kt
  14. 17
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceDialog.kt
  15. 13
      app/src/main/java/io/legado/app/ui/book/changesource/ChangeSourceViewModel.kt
  16. 3
      app/src/main/java/io/legado/app/ui/book/info/BookInfoActivity.kt
  17. 19
      app/src/main/java/io/legado/app/ui/book/read/config/PaddingConfigDialog.kt
  18. 7
      app/src/main/java/io/legado/app/ui/book/read/config/ReadAloudDialog.kt
  19. 18
      app/src/main/java/io/legado/app/ui/book/read/config/ReadStyleDialog.kt
  20. 71
      app/src/main/java/io/legado/app/ui/book/read/config/TocRegexDialog.kt
  21. 48
      app/src/main/java/io/legado/app/ui/book/read/config/TocRegexViewModel.kt
  22. 96
      app/src/main/java/io/legado/app/ui/book/read/page/ChapterProvider.kt
  23. 1
      app/src/main/java/io/legado/app/ui/book/read/page/ContentView.kt
  24. 6
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/CoverPageDelegate.kt
  25. 18
      app/src/main/java/io/legado/app/ui/book/read/page/delegate/PageDelegate.kt
  26. 10
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextLine.kt
  27. 5
      app/src/main/java/io/legado/app/ui/book/read/page/entities/TextPage.kt
  28. 3
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  29. 22
      app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt
  30. 4
      app/src/main/java/io/legado/app/ui/login/SourceLogin.kt
  31. 3
      app/src/main/java/io/legado/app/ui/replacerule/ReplaceRuleActivity.kt
  32. 13
      app/src/main/java/io/legado/app/ui/replacerule/edit/ReplaceEditDialog.kt
  33. 5
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt
  34. 3
      app/src/main/java/io/legado/app/ui/rss/source/manage/RssSourceActivity.kt
  35. 79
      app/src/main/java/io/legado/app/ui/widget/ArcView.kt
  36. 1
      app/src/main/java/io/legado/app/ui/widget/font/FontSelectDialog.kt
  37. 6
      app/src/main/java/io/legado/app/ui/widget/text/AutoCompleteTextView.kt
  38. 81
      app/src/main/java/io/legado/app/ui/widget/text/ScrollTextView.kt
  39. 456
      app/src/main/res/layout/activity_book_info.xml
  40. 8
      app/src/main/res/layout/dialog_read_book_style.xml
  41. 110
      app/src/main/res/layout/dialog_read_padding.xml
  42. 1
      app/src/main/res/layout/item_search.xml
  43. 13
      app/src/main/res/menu/change_cover.xml
  44. 5
      app/src/main/res/menu/txt_toc_regex.xml
  45. 42
      app/src/main/res/values/array_values.xml
  46. 52
      app/src/main/res/values/arrays.xml
  47. 15
      app/src/main/res/values/attrs.xml
  48. 3
      app/src/main/res/values/strings.xml
  49. 5
      app/src/main/res/xml/pref_config_other.xml

@ -138,8 +138,10 @@
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="booksource"
android:scheme="yuedu" />
@ -186,6 +188,8 @@
android:launchMode="singleTop" />
<activity
android:name=".ui.rss.read.ReadRssActivity"
android:configChanges="orientation|screenSize"
android:hardwareAccelerated="true"
android:launchMode="singleTop" />
<activity
android:name="io.legado.app.ui.book.local.ImportBookActivity"
@ -205,6 +209,10 @@
<activity
android:name="io.legado.app.ui.book.download.DownloadActivity"
android:launchMode="singleTop" />
<activity
android:name=".ui.login.SourceLogin"
android:configChanges="orientation|screenSize"
android:hardwareAccelerated="true" />
<activity
android:name=".receiver.SharedReceiverActivity"
android:label="@string/receiving_shared_label">
@ -219,7 +227,6 @@
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<activity android:name=".ui.login.SourceLogin" />
<service android:name=".service.CheckSourceService" />
<service android:name=".service.DownloadService" />

@ -2,11 +2,19 @@
* 旧版数据导入教程:先在旧版阅读(2.x)中进行备份,然后在新版阅读(3.x)【我的】->【备份与恢复】,选择【导入旧版本数据】。
* 请关注[开源阅读软件]()支持我,同时关注合作公众号[小说拾遗](),阅读公众号小编。
**2020/03/06**
* 添加隐藏标题
* 行距段距改成倍距,根据字体大小变化
* 修复翻页时右下角页数闪烁
* 修复朗读错行
* 添加底部分隔线,开关在边距设置里
**2020/03/05**
* 修复翻页动画
* 修复主题模式跟随
* 修复滚动翻页切换章节时跳动
* 适配阅读3.0的web做源
* 本地目录规则网络导入
**2020/03/04**
* 修复仿真翻页动画

@ -27,6 +27,7 @@ object PreferKey {
const val fontFolder = "fontFolder"
const val backupPath = "backupUri"
const val threadCount = "threadCount"
const val webPort = "webPort"
const val keepLight = "keep_light"
const val webService = "webService"
const val webDavUrl = "web_dav_url"

@ -16,6 +16,9 @@ interface TxtTocRuleDao {
@get:Query("select * from txtTocRules where enable = 1 order by serialNumber")
val enabled: List<TxtTocRule>
@get:Query("select ifNull(max(serialNumber), 0) from txtTocRules")
val lastOrderNum: Int
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(vararg rule: TxtTocRule)

@ -1,11 +1,14 @@
package io.legado.app.data.entities
import android.os.Parcelable
import android.text.TextUtils
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.Index
import androidx.room.PrimaryKey
import kotlinx.android.parcel.Parcelize
import java.util.regex.Pattern
import java.util.regex.PatternSyntaxException
@Parcelize
@Entity(
@ -37,4 +40,20 @@ data class ReplaceRule(
override fun hashCode(): Int {
return id.hashCode()
}
fun isValid(): Boolean{
if (TextUtils.isEmpty(pattern)){
return false;
}
//判断正则表达式是否正确
if (isRegex){
try {
Pattern.compile(pattern);
}
catch (ex: PatternSyntaxException){
return false;
}
}
return true;
}
}

@ -8,10 +8,8 @@ import android.renderscript.Allocation
import android.renderscript.Element
import android.renderscript.RenderScript
import android.renderscript.ScriptIntrinsicBlur
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import com.bumptech.glide.load.resource.bitmap.CenterCrop
import java.security.MessageDigest
import kotlin.math.min
import kotlin.math.roundToInt
@ -21,15 +19,16 @@ import kotlin.math.roundToInt
* 模糊
* @radius: 0..25
*/
class BlurTransformation(context: Context, private val radius: Int) : BitmapTransformation() {
class BlurTransformation(context: Context, private val radius: Int) : CenterCrop() {
private val rs: RenderScript = RenderScript.create(context)
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
val transform = super.transform(pool, toTransform, outWidth, outHeight)
//图片缩小1/2
val width = (min(outWidth, toTransform.width) / 2f).roundToInt()
val height = (min(outHeight, toTransform.height) / 2f).roundToInt()
val blurredBitmap = Bitmap.createScaledBitmap(toTransform, width, height, false);
val width = (min(outWidth, transform.width) / 2f).roundToInt()
val height = (min(outHeight, transform.height) / 2f).roundToInt()
val blurredBitmap = Bitmap.createScaledBitmap(transform, width, height, false);
// Allocate memory for Renderscript to work with
//分配用于渲染脚本的内存
val input = Allocation.createFromBitmap(

@ -165,10 +165,21 @@ object ReadBookConfig {
if (shareLayout) shareConfig.paragraphSpacing = value
else durConfig.paragraphSpacing = value
var titleCenter: Boolean
get() = if (shareLayout) shareConfig.titleCenter else durConfig.titleCenter
var titleMode: Int
get() = if (shareLayout) shareConfig.titleMode else durConfig.titleMode
set(value) =
if (shareLayout) shareConfig.titleCenter = value else durConfig.titleCenter = value
if (shareLayout) shareConfig.titleMode = value else durConfig.titleMode = value
var titleTopSpacing: Int
get() = if (shareLayout) shareConfig.titleTopSpacing else durConfig.titleTopSpacing
set(value) =
if (shareLayout) shareConfig.titleTopSpacing = value
else durConfig.titleTopSpacing = value
var titleBottomSpacing: Int
get() = if (shareLayout) shareConfig.titleBottomSpacing else durConfig.titleBottomSpacing
set(value) =
if (shareLayout) shareConfig.titleBottomSpacing = value
else durConfig.titleBottomSpacing = value
var paddingBottom: Int
get() = if (shareLayout) shareConfig.paddingBottom else durConfig.paddingBottom
@ -238,6 +249,12 @@ object ReadBookConfig {
if (shareLayout) shareConfig.footerPaddingTop = value
else durConfig.footerPaddingTop = value
var showFooterLine: Boolean
get() = if (shareLayout) shareConfig.showFooterLine else durConfig.showFooterLine
set(value) =
if (shareLayout) shareConfig.showFooterLine = value
else durConfig.showFooterLine = value
@Keep
class Config(
private var bgStr: String = "#EEEEEE",//白天背景
@ -253,7 +270,9 @@ object ReadBookConfig {
var letterSpacing: Float = 0.5f,//字间距
var lineSpacingExtra: Int = 12,//行间距
var paragraphSpacing: Int = 12,//段距
var titleCenter: Boolean = true,//标题居中
var titleMode: Int = 0,//标题居中
var titleTopSpacing: Int = 0,
var titleBottomSpacing: Int = 0,
var paddingBottom: Int = 6,
var paddingLeft: Int = 16,
var paddingRight: Int = 16,
@ -265,7 +284,8 @@ object ReadBookConfig {
var footerPaddingBottom: Int = 6,
var footerPaddingLeft: Int = 16,
var footerPaddingRight: Int = 16,
var footerPaddingTop: Int = 6
var footerPaddingTop: Int = 6,
var showFooterLine: Boolean = true
) {
fun setBg(bgType: Int, bg: String) {
if (AppConfig.isNightTheme) {

@ -136,13 +136,17 @@ object ImportOldData {
return bookSources.size
}
fun importOldReplaceRule(json: String): Int {
val replaceRules = mutableListOf<ReplaceRule>()
val items: List<Map<String, Any>> = Restore.jsonPath.parse(json).read("$")
for (item in items) {
val jsonItem = Restore.jsonPath.parse(item)
OldRule.jsonToReplaceRule(jsonItem.jsonString())?.let {
replaceRules.add(it)
if (it.isValid()){
replaceRules.add(it)
}
}
}
App.db.replaceRuleDao().insert(*replaceRules.toTypedArray())

@ -3,12 +3,12 @@ package io.legado.app.service
import android.content.Context
import android.content.Intent
import androidx.core.app.NotificationCompat
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseService
import io.legado.app.constant.IntentAction
import io.legado.app.constant.AppConst
import io.legado.app.constant.EventBus
import io.legado.app.constant.IntentAction
import io.legado.app.constant.PreferKey
import io.legado.app.help.IntentHelp
import io.legado.app.utils.NetworkUtils
import io.legado.app.utils.getPrefInt
@ -96,7 +96,7 @@ class WebService : BaseService() {
}
private fun getPort(): Int {
var port = App.INSTANCE.getPrefInt("webPort", 1122)
var port = getPrefInt(PreferKey.webPort, 1122)
if (port > 65530 || port < 1024) {
port = 1122
}

@ -11,7 +11,7 @@ import android.widget.SeekBar
import androidx.lifecycle.Observer
import com.bumptech.glide.RequestBuilder
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.RequestOptions.bitmapTransform
import io.legado.app.R
import io.legado.app.base.VMBaseActivity
import io.legado.app.constant.EventBus
@ -120,19 +120,17 @@ class AudioPlayActivity :
ImageLoader.load(this, path)
.placeholder(R.drawable.image_cover_default)
.error(R.drawable.image_cover_default)
.centerCrop()
.into(iv_cover)
ImageLoader.load(this, path)
.transition(DrawableTransitionOptions.withCrossFade(1500))
.thumbnail(defaultCover())
.centerCrop()
.apply(RequestOptions.bitmapTransform(BlurTransformation(this, 25)))
.apply(bitmapTransform(BlurTransformation(this, 25)))
.into(iv_bg)
}
private fun defaultCover(): RequestBuilder<Drawable> {
return ImageLoader.load(this, R.drawable.image_cover_default)
.apply(RequestOptions.bitmapTransform(BlurTransformation(this, 25)))
.apply(bitmapTransform(BlurTransformation(this, 25)))
}
private fun playButton() {

@ -3,19 +3,24 @@ package io.legado.app.ui.book.changecover
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.fragment.app.DialogFragment
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.GridLayoutManager
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.Theme
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel
import kotlinx.android.synthetic.main.dialog_change_source.*
class ChangeCoverDialog : DialogFragment(),
ChangeCoverViewModel.CallBack,
class ChangeCoverDialog : BaseDialogFragment(),
Toolbar.OnMenuItemClickListener,
CoverAdapter.CallBack {
companion object {
@ -35,7 +40,7 @@ class ChangeCoverDialog : DialogFragment(),
private var callBack: CallBack? = null
private lateinit var viewModel: ChangeCoverViewModel
override lateinit var adapter: CoverAdapter
lateinit var adapter: CoverAdapter
override fun onStart() {
super.onStart()
@ -51,25 +56,57 @@ class ChangeCoverDialog : DialogFragment(),
): View? {
callBack = activity as? CallBack
viewModel = getViewModel(ChangeCoverViewModel::class.java)
viewModel.callBack = this
return inflater.inflate(R.layout.dialog_change_cover, container)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.searchStateData.observe(viewLifecycleOwner, Observer {
refresh_progress_bar.isAutoLoading = it
})
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
tool_bar.setTitle(R.string.change_cover_source)
arguments?.let { bundle ->
viewModel.initData(bundle)
}
viewModel.initData(arguments)
initMenu()
initView()
}
private fun initMenu() {
tool_bar.inflateMenu(R.menu.change_cover)
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
tool_bar.setOnMenuItemClickListener(this)
}
private fun initView() {
recycler_view.layoutManager = GridLayoutManager(requireContext(), 3)
adapter = CoverAdapter(requireContext(), this)
recycler_view.adapter = adapter
viewModel.loadDbSearchBook()
}
override fun observeLiveBus() {
super.observeLiveBus()
viewModel.searchStateData.observe(viewLifecycleOwner, Observer {
refresh_progress_bar.isAutoLoading = it
if (it) {
stopMenuItem?.setIcon(R.drawable.ic_stop_black_24dp)
} else {
stopMenuItem?.setIcon(R.drawable.ic_refresh_black_24dp)
}
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
})
viewModel.searchBooksLiveData.observe(viewLifecycleOwner, Observer {
val diffResult = DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), it))
adapter.setItems(it)
diffResult.dispatchUpdatesTo(adapter)
})
}
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_stop -> viewModel.stopSearch()
}
return false
}
private val stopMenuItem: MenuItem?
get() = tool_bar.menu.findItem(R.id.menu_stop)
override fun changeTo(coverUrl: String) {
callBack?.coverChangeTo(coverUrl)
dismiss()

@ -6,41 +6,46 @@ import androidx.lifecycle.MutableLiveData
import io.legado.app.App
import io.legado.app.base.BaseViewModel
import io.legado.app.constant.AppPattern
import io.legado.app.data.entities.SearchBook
import io.legado.app.help.AppConfig
import io.legado.app.help.coroutine.Coroutine
import io.legado.app.model.WebBook
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.withContext
import java.util.concurrent.Executors
class ChangeCoverViewModel(application: Application) : BaseViewModel(application) {
private var searchPool =
Executors.newFixedThreadPool(AppConfig.threadCount).asCoroutineDispatcher()
var callBack: CallBack? = null
var name: String = ""
var author: String = ""
private var task: Coroutine<*>? = null
val searchStateData = MutableLiveData<Boolean>()
val searchBooksLiveData = MutableLiveData<List<SearchBook>>()
private val searchBooks = ArrayList<SearchBook>()
fun initData(bundle: Bundle) {
bundle.getString("name")?.let {
name = it
}
bundle.getString("author")?.let {
author = it.replace(AppPattern.authorRegex, "")
fun initData(arguments: Bundle?) {
arguments?.let { bundle ->
bundle.getString("name")?.let {
name = it
}
bundle.getString("author")?.let {
author = it.replace(AppPattern.authorRegex, "")
}
}
}
fun loadDbSearchBook() {
execute {
App.db.searchBookDao().getEnableHasCover(name, author)
}.onSuccess {
it?.let {
callBack?.adapter?.setItems(it)
App.db.searchBookDao().getEnableHasCover(name, author).let {
searchBooks.addAll(it)
if (it.size <= 1) {
searchBooksLiveData.postValue(searchBooks)
search()
} else {
searchBooksLiveData.postValue(searchBooks)
}
}
}.onFinally {
search()
}
}
@ -59,12 +64,9 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application
&& !searchBook.coverUrl.isNullOrEmpty()
) {
App.db.searchBookDao().insert(searchBook)
callBack?.adapter?.let { adapter ->
if (!adapter.getItems().contains(searchBook)) {
withContext(Dispatchers.Main) {
adapter.addItem(searchBook)
}
}
if (!searchBooks.contains(searchBook)) {
searchBooks.add(searchBook)
searchBooksLiveData.postValue(searchBooks)
}
}
}
@ -77,12 +79,17 @@ class ChangeCoverViewModel(application: Application) : BaseViewModel(application
}
}
fun stopSearch() {
if (task?.isActive == true) {
task?.cancel()
} else {
search()
}
}
override fun onCleared() {
super.onCleared()
searchPool.close()
}
interface CallBack {
var adapter: CoverAdapter
}
}

@ -0,0 +1,52 @@
package io.legado.app.ui.book.changecover
import android.os.Bundle
import androidx.recyclerview.widget.DiffUtil
import io.legado.app.data.entities.SearchBook
class DiffCallBack(private val oldItems: List<SearchBook>, private val newItems: List<SearchBook>) :
DiffUtil.Callback() {
override fun getOldListSize(): Int {
return oldItems.size
}
override fun getNewListSize(): Int {
return newItems.size
}
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
return oldItem.bookUrl == newItem.bookUrl
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
if (oldItem.originName != newItem.originName) {
return false
}
if (oldItem.coverUrl != newItem.coverUrl) {
return false
}
return true
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val oldItem = oldItems[oldItemPosition]
val newItem = newItems[newItemPosition]
val payload = Bundle()
if (oldItem.originName != newItem.originName) {
payload.putString("name", newItem.originName)
}
if (oldItem.coverUrl != newItem.coverUrl) {
payload.putString("coverUrl", newItem.coverUrl)
}
if (payload.isEmpty) {
return null
}
return payload
}
}

@ -8,13 +8,13 @@ import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
import io.legado.app.constant.PreferKey
import io.legado.app.constant.Theme
import io.legado.app.data.entities.Book
@ -27,7 +27,7 @@ import io.legado.app.utils.putPrefBoolean
import kotlinx.android.synthetic.main.dialog_change_source.*
class ChangeSourceDialog : DialogFragment(),
class ChangeSourceDialog : BaseDialogFragment(),
Toolbar.OnMenuItemClickListener,
ChangeSourceAdapter.CallBack {
@ -67,8 +67,7 @@ class ChangeSourceDialog : DialogFragment(),
return inflater.inflate(R.layout.dialog_change_source, container)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
viewModel.initData(arguments)
showTitle()
initMenu()
@ -76,7 +75,6 @@ class ChangeSourceDialog : DialogFragment(),
initSearchView()
initLiveData()
viewModel.loadDbSearchBook()
viewModel.search()
}
private fun showTitle() {
@ -138,6 +136,12 @@ class ChangeSourceDialog : DialogFragment(),
private fun initLiveData() {
viewModel.searchStateData.observe(viewLifecycleOwner, Observer {
refresh_progress_bar.isAutoLoading = it
if (it) {
stopMenuItem?.setIcon(R.drawable.ic_stop_black_24dp)
} else {
stopMenuItem?.setIcon(R.drawable.ic_refresh_black_24dp)
}
tool_bar.menu.applyTint(requireContext(), Theme.getTheme())
})
viewModel.searchBooksLiveData.observe(viewLifecycleOwner, Observer {
val diffResult = DiffUtil.calculateDiff(DiffCallBack(adapter.getItems(), it))
@ -146,6 +150,9 @@ class ChangeSourceDialog : DialogFragment(),
})
}
private val stopMenuItem: MenuItem?
get() = tool_bar.menu.findItem(R.id.menu_stop)
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_load_toc -> {

@ -45,7 +45,12 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
execute {
App.db.searchBookDao().getByNameAuthorEnable(name, author).let {
searchBooks.addAll(it)
upAdapter()
if (it.size <= 1) {
upAdapter()
search()
} else {
upAdapter()
}
}
}
}
@ -147,7 +152,11 @@ class ChangeSourceViewModel(application: Application) : BaseViewModel(applicatio
}
fun stopSearch() {
task?.cancel()
if (task?.isActive == true) {
task?.cancel()
} else {
search()
}
}
override fun onCleared() {

@ -4,7 +4,6 @@ import android.app.Activity
import android.content.Intent
import android.graphics.drawable.Drawable
import android.os.Bundle
import android.text.method.ScrollingMovementMethod
import android.view.Menu
import android.view.MenuItem
import androidx.lifecycle.Observer
@ -55,7 +54,6 @@ class BookInfoActivity :
override fun onActivityCreated(savedInstanceState: Bundle?) {
setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
tv_intro.movementMethod = ScrollingMovementMethod.getInstance()
viewModel.bookData.observe(this, Observer { showBook(it) })
viewModel.chapterListData.observe(this, Observer { upLoading(false, it) })
viewModel.initData(intent)
@ -126,7 +124,6 @@ class BookInfoActivity :
ImageLoader.load(this, book.getDisplayCover())
.transition(DrawableTransitionOptions.withCrossFade(1500))
.thumbnail(defaultCover())
.centerCrop()
.apply(bitmapTransform(BlurTransformation(this, 25)))
.into(bg_book) //模糊、渐变、缩小效果
}

@ -11,6 +11,7 @@ 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.gone
import io.legado.app.utils.postEvent
import io.legado.app.utils.visible
import kotlinx.android.synthetic.main.dialog_read_padding.*
@ -51,14 +52,13 @@ class PaddingConfigDialog : DialogFragment() {
ReadBookConfig.save()
}
private fun initData() = with(ReadBookConfig) {
private fun initData() = ReadBookConfig.apply {
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()
ll_header_padding.visible()
tv_body_padding.setPadding(0, 10.dp, 0, 10.dp)
} else {
ll_header_padding.gone()
tv_body_padding.setPadding(0, 0.dp, 0, 10.dp)
}
//正文
dsb_padding_top.progress = paddingTop
@ -75,6 +75,7 @@ class PaddingConfigDialog : DialogFragment() {
dsb_footer_padding_bottom.progress = footerPaddingBottom
dsb_footer_padding_left.progress = footerPaddingLeft
dsb_footer_padding_right.progress = footerPaddingRight
cb_show_line.isChecked = showFooterLine
}
private fun initView() = with(ReadBookConfig) {
@ -129,6 +130,12 @@ class PaddingConfigDialog : DialogFragment() {
footerPaddingRight = it
postEvent(EventBus.UP_CONFIG, true)
}
cb_show_line.onCheckedChangeListener = { cb, isChecked ->
if (cb.isPressed) {
showFooterLine = isChecked
postEvent(EventBus.UP_CONFIG, true)
}
}
}
}

@ -62,6 +62,7 @@ class ReadAloudDialog : BaseDialogFragment() {
private fun initData() {
upPlayState()
upTimerText(BaseReadAloudService.timeMinute)
seek_timer.progress = BaseReadAloudService.timeMinute
cb_tts_follow_sys.isChecked = requireContext().getPrefBoolean("ttsFollowSys", true)
seek_tts_SpeechRate.isEnabled = !cb_tts_follow_sys.isChecked
@ -89,7 +90,7 @@ class ReadAloudDialog : BaseDialogFragment() {
})
seek_timer.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
tv_timer.text = requireContext().getString(R.string.timer_m, progress)
upTimerText(progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar?) = Unit
@ -126,6 +127,10 @@ class ReadAloudDialog : BaseDialogFragment() {
}
}
private fun upTimerText(timeMinute: Int) {
tv_timer.text = requireContext().getString(R.string.timer_m, timeMinute)
}
private fun upTtsSpeechRate() {
ReadAloud.upTtsSpeechRate(requireContext())
if (!BaseReadAloudService.pause) {

@ -32,6 +32,7 @@ import org.jetbrains.anko.sdk27.listeners.onLongClick
class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
val callBack get() = activity as? ReadBookActivity
lateinit var titleModes: Array<String>
override fun onStart() {
super.onStart()
@ -61,6 +62,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
titleModes = requireContext().resources.getStringArray(R.array.title_mode)
initView()
initData()
initViewEvent()
@ -79,6 +81,8 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
dsb_text_letter_spacing.valueFormat = {
((it - 50) / 100f).toString()
}
dsb_line_size.valueFormat = { ((it - 10) / 10f).toString() }
dsb_paragraph_spacing.valueFormat = { (it / 10f).toString() }
}
private fun initData() {
@ -97,12 +101,12 @@ 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
tv_title_mode.onClick {
requireContext().selector("标题模式", titleModes.toList()) { _, index ->
ReadBookConfig.titleMode = index
tv_title_mode.text = titleModes[index]
postEvent(EventBus.UP_CONFIG, true)
}
postEvent(EventBus.UP_CONFIG, true)
}
tv_text_bold.onClick {
ReadBookConfig.apply {
@ -128,7 +132,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
callBack?.showPaddingConfig()
}
dsb_text_size.onChanged = {
ReadBookConfig.textSize = it + 5
ReadBookConfig.textSize = it + 10
postEvent(EventBus.UP_CONFIG, true)
}
dsb_text_letter_spacing.onChanged = {
@ -187,7 +191,7 @@ class ReadStyleDialog : DialogFragment(), FontSelectDialog.CallBack {
private fun upStyle() {
ReadBookConfig.let {
tv_title_center.isSelected = it.titleCenter
tv_title_mode.text = titleModes.getOrElse(it.titleMode) { titleModes[0] }
tv_text_bold.isSelected = it.textBold
dsb_text_size.progress = it.textSize - 5
dsb_text_letter_spacing.progress = (it.letterSpacing * 100).toInt() + 50

@ -15,6 +15,7 @@ import androidx.lifecycle.Observer
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import io.legado.app.App
import io.legado.app.R
import io.legado.app.base.BaseDialogFragment
@ -27,9 +28,10 @@ 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 io.legado.app.ui.widget.text.AutoCompleteTextView
import io.legado.app.utils.*
import kotlinx.android.synthetic.main.dialog_edit_text.view.*
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.*
@ -40,11 +42,12 @@ import java.util.*
class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
private val importTocRuleKey = "tocRuleUrl"
private lateinit var adapter: TocRegexAdapter
private var tocRegexLiveData: LiveData<List<TxtTocRule>>? = null
var selectedName: String? = null
private var durRegex: String? = null
lateinit var viewModel: TocRegexViewModel
override fun onStart() {
super.onStart()
@ -58,6 +61,7 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
viewModel = getViewModel(TocRegexViewModel::class.java)
return inflater.inflate(R.layout.dialog_toc_regex, container)
}
@ -121,17 +125,51 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_add -> editRule()
R.id.menu_default -> importDefault()
R.id.menu_default -> viewModel.importDefault()
R.id.menu_import -> showImportDialog()
}
return false
}
private fun importDefault() {
launch(IO) {
AnalyzeTxtFile.getDefaultRules().let {
App.db.txtTocRule().insert(*it.toTypedArray())
}
@SuppressLint("InflateParams")
private fun showImportDialog() {
val aCache = ACache.get(requireContext(), cacheDir = false)
val defaultUrl = "https://gitee.com/fisher52/YueDuJson/raw/master/myTxtChapterRule.json"
val cacheUrls: MutableList<String> = aCache
.getAsString(importTocRuleKey)
?.splitNotBlank(",")
?.toMutableList()
?: mutableListOf()
if (!cacheUrls.contains(defaultUrl)) {
cacheUrls.add(0, defaultUrl)
}
requireContext().alert(titleResource = R.string.import_book_source_on_line) {
var editText: AutoCompleteTextView? = null
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = this.edit_view
edit_view.setFilterValues(cacheUrls)
edit_view.delCallBack = {
cacheUrls.remove(it)
aCache.put(importTocRuleKey, cacheUrls.joinToString(","))
}
}
}
okButton {
val text = editText?.text?.toString()
text?.let {
if (!cacheUrls.contains(it)) {
cacheUrls.add(0, it)
aCache.put(importTocRuleKey, cacheUrls.joinToString(","))
}
Snackbar.make(tool_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show()
viewModel.importOnLine(it) { msg ->
tool_bar.snackbar(msg)
}
}
}
cancelButton()
}.show().applyTint()
}
@SuppressLint("InflateParams")
@ -151,25 +189,13 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
rootView?.apply {
tocRule.name = tv_rule_name.text.toString()
tocRule.rule = tv_rule_regex.text.toString()
saveRule(tocRule, rule)
viewModel.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<TxtTocRule>(context, R.layout.item_toc_regex),
ItemTouchCallback.OnItemTouchCallbackListener {
@ -240,7 +266,6 @@ class TocRegexDialog : BaseDialogFragment(), Toolbar.OnMenuItemClickListener {
}
}
companion object {
fun show(fragmentManager: FragmentManager, tocRegex: String? = null) {
val dialog = TocRegexDialog()

@ -0,0 +1,48 @@
package io.legado.app.ui.book.read.config
import android.app.Application
import io.legado.app.App
import io.legado.app.base.BaseViewModel
import io.legado.app.data.entities.TxtTocRule
import io.legado.app.help.http.HttpHelper
import io.legado.app.model.localBook.AnalyzeTxtFile
import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray
class TocRegexViewModel(application: Application) : BaseViewModel(application) {
fun saveRule(rule: TxtTocRule, oldRule: TxtTocRule? = null) {
execute {
if (rule.serialNumber < 0) {
rule.serialNumber = App.db.txtTocRule().lastOrderNum + 1
}
oldRule?.let {
App.db.txtTocRule().delete(oldRule)
}
App.db.txtTocRule().insert(rule)
}
}
fun importDefault() {
execute {
AnalyzeTxtFile.getDefaultRules().let {
App.db.txtTocRule().insert(*it.toTypedArray())
}
}
}
fun importOnLine(url: String, finally: (msg: String) -> Unit) {
execute {
HttpHelper.simpleGetAsync(url)?.let { json ->
GSON.fromJsonArray<TxtTocRule>(json)?.let {
App.db.txtTocRule().insert(*it.toTypedArray())
}
}
}.onSuccess {
finally("导入成功")
}.onError {
finally("导入失败")
}
}
}

@ -13,10 +13,7 @@ import io.legado.app.help.ReadBookConfig
import io.legado.app.ui.book.read.page.entities.TextChapter
import io.legado.app.ui.book.read.page.entities.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
import io.legado.app.utils.toStringArray
import io.legado.app.utils.*
@Suppress("DEPRECATION")
@ -51,34 +48,16 @@ object ChapterProvider {
val pageLines = arrayListOf<Int>()
val pageLengths = arrayListOf<Int>()
val stringBuilder = StringBuilder()
var surplusText = content
val contents = content.split("\n")
var durY = 0f
textPages.add(TextPage())
while (surplusText.isNotEmpty()) {
if (textPages.first().textLines.isEmpty()) {
//title
val end = surplusText.indexOf("\n")
if (end > 0) {
val title = surplusText.substring(0, end)
surplusText = surplusText.substring(end + 1)
durY = setTypeText(
title, durY, textPages, pageLines, pageLengths, stringBuilder, true
)
}
} else {
//正文
val end = surplusText.indexOf("\n")
val text: String
if (end >= 0) {
text = surplusText.substring(0, end)
surplusText = surplusText.substring(end + 1)
} else {
text = surplusText
surplusText = ""
}
durY =
setTypeText(text, durY, textPages, pageLines, pageLengths, stringBuilder, false)
for ((index, text) in contents.withIndex()) {
val isTitle = index == 0
if (isTitle && ReadBookConfig.titleMode == 2) {
continue
}
durY =
setTypeText(text, durY, textPages, pageLines, pageLengths, stringBuilder, isTitle)
}
textPages.last().height = durY + 20.dp
textPages.last().text = stringBuilder.toString()
@ -125,44 +104,45 @@ object ChapterProvider {
Layout.Alignment.ALIGN_NORMAL, 0f, 0f, true
)
for (lineIndex in 0 until layout.lineCount) {
textPages.last().height = durY
val textLine = TextLine(isTitle = isTitle)
if (durY + textPaint.textHeight < visibleHeight) {
textPages.last().textLines.add(textLine)
durY += textPaint.textHeight + lineSpacingExtra
} else {
textPages.last().text = stringBuilder.toString()
stringBuilder.clear()
pageLines.add(textPages.last().textLines.size)
pageLengths.add(textPages.last().text.length)
//新页面
durY = textPaint.textHeight + lineSpacingExtra
textPages.add(TextPage())
textPages.last().textLines.add(textLine)
}
textLine.lineBottom = paddingTop + durY - lineSpacingExtra
textLine.lineBase = textLine.lineBottom - textPaint.fontMetrics.descent
textLine.lineTop = textLine.lineBottom - textPaint.textHeight
val words =
text.substring(layout.getLineStart(lineIndex), layout.getLineEnd(lineIndex))
stringBuilder.append(words)
textLine.text = words
val desiredWidth = layout.getLineWidth(lineIndex)
var isLastLine = false
if (lineIndex == 0 && layout.lineCount > 1 && !isTitle) {
//第一行
addCharsToLineFirst(textLine, words, textPaint, desiredWidth)
} else if (lineIndex == layout.lineCount - 1) {
//最后一行
val x = if (isTitle && ReadBookConfig.titleCenter)
isLastLine = true
val x = if (isTitle && ReadBookConfig.titleMode == 1)
(visibleWidth - layout.getLineWidth(lineIndex)) / 2
else 0f
addCharsToLineLast(textLine, words, stringBuilder, textPaint, x)
addCharsToLineLast(textLine, words, textPaint, x)
} else {
//中间行
addCharsToLineMiddle(textLine, words, textPaint, desiredWidth, 0f)
}
if (durY + textPaint.textHeight > visibleHeight) {
//当前页面结束,设置各种值
textPages.last().text = stringBuilder.toString()
pageLines.add(textPages.last().textLines.size)
pageLengths.add(textPages.last().text.length)
textPages.last().height = durY
//新建页面
textPages.add(TextPage())
stringBuilder.clear()
durY = 0f
}
stringBuilder.append(words)
if (isLastLine) stringBuilder.append("\n")
textPages.last().textLines.add(textLine)
textLine.upTopBottom(durY, textPaint)
durY += textPaint.textHeight * lineSpacingExtra / 10f
textPages.last().height = durY
}
durY += paragraphSpacing
durY += textPaint.textHeight * paragraphSpacing / 10f
return durY
}
@ -223,11 +203,9 @@ object ChapterProvider {
private fun addCharsToLineLast(
textLine: TextLine,
words: String,
stringBuilder: StringBuilder,
textPaint: TextPaint,
startX: Float
) {
stringBuilder.append("\n")
textLine.text = "$words\n"
var x = startX
words.toStringArray().forEach {
@ -291,10 +269,10 @@ object ChapterProvider {
val bold = if (ReadBookConfig.textBold) Typeface.BOLD else Typeface.NORMAL
contentPaint.typeface = Typeface.create(typeface, bold)
//间距
lineSpacingExtra = ReadBookConfig.lineSpacingExtra.dp
paragraphSpacing = ReadBookConfig.paragraphSpacing.dp
titlePaint.textSize = (ReadBookConfig.textSize + 2).dp.toFloat()
contentPaint.textSize = ReadBookConfig.textSize.dp.toFloat()
lineSpacingExtra = ReadBookConfig.lineSpacingExtra
paragraphSpacing = ReadBookConfig.paragraphSpacing
titlePaint.textSize = (ReadBookConfig.textSize + 2).sp.toFloat()
contentPaint.textSize = ReadBookConfig.textSize.sp.toFloat()
upSize()
}
@ -311,8 +289,6 @@ object ChapterProvider {
visibleBottom = paddingTop + visibleHeight
}
private val TextPaint.textHeight: Float
get() {
return this.fontMetrics.descent - fontMetrics.ascent + fontMetrics.leading
}
val TextPaint.textHeight: Float
get() = this.fontMetrics.descent - fontMetrics.ascent + fontMetrics.leading
}

@ -60,6 +60,7 @@ class ContentView(context: Context) : FrameLayout(context) {
footerPaddingRight.dp,
footerPaddingBottom.dp
)
vw_bottom_divider.visible(showFooterLine)
content_text_view.upVisibleRect()
durConfig.textColor().let {
tv_top_left.setTextColor(it)

@ -18,14 +18,16 @@ class CoverPageDelegate(pageView: PageView) : HorizontalPageDelegate(pageView) {
}
override fun onDraw(canvas: Canvas) {
if (!isRunning) return
val offsetX = touchX - startX
if ((mDirection == Direction.NEXT && offsetX > 0)
|| (mDirection == Direction.PREV && offsetX < 0)
) return
) {
return
}
val distanceX = if (offsetX > 0) offsetX - viewWidth else offsetX + viewWidth
if (!isRunning) return
if (mDirection == Direction.PREV) {
bitmapMatrix.setTranslate(distanceX, 0.toFloat())
curBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }

@ -65,13 +65,13 @@ abstract class PageDelegate(protected val pageView: PageView) :
var mDirection = Direction.NONE
var isCancel = false
var isRunning = false
var isStarted = false
private var isStarted = false
var isTextSelected = false
var selectedOnDown = false
private var selectedOnDown = false
var firstRelativePage = 0
var firstLineIndex: Int = 0
var firstCharIndex: Int = 0
private var firstRelativePage = 0
private var firstLineIndex: Int = 0
private var firstCharIndex: Int = 0
init {
curPage.resetPageOffset()
@ -127,10 +127,12 @@ abstract class PageDelegate(protected val pageView: PageView) :
}
private fun stopScroll() {
isMoved = false
isRunning = false
isStarted = false
pageView.invalidate()
pageView.post {
isMoved = false
isRunning = false
pageView.invalidate()
}
}
open fun setViewSize(width: Int, height: Int) {

@ -1,5 +1,9 @@
package io.legado.app.ui.book.read.page.entities
import android.text.TextPaint
import io.legado.app.ui.book.read.page.ChapterProvider
import io.legado.app.ui.book.read.page.ChapterProvider.textHeight
data class TextLine(
var text: String = "",
val textChars: ArrayList<TextChar> = arrayListOf(),
@ -10,6 +14,12 @@ data class TextLine(
var isReadAloud: Boolean = false
) {
fun upTopBottom(durY: Float, textPaint: TextPaint) {
lineTop = ChapterProvider.paddingTop + durY
lineBottom = lineTop + textPaint.textHeight
lineBase = lineBottom - textPaint.fontMetrics.descent
}
fun addTextChar(charData: String, start: Float, end: Float) {
textChars.add(TextChar(charData, start = start, end = end))
}

@ -34,14 +34,15 @@ data class TextPage(
(layout.getLineBottom(lineIndex) - layout.getLineBaseline(lineIndex)))
textLine.lineBottom =
textLine.lineBase + ChapterProvider.contentPaint.fontMetrics.descent
var x = (ChapterProvider.visibleWidth - layout.getLineMax(lineIndex)) / 2
var x = ChapterProvider.paddingLeft +
(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))
textLine.addTextChar(charData = char, start = x, end = x1)
x = x1
}
textLines.add(textLine)

@ -214,7 +214,8 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view
edit_view.setFilterValues(cacheUrls) {
edit_view.setFilterValues(cacheUrls)
edit_view.delCallBack = {
cacheUrls.remove(it)
aCache.put(importRecordKey, cacheUrls.joinToString(","))
}

@ -18,6 +18,7 @@ import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp
import io.legado.app.lib.theme.ATH
import io.legado.app.receiver.SharedReceiverActivity
import io.legado.app.service.WebService
import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.filechooser.FilePicker
import io.legado.app.ui.widget.number.NumberPickerDialog
@ -34,12 +35,14 @@ class OtherConfigFragment : PreferenceFragmentCompat(),
App.INSTANCE,
SharedReceiverActivity::class.java.name
)
private val webPort get() = getPrefInt(PreferKey.webPort, 1122)
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
putPrefBoolean(PreferKey.processText, isProcessTextEnabled())
addPreferencesFromResource(R.xml.pref_config_other)
upPreferenceSummary(PreferKey.downloadPath, BookHelp.downloadPath)
upPreferenceSummary(PreferKey.threadCount, AppConfig.threadCount.toString())
upPreferenceSummary(PreferKey.webPort, webPort.toString())
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -63,6 +66,14 @@ class OtherConfigFragment : PreferenceFragmentCompat(),
.show {
AppConfig.threadCount = it
}
PreferKey.webPort -> NumberPickerDialog(requireContext())
.setTitle(getString(R.string.web_port_title))
.setMaxValue(60000)
.setMinValue(1024)
.setValue(webPort)
.show {
putPrefInt(PreferKey.webPort, it)
}
PreferKey.downloadPath -> selectDownloadPath()
PreferKey.cleanCache -> {
BookHelp.clearCache()
@ -78,9 +89,15 @@ class OtherConfigFragment : PreferenceFragmentCompat(),
upPreferenceSummary(key, BookHelp.downloadPath)
}
PreferKey.threadCount -> upPreferenceSummary(
PreferKey.threadCount,
AppConfig.threadCount.toString()
key, AppConfig.threadCount.toString()
)
PreferKey.webPort -> {
upPreferenceSummary(key, webPort.toString())
if (WebService.isRun) {
WebService.stop(requireContext())
WebService.start(requireContext())
}
}
PreferKey.recordLog -> LogUtils.upLevel()
PreferKey.processText -> sharedPreferences?.let {
setProcessTextEnable(it.getBoolean(key, true))
@ -93,6 +110,7 @@ class OtherConfigFragment : PreferenceFragmentCompat(),
val preference = findPreference<Preference>(preferenceKey) ?: return
when (preferenceKey) {
PreferKey.threadCount -> preference.summary = getString(R.string.threads_num, value)
PreferKey.webPort -> preference.summary = getString(R.string.web_port_summary, value)
else -> if (preference is ListPreference) {
val index = preference.findIndexOfValue(value)
// Set the summary to reflect the new value.

@ -76,4 +76,8 @@ class SourceLogin : BaseActivity(R.layout.activity_source_login) {
return super.onCompatOptionsItemSelected(item)
}
override fun onDestroy() {
super.onDestroy()
web_view.destroy()
}
}

@ -213,7 +213,8 @@ class ReplaceRuleActivity : VMBaseActivity<ReplaceRuleViewModel>(R.layout.activi
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view
edit_view.setFilterValues(cacheUrls) {
edit_view.setFilterValues(cacheUrls)
edit_view.delCallBack = {
cacheUrls.remove(it)
aCache.put(importRecordKey, cacheUrls.joinToString(","))
}

@ -16,6 +16,7 @@ import io.legado.app.constant.Theme
import io.legado.app.data.entities.ReplaceRule
import io.legado.app.utils.applyTint
import io.legado.app.utils.getViewModel
import io.legado.app.utils.toast
import kotlinx.android.synthetic.main.dialog_replace_edit.*
class ReplaceEditDialog : DialogFragment(),
@ -73,9 +74,15 @@ class ReplaceEditDialog : DialogFragment(),
override fun onMenuItemClick(item: MenuItem?): Boolean {
when (item?.itemId) {
R.id.menu_save -> {
viewModel.save(getReplaceRule()) {
callBack?.onReplaceRuleSave()
dismiss()
val rule = getReplaceRule();
if (!rule.isValid()){
toast(R.string.replace_rule_invalid)
}
else{
viewModel.save(rule) {
callBack?.onReplaceRuleSave()
dismiss()
}
}
}
}

@ -203,4 +203,9 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_r
}
}
override fun onDestroy() {
super.onDestroy()
web_view.destroy()
}
}

@ -227,7 +227,8 @@ class RssSourceActivity : VMBaseActivity<RssSourceViewModel>(R.layout.activity_r
customView {
layoutInflater.inflate(R.layout.dialog_edit_text, null).apply {
editText = edit_view
edit_view.setFilterValues(cacheUrls) {
edit_view.setFilterValues(cacheUrls)
edit_view.delCallBack = {
cacheUrls.remove(it)
aCache.put(importRecordKey, cacheUrls.joinToString(","))
}

@ -0,0 +1,79 @@
package io.legado.app.ui.widget
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import io.legado.app.R
class ArcView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
private var mWidth = 0
private var mHeight = 0
//弧形高度
private val mArcHeight: Int
//背景颜色
private val mBgColor: Int
private val mPaint: Paint = Paint()
private val mDirectionTop: Boolean
val rect = Rect()
val path = Path()
init {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ArcView)
mArcHeight = typedArray.getDimensionPixelSize(R.styleable.ArcView_arcHeight, 0)
mBgColor = typedArray.getColor(
R.styleable.ArcView_bgColor,
Color.parseColor("#303F9F")
)
mDirectionTop = typedArray.getBoolean(R.styleable.ArcView_arcDirectionTop, false)
typedArray.recycle()
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
mPaint.style = Paint.Style.FILL
mPaint.color = mBgColor
if (mDirectionTop) {
rect.set(0, mArcHeight, mWidth, mHeight)
canvas.drawRect(rect, mPaint)
path.reset()
path.moveTo(0f, mArcHeight.toFloat())
path.quadTo(mWidth / 2.toFloat(), 0f, mWidth.toFloat(), mArcHeight.toFloat())
canvas.drawPath(path, mPaint)
} else {
rect.set(0, 0, mWidth, mHeight - mArcHeight)
canvas.drawRect(rect, mPaint)
path.reset()
path.moveTo(0f, mHeight - mArcHeight.toFloat())
path.quadTo(
mWidth / 2.toFloat(),
mHeight.toFloat(),
mWidth.toFloat(),
mHeight - mArcHeight.toFloat()
)
canvas.drawPath(path, mPaint)
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val widthSize = MeasureSpec.getSize(widthMeasureSpec)
val widthMode = MeasureSpec.getMode(widthMeasureSpec)
val heightSize = MeasureSpec.getSize(heightMeasureSpec)
val heightMode = MeasureSpec.getMode(heightMeasureSpec)
if (widthMode == MeasureSpec.EXACTLY) {
mWidth = widthSize
}
if (heightMode == MeasureSpec.EXACTLY) {
mHeight = heightSize
}
setMeasuredDimension(mWidth, mHeight)
}
}

@ -61,7 +61,6 @@ class FontSelectDialog : BaseDialogFragment(),
}
override fun onFragmentCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
tool_bar.setTitle(R.string.select_font)
tool_bar.inflateMenu(R.menu.font_select)
tool_bar.setOnMenuItemClickListener(this)

@ -42,15 +42,13 @@ class AutoCompleteTextView : AppCompatAutoCompleteTextView {
return super.onTouchEvent(event)
}
fun setFilterValues(values: List<String>?, delCallBack: ((value: String) -> Unit)? = null) {
this.delCallBack = delCallBack
fun setFilterValues(values: List<String>?) {
values?.let {
setAdapter(MyAdapter(context, values))
}
}
fun setFilterValues(vararg value: String, delCallBack: ((value: String) -> Unit)? = null) {
this.delCallBack = delCallBack
fun setFilterValues(vararg value: String) {
setAdapter(MyAdapter(context, value.toMutableList()))
}

@ -0,0 +1,81 @@
package io.legado.app.ui.widget.text
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.appcompat.widget.AppCompatTextView
class ScrollTextView(context: Context?, attrs: AttributeSet?) : AppCompatTextView(context, attrs) {
//滑动距离的最大边界
private var mOffsetHeight = 0
//是否到顶或者到底的标志
private var mBottomFlag = false
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
initOffsetHeight()
}
override fun onTextChanged(
text: CharSequence,
start: Int,
lengthBefore: Int,
lengthAfter: Int
) {
super.onTextChanged(text, start, lengthBefore, lengthAfter)
initOffsetHeight()
}
private fun initOffsetHeight() {
val mLayoutHeight: Int
//获得内容面板
val mLayout = layout ?: return
//获得内容面板的高度
mLayoutHeight = mLayout.height
//获取上内边距
val paddingTop: Int = totalPaddingTop
//获取下内边距
val paddingBottom: Int = totalPaddingBottom
//获得控件的实际高度
val mHeight: Int = measuredHeight
//计算滑动距离的边界
mOffsetHeight = mLayoutHeight + paddingTop + paddingBottom - mHeight
if (mOffsetHeight <= 0) {
scrollTo(0, 0)
}
}
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
//如果是新的按下事件,则对mBottomFlag重新初始化
mBottomFlag = mOffsetHeight <= 0
}
//如果已经不要这次事件,则传出取消的信号,这里的作用不大
if (mBottomFlag) {
event.action = MotionEvent.ACTION_CANCEL
}
return super.dispatchTouchEvent(event)
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
val result = super.onTouchEvent(event)
//如果是需要拦截,则再拦截,这个方法会在onScrollChanged方法之后再调用一次
if (!mBottomFlag) parent.requestDisallowInterceptTouchEvent(true)
return result
}
override fun onScrollChanged(horiz: Int, vert: Int, oldHoriz: Int, oldVert: Int) {
super.onScrollChanged(horiz, vert, oldHoriz, oldVert)
if (vert == mOffsetHeight || vert == 0) {
//这里触发父布局或祖父布局的滑动事件
parent.requestDisallowInterceptTouchEvent(false)
mBottomFlag = true
}
}
}

@ -9,227 +9,335 @@
<ImageView
android:id="@+id/bg_book"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:contentDescription="@string/bg_image"
app:layout_constraintBottom_toTopOf="@+id/view_info"
app:layout_constraintTop_toTopOf="parent" />
<View
<LinearLayout
android:id="@+id/vw_bg"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_height="match_parent"
android:background="#50000000"
app:layout_constraintBottom_toTopOf="@+id/view_info"
app:layout_constraintTop_toTopOf="parent" />
android:orientation="vertical">
<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" />
<io.legado.app.ui.widget.image.CoverImageView
android:id="@+id/iv_cover"
android:layout_width="110dp"
android:layout_height="160dp"
android:layout_margin="10dp"
android:scaleType="centerCrop"
android:src="@drawable/image_cover_default"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar" />
<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" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp">
<io.legado.app.ui.widget.ArcView
app:arcHeight="36dp"
app:bgColor="@color/background"
app:arcDirectionTop="true"
android:layout_marginTop="90dp"
android:layout_width="match_parent"
android:layout_height="78dp" />
<androidx.cardview.widget.CardView
app:cardCornerRadius="5dp"
app:cardElevation="8dp"
android:layout_margin="3dp"
android:layout_centerHorizontal="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<io.legado.app.ui.widget.image.CoverImageView
android:id="@+id/iv_cover"
android:layout_width="110dp"
android:layout_height="160dp"
android:contentDescription="@string/img_cover"
android:scaleType="centerCrop"
android:src="@drawable/image_cover_default" />
</androidx.cardview.widget.CardView>
</RelativeLayout>
<LinearLayout
android:id="@+id/ll_book_info"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:minHeight="115dp"
android:background="@color/background"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="@+id/iv_cover"
app:layout_constraintLeft_toRightOf="@+id/iv_cover"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@+id/iv_cover">
android:paddingLeft="10dp"
android:paddingTop="8dp"
android:paddingRight="10dp"
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:textColor="@color/md_white_1000"
android:textSize="18sp" />
android:text="@string/book_name"
android:textColor="@color/tv_text_default"
android:textSize="18sp"
tools:ignore="RtlHardcoded" />
<TextView
android:id="@+id/tv_author"
<io.legado.app.ui.widget.LabelsBar
android:id="@+id/lb_kind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:singleLine="true"
android:textColor="@color/md_white_1000"
android:textSize="16sp" />
android:layout_gravity="center"
android:gravity="center"
android:visibility="gone" />
<LinearLayout
</LinearLayout>
<ScrollView
android:background="@color/background"
android:layout_weight="1.0"
android:padding="0dp"
android:layout_height="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:orientation="horizontal">
android:scrollbarStyle="outsideInset"
android:scrollbars="vertical"
android:fillViewport="true"
android:fitsSystemWindows="false"
android:focusable="true" >
<TextView
android:id="@+id/tv_origin"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:singleLine="true"
android:textSize="16sp"
android:textColor="@color/md_white_1000"
tools:ignore="NestedWeights" />
<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">
<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="16sp"
app:radius="2dp" />
<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>
<io.legado.app.ui.widget.LabelsBar
android:id="@+id/lb_kind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="3dp"
android:visibility="gone" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="3dp"
android:paddingBottom="3dp">
<TextView
android:id="@+id/tv_lasted"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:singleLine="true"
android:textSize="16sp"
android:textColor="@color/md_white_1000"
app:layout_constraintBottom_toBottomOf="@+id/iv_cover"
app:layout_constraintLeft_toLeftOf="@+id/tv_name"
app:layout_constraintRight_toRightOf="@id/tv_name" />
<ImageView
android:layout_width="18sp"
android:layout_height="18sp"
android:contentDescription="@string/origin_format"
android:paddingRight="2dp"
android:src="@drawable/ic_author"
app:tint="@color/tv_text_summary"
tools:ignore="RtlHardcoded,RtlSymmetry" />
</LinearLayout>
<TextView
android:id="@+id/tv_author"
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/author"
android:textColor="@color/tv_text_summary"
android:textSize="13sp"
tools:ignore="NestedWeights" />
<LinearLayout
android:id="@+id/view_info"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="10dp"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="@+id/view_other"
app:layout_constraintTop_toBottomOf="@+id/iv_cover">
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="5dp"
android:background="@color/bg_divider_line" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="3dp"
android:paddingBottom="3dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="@string/book_intro"
android:textStyle="bold"
android:textColor="@color/tv_text_default"
android:textSize="16sp" />
<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_intro"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:minHeight="100dp"
android:textSize="14sp" />
<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" />
<View
android:layout_width="match_parent"
android:layout_height="5dp"
android:background="@color/bg_divider_line" />
<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="16sp"
app:radius="2dp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/view_other"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="12dp"
android:paddingBottom="12dp"
app:layout_constraintBottom_toTopOf="@+id/fl_action"
app:layout_constraintTop_toBottomOf="@+id/view_info">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingTop="3dp"
android:paddingBottom="3dp">
<TextView
android:id="@+id/tv_group"
<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>
<io.legado.app.ui.widget.text.ScrollTextView
android:id="@+id/tv_intro"
android:layout_marginTop="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:text="@string/book_intro"
android:textColor="@color/tv_text_secondary"
android:textSize="14sp"
android:paddingLeft="8dp"
android:visibility="visible" />
</LinearLayout>
</ScrollView>
<LinearLayout
android:id="@+id/view_other"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:singleLine="true"
android:text="@string/group_s"
android:textColor="@color/tv_text_default"
android:textSize="16sp" />
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_toc"
<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"
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>
<View
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="12dp"
android:singleLine="true"
android:text="@string/toc_s"
android:textColor="@color/tv_text_default"
android:textSize="16sp" />
android:layout_height="1px"
android:background="@color/bg_divider_line" />
</LinearLayout>
<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">
<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" />
<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" />
<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>
</androidx.constraintlayout.widget.ConstraintLayout>

@ -10,7 +10,7 @@
android:padding="10dp">
<io.legado.app.ui.widget.text.StrokeTextView
android:id="@+id/tv_title_center"
android:id="@+id/tv_title_mode"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="6dp"
@ -34,7 +34,7 @@
android:text="@string/text_bold"
android:gravity="center"
android:textSize="14sp"
app:layout_constraintLeft_toRightOf="@+id/tv_title_center"
app:layout_constraintLeft_toRightOf="@+id/tv_title_mode"
app:layout_constraintRight_toLeftOf="@+id/tv_text_font"
app:layout_constraintTop_toTopOf="parent" />
@ -123,7 +123,7 @@
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
app:max="50"
app:max="20"
app:title="@string/line_size"
app:layout_constraintTop_toBottomOf="@+id/dsb_text_letter_spacing" />
@ -133,7 +133,7 @@
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
app:max="50"
app:max="20"
app:title="@string/paragraph_size"
app:layout_constraintTop_toBottomOf="@+id/dsb_line_size" />

@ -7,51 +7,55 @@
android:padding="10dp"
android:orientation="vertical">
<io.legado.app.ui.widget.text.AccentTextView
android:id="@+id/tv_header_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:textSize="18sp"
android:visibility="gone"
android:text="@string/header" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_header_padding_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:title="@string/padding_top"
app:max="100" />
<io.legado.app.ui.widget.DetailSeekBar
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" />
<io.legado.app.ui.widget.DetailSeekBar
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" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_header_padding_right"
<LinearLayout
android:id="@+id/ll_header_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:title="@string/padding_right"
app:max="100" />
android:orientation="vertical">
<io.legado.app.ui.widget.text.AccentTextView
android:id="@+id/tv_header_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:textSize="18sp"
android:text="@string/header" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_header_padding_top"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_top"
app:max="100" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_header_padding_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_bottom"
app:max="100" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_header_padding_left"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_left"
app:max="100" />
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_header_padding_right"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/padding_right"
app:max="100" />
</LinearLayout>
<io.legado.app.ui.widget.text.AccentTextView
android:id="@+id/tv_body_padding"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textSize="18sp"
android:text="@string/main_body" />
@ -84,13 +88,33 @@
app:title="@string/padding_right"
app:max="100" />
<io.legado.app.ui.widget.text.AccentTextView
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textSize="18sp"
android:text="@string/footer" />
android:gravity="center_vertical"
android:orientation="horizontal">
<io.legado.app.ui.widget.text.AccentTextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textSize="18sp"
android:text="@string/footer" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/showLine" />
<io.legado.app.ui.widget.checkbox.SmoothCheckBox
android:id="@+id/cb_show_line"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_margin="6dp" />
</LinearLayout>
<io.legado.app.ui.widget.DetailSeekBar
android:id="@+id/dsb_footer_padding_top"

@ -43,6 +43,7 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="@id/iv_cover"
app:layout_constraintLeft_toLeftOf="@+id/tv_name"
app:layout_constraintRight_toRightOf="@id/tv_name"

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_stop"
android:title="@string/stop"
android:icon="@drawable/ic_stop_black_24dp"
app:showAsAction="always"
tools:ignore="AlwaysShowAction" />
</menu>

@ -15,4 +15,9 @@
android:title="@string/import_default_rule"
app:showAsAction="never" />
<item
android:id="@+id/menu_import"
android:title="@string/import_replace_rule_on_line"
app:showAsAction="never" />
</menu>

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="icons">
<item>ic_launcher</item>
<item>launcher1</item>
<item>launcher2</item>
<item>launcher3</item>
</string-array>
<string-array name="screen_time_out_value">
<item>0</item>
<item>60</item>
<item>120</item>
<item>180</item>
<item>-1</item>
</string-array>
<string-array name="theme_mode_v">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
<string-array name="tts_speech_per_value">
<item>0</item>
<item>1</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>103</item>
<item>106</item>
<item>110</item>
<item>111</item>
<item>11</item>
<item>6</item>
<item>9</item>
</string-array>
</resources>

@ -20,21 +20,6 @@
<item>百度主持</item>
</string-array>
<string-array name="tts_speech_per_value">
<item>0</item>
<item>1</item>
<item>3</item>
<item>4</item>
<item>5</item>
<item>103</item>
<item>106</item>
<item>110</item>
<item>111</item>
<item>11</item>
<item>6</item>
<item>9</item>
</string-array>
<string-array name="indent">
<item>@string/indent_0</item>
<item>@string/indent_1</item>
@ -61,12 +46,6 @@
<item>暗色主题</item>
</string-array>
<string-array name="theme_mode_v">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
<string-array name="NavBarColors">
<item>自动</item>
<item>黑色</item>
@ -82,14 +61,6 @@
<item>常亮</item>
</string-array>
<string-array name="screen_time_out_value">
<item>0</item>
<item>60</item>
<item>120</item>
<item>180</item>
<item>-1</item>
</string-array>
<string-array name="select_folder">
<item>@string/default_path</item>
<item>@string/sys_folder_picker</item>
@ -103,15 +74,6 @@
<item>@string/screen_sensor</item>
</string-array>
<declare-styleable name="Battery">
<attr name="batteryOrientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
<attr name="batteryColor" format="color" />
<attr name="batteryPower" format="integer" />
</declare-styleable>
<string-array name="icon_names">
<item>iconMain</item>
<item>icon1</item>
@ -119,13 +81,6 @@
<item>icon3</item>
</string-array>
<string-array name="icons">
<item>ic_launcher</item>
<item>launcher1</item>
<item>launcher2</item>
<item>launcher3</item>
</string-array>
<string-array name="chinese_mode">
<item>关闭</item>
<item>繁体转简体</item>
@ -137,4 +92,11 @@
<item>系统衬线字体</item>
<item>系统等宽字体</item>
</string-array>
<string-array name="title_mode">
<item>标题靠左</item>
<item>标题居中</item>
<item>标题隐藏</item>
</string-array>
</resources>

@ -137,6 +137,15 @@
<attr name="textColor" format="color" />
</declare-styleable>
<declare-styleable name="Battery">
<attr name="batteryOrientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
<attr name="batteryColor" format="color" />
<attr name="batteryPower" format="integer" />
</declare-styleable>
<declare-styleable name="VerticalSeekBar">
<attr name="seekBarRotation">
<!-- Clock wise - 90 deg; top = min, bottom = max -->
@ -154,4 +163,10 @@
<attr name="radius" />
<attr name="up_flat_angle" format="boolean" />
</declare-styleable>
<declare-styleable name="ArcView">
<attr name="arcHeight" format="dimension" />
<attr name="bgColor" format="color" />
<attr name="arcDirectionTop" format="boolean" />
</declare-styleable>
</resources>

@ -204,6 +204,7 @@
<string name="clear_all_content">清除缓存会删除所有已保存章节,是否确认删除?</string>
<string name="book_source_share_url">书源共享</string>
<string name="replace_rule_summary">替换规则名称</string>
<string name="replace_rule_invalid">替换规则为空或者不满足正则表达式要求</string>
<string name="select_action">选择操作</string>
<string name="select_all">全选</string>
<string name="select_all_count">全选(%d/%d)</string>
@ -226,7 +227,7 @@
<string name="open_from_other">打开外部书籍</string>
<string name="origin_show">来源:%s</string>
<string name="import_replace_rule">本地导入</string>
<string name="import_replace_rule_on_line">网络导入</string>
<string name="import_replace_rule_on_line">导入在线规则</string>
<string name="check_update_interval">检查更新间隔</string>
<string name="bookshelf_px_0">按阅读时间</string>
<string name="bookshelf_px_1">按更新时间</string>

@ -37,6 +37,11 @@
android:title="@string/threads_num_title"
app:iconSpaceReserved="false" />
<Preference
android:key="webPort"
android:title="@string/web_port_title"
app:iconSpaceReserved="false" />
<Preference
android:key="downloadPath"
android:title="@string/download_path"

Loading…
Cancel
Save