Merge pull request #4021 from xuanyue202/simulated_reading

feat(app): 增加模拟追读功能
This commit is contained in:
Horis
2024-07-10 15:10:06 +08:00
committed by GitHub
24 changed files with 388 additions and 23 deletions
@@ -16,18 +16,18 @@ import io.legado.app.help.book.BookHelp
import io.legado.app.help.book.ContentProcessor import io.legado.app.help.book.ContentProcessor
import io.legado.app.help.book.isEpub import io.legado.app.help.book.isEpub
import io.legado.app.help.book.isImage import io.legado.app.help.book.isImage
import io.legado.app.help.book.isLocal
import io.legado.app.help.book.isPdf import io.legado.app.help.book.isPdf
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
import io.legado.app.model.ReadBook import io.legado.app.model.ReadBook
import io.legado.app.model.localBook.LocalBook
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.MD5Utils import io.legado.app.utils.MD5Utils
import io.legado.app.utils.fromJsonObject import io.legado.app.utils.fromJsonObject
import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import java.nio.charset.Charset import java.nio.charset.Charset
import java.time.LocalDate
import java.time.Period.between
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@@ -160,9 +160,21 @@ data class Book(
@IgnoredOnParcel @IgnoredOnParcel
val lastChapterIndex get() = totalChapterNum - 1 val lastChapterIndex get() = totalChapterNum - 1
// 根据当前日期计算章节总数
fun simulatedTotalChapterNum(): Int {
if (config.readSimulating) {
val currentDate = LocalDate.now()
val daysPassed = between(this.config.startDate, currentDate).days + 1
// 计算当前应该解锁到哪一章
val chaptersToUnlock =
max(0, (config.startChapter ?: 0) + (daysPassed * config.dailyChapters))
return min(this.totalChapterNum, chaptersToUnlock)
} else return this.totalChapterNum
}
fun getRealAuthor() = author.replace(AppPattern.authorRegex, "") fun getRealAuthor() = author.replace(AppPattern.authorRegex, "")
fun getUnreadChapterNum() = max(totalChapterNum - durChapterIndex - 1, 0) fun getUnreadChapterNum() = max(simulatedTotalChapterNum() - durChapterIndex - 1, 0)
fun getDisplayCover() = if (customCoverUrl.isNullOrEmpty()) coverUrl else customCoverUrl fun getDisplayCover() = if (customCoverUrl.isNullOrEmpty()) coverUrl else customCoverUrl
@@ -257,6 +269,46 @@ data class Book(
return config.splitLongChapter return config.splitLongChapter
} }
// readSimulating 的 setter 和 getter
fun setReadSimulating(readSimulating: Boolean) {
config.readSimulating = readSimulating
}
fun getReadSimulating(): Boolean {
return config.readSimulating
}
// startDate 的 setter 和 getter
fun setStartDate(startDate: LocalDate?) {
config.startDate = startDate
}
fun getStartDate(): LocalDate? {
if (!config.readSimulating || config.startDate == null) {
return LocalDate.now()
}
return config.startDate
}
// startChapter 的 setter 和 getter
fun setStartChapter(startChapter: Int) {
config.startChapter = startChapter
}
fun getStartChapter(): Int {
if (config.readSimulating) return config.startChapter ?: 0
return this.durChapterIndex
}
// dailyChapters 的 setter 和 getter
fun setDailyChapters(dailyChapters: Int) {
config.dailyChapters = dailyChapters
}
fun getDailyChapters(): Int {
return config.dailyChapters
}
fun getDelTag(tag: Long): Boolean { fun getDelTag(tag: Long): Boolean {
return config.delTag and tag == tag return config.delTag and tag == tag
} }
@@ -385,7 +437,11 @@ data class Book(
var useReplaceRule: Boolean? = null,// 正文使用净化替换规则 var useReplaceRule: Boolean? = null,// 正文使用净化替换规则
var delTag: Long = 0L,//去除标签 var delTag: Long = 0L,//去除标签
var ttsEngine: String? = null, var ttsEngine: String? = null,
var splitLongChapter: Boolean = true var splitLongChapter: Boolean = true,
var readSimulating: Boolean = false,
var startDate: LocalDate? = null,
var startChapter: Int? = null, // 用户设置的起始章节
var dailyChapters: Int = 3 // 用户设置的每日更新章节数
) : Parcelable ) : Parcelable
class Converters { class Converters {
@@ -165,7 +165,7 @@ object AudioPlay {
fun next(context: Context) { fun next(context: Context) {
book?.let { book -> book?.let { book ->
if (book.durChapterIndex + 1 < book.totalChapterNum) { if (book.durChapterIndex + 1 < book.simulatedTotalChapterNum()) {
book.durChapterIndex += 1 book.durChapterIndex += 1
book.durChapterPos = 0 book.durChapterPos = 0
durChapterIndex = book.durChapterIndex durChapterIndex = book.durChapterIndex
@@ -151,7 +151,7 @@ object ReadBook : CoroutineScope by MainScope() {
fun setProgress(progress: BookProgress) { fun setProgress(progress: BookProgress) {
if (progress.durChapterIndex < chapterSize && if (progress.durChapterIndex < chapterSize &&
(durChapterIndex != progress.durChapterIndex (durChapterIndex != progress.durChapterIndex
|| durChapterPos != progress.durChapterPos) || durChapterPos != progress.durChapterPos)
) { ) {
durChapterIndex = progress.durChapterIndex durChapterIndex = progress.durChapterIndex
durChapterPos = progress.durChapterPos durChapterPos = progress.durChapterPos
@@ -229,7 +229,7 @@ object ReadBook : CoroutineScope by MainScope() {
} }
fun moveToNextChapter(upContent: Boolean, upContentInPlace: Boolean = true): Boolean { fun moveToNextChapter(upContent: Boolean, upContentInPlace: Boolean = true): Boolean {
if (durChapterIndex < chapterSize - 1) { if (durChapterIndex < (book?.simulatedTotalChapterNum()?: chapterSize) - 1) {
durChapterPos = 0 durChapterPos = 0
durChapterIndex++ durChapterIndex++
prevTextChapter?.cancelLayout() prevTextChapter?.cancelLayout()
@@ -590,6 +590,8 @@ object ReadBook : CoroutineScope by MainScope() {
} }
} }
} }
return@async
}.onError { }.onError {
AppLog.put("ChapterProvider ERROR", it) AppLog.put("ChapterProvider ERROR", it)
appCtx.toastOnUi("ChapterProvider ERROR:\n${it.stackTraceStr}") appCtx.toastOnUi("ChapterProvider ERROR:\n${it.stackTraceStr}")
@@ -113,11 +113,11 @@ object BookChapterList {
if (chapterList.isEmpty()) { if (chapterList.isEmpty()) {
throw TocEmptyException(appCtx.getString(R.string.chapter_list_empty)) throw TocEmptyException(appCtx.getString(R.string.chapter_list_empty))
} }
//去重
if (!reverse) { if (!reverse) {
chapterList.reverse() chapterList.reverse()
} }
coroutineContext.ensureActive() coroutineContext.ensureActive()
//去重
val lh = LinkedHashSet(chapterList) val lh = LinkedHashSet(chapterList)
val list = ArrayList(lh) val list = ArrayList(lh)
if (!book.getReverseToc()) { if (!book.getReverseToc()) {
@@ -145,7 +145,8 @@ object BookChapterList {
} }
val replaceRules = ContentProcessor.get(book.name, book.origin).getTitleReplaceRules() val replaceRules = ContentProcessor.get(book.name, book.origin).getTitleReplaceRules()
book.latestChapterTitle = book.latestChapterTitle =
list.last().getDisplayTitle(replaceRules, book.getUseReplaceRule()) list.getOrElse(book.simulatedTotalChapterNum() - 1) {
list.last() }.getDisplayTitle(replaceRules, book.getUseReplaceRule())
book.durChapterTitle = list.getOrElse(book.durChapterIndex) { list.last() } book.durChapterTitle = list.getOrElse(book.durChapterIndex) { list.last() }
.getDisplayTitle(replaceRules, book.getUseReplaceRule()) .getDisplayTitle(replaceRules, book.getUseReplaceRule())
if (book.totalChapterNum < list.size) { if (book.totalChapterNum < list.size) {
@@ -36,13 +36,21 @@ import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.book.toc.TocActivityResult import io.legado.app.ui.book.toc.TocActivityResult
import io.legado.app.ui.login.SourceLoginActivity import io.legado.app.ui.login.SourceLoginActivity
import io.legado.app.ui.widget.seekbar.SeekBarChangeListener import io.legado.app.ui.widget.seekbar.SeekBarChangeListener
import io.legado.app.utils.* import io.legado.app.utils.StartActivityContract
import io.legado.app.utils.dpToPx
import io.legado.app.utils.invisible
import io.legado.app.utils.observeEvent
import io.legado.app.utils.observeEventSticky
import io.legado.app.utils.sendToClip
import io.legado.app.utils.showDialogFragment
import io.legado.app.utils.startActivity
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
import io.legado.app.utils.visible
import kotlinx.coroutines.Dispatchers.IO import kotlinx.coroutines.Dispatchers.IO
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import splitties.views.onLongClick import splitties.views.onLongClick
import java.util.* import java.util.Locale
/** /**
* 音频播放 * 音频播放
@@ -108,12 +116,14 @@ class AudioPlayActivity :
R.id.menu_change_source -> AudioPlay.book?.let { R.id.menu_change_source -> AudioPlay.book?.let {
showDialogFragment(ChangeBookSourceDialog(it.name, it.author)) showDialogFragment(ChangeBookSourceDialog(it.name, it.author))
} }
R.id.menu_login -> AudioPlay.bookSource?.let { R.id.menu_login -> AudioPlay.bookSource?.let {
startActivity<SourceLoginActivity> { startActivity<SourceLoginActivity> {
putExtra("type", "bookSource") putExtra("type", "bookSource")
putExtra("key", it.bookSourceUrl) putExtra("key", it.bookSourceUrl)
} }
} }
R.id.menu_wake_lock -> AppConfig.audioPlayUseWakeLock = !AppConfig.audioPlayUseWakeLock R.id.menu_wake_lock -> AppConfig.audioPlayUseWakeLock = !AppConfig.audioPlayUseWakeLock
R.id.menu_copy_audio_url -> sendToClip(AudioPlayService.url) R.id.menu_copy_audio_url -> sendToClip(AudioPlayService.url)
R.id.menu_edit_source -> AudioPlay.bookSource?.let { R.id.menu_edit_source -> AudioPlay.bookSource?.let {
@@ -121,6 +131,7 @@ class AudioPlayActivity :
putExtra("sourceUrl", it.bookSourceUrl) putExtra("sourceUrl", it.bookSourceUrl)
} }
} }
R.id.menu_log -> showDialogFragment<AppLogDialog>() R.id.menu_log -> showDialogFragment<AppLogDialog>()
} }
return super.onCompatOptionsItemSelected(item) return super.onCompatOptionsItemSelected(item)
@@ -258,7 +269,8 @@ class AudioPlayActivity :
binding.tvSubTitle.text = it binding.tvSubTitle.text = it
AudioPlay.book?.let { book -> AudioPlay.book?.let { book ->
binding.ivSkipPrevious.isEnabled = book.durChapterIndex > 0 binding.ivSkipPrevious.isEnabled = book.durChapterIndex > 0
binding.ivSkipNext.isEnabled = book.durChapterIndex < book.totalChapterNum - 1 binding.ivSkipNext.isEnabled =
book.durChapterIndex < book.simulatedTotalChapterNum() - 1
} }
} }
observeEventSticky<Int>(EventBus.AUDIO_SIZE) { observeEventSticky<Int>(EventBus.AUDIO_SIZE) {
@@ -251,7 +251,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
appDb.bookChapterDao.insert(*it.toTypedArray()) appDb.bookChapterDao.insert(*it.toTypedArray())
if (book.isSameNameAuthor(ReadBook.book)) { if (book.isSameNameAuthor(ReadBook.book)) {
ReadBook.book = book ReadBook.book = book
ReadBook.chapterSize = book.totalChapterNum ReadBook.chapterSize = book.simulatedTotalChapterNum()
} }
} }
bookData.postValue(book) bookData.postValue(book)
@@ -1,6 +1,7 @@
package io.legado.app.ui.book.read package io.legado.app.ui.book.read
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.DatePickerDialog
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@@ -20,6 +21,7 @@ import io.legado.app.constant.PreferKey
import io.legado.app.databinding.ActivityBookReadBinding import io.legado.app.databinding.ActivityBookReadBinding
import io.legado.app.databinding.DialogDownloadChoiceBinding import io.legado.app.databinding.DialogDownloadChoiceBinding
import io.legado.app.databinding.DialogEditTextBinding import io.legado.app.databinding.DialogEditTextBinding
import io.legado.app.databinding.DialogSimulatedReadingBinding
import io.legado.app.help.config.AppConfig import io.legado.app.help.config.AppConfig
import io.legado.app.help.config.LocalConfig import io.legado.app.help.config.LocalConfig
import io.legado.app.help.config.ReadBookConfig import io.legado.app.help.config.ReadBookConfig
@@ -48,6 +50,8 @@ import io.legado.app.utils.setNavigationBarColorAuto
import io.legado.app.utils.showDialogFragment import io.legado.app.utils.showDialogFragment
import io.legado.app.utils.viewbindingdelegate.viewBinding import io.legado.app.utils.viewbindingdelegate.viewBinding
import io.legado.app.utils.visible import io.legado.app.utils.visible
import java.time.LocalDate
import java.time.format.DateTimeFormatter
/** /**
* 阅读界面 * 阅读界面
@@ -57,6 +61,7 @@ abstract class BaseReadBookActivity :
override val binding by viewBinding(ActivityBookReadBinding::inflate) override val binding by viewBinding(ActivityBookReadBinding::inflate)
override val viewModel by viewModels<ReadBookViewModel>() override val viewModel by viewModels<ReadBookViewModel>()
var bottomDialog = 0 var bottomDialog = 0
set(value) { set(value) {
if (field != value) { if (field != value) {
@@ -304,6 +309,59 @@ abstract class BaseReadBookActivity :
} }
} }
fun showSimulatedReading() {
val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
ReadBook.book?.let { book ->
alert(titleResource = R.string.simulated_reading) {
val alertBinding = DialogSimulatedReadingBinding.inflate(layoutInflater).apply {
root.setBackgroundColor(root.context.backgroundColor)
srEnabled.isChecked = book.getReadSimulating()
editStart.setText(book.getStartChapter().toString())
editNum.setText(book.getDailyChapters().toString())
startDate.setText(book.getStartDate()?.format(dateFormatter))
startDate.isFocusable = false // 设置为false,不允许获得焦点
startDate.isCursorVisible = false // 不显示光标
startDate.setOnClickListener {
// 获取当前日期
val localStartDate = LocalDate.parse(startDate.text)
// 创建 DatePickerDialog
val datePickerDialog = DatePickerDialog(
root.context,
{ _, yy, mm, dayOfMonth ->
// 使用Java 8的日期和时间API来格式化日期
val date = LocalDate.of(yy, mm + 1, dayOfMonth) // Java 8的LocalDate,月份从1开始
val formattedDate = date.format(dateFormatter)
startDate.setText(formattedDate)
}, localStartDate.year, localStartDate.monthValue - 1, localStartDate.dayOfMonth
)
datePickerDialog.show()
}
}
customView { alertBinding.root }
yesButton {
alertBinding.run {
val start = editStart.text!!.toString().let {
if (it.isEmpty()) 0 else it.toInt()
}
val num = editNum.text!!.toString().let {
if (it.isEmpty()) book.totalChapterNum else it.toInt()
}
val enabled = srEnabled.isChecked
val date = startDate.text!!.toString().let {
if (it.isEmpty()) LocalDate.now() else LocalDate.parse(it, dateFormatter)
}
book.setStartDate(date)
book.setDailyChapters(num)
book.setStartChapter(start)
book.setReadSimulating(enabled)
book.save()
}
}
noButton()
}
}
}
fun showCharsetConfig() { fun showCharsetConfig() {
alert(R.string.set_charset) { alert(R.string.set_charset) {
val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply { val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
@@ -473,6 +473,7 @@ class ReadBookActivity : BaseReadBookActivity(),
R.id.menu_download -> showDownloadDialog() R.id.menu_download -> showDownloadDialog()
R.id.menu_add_bookmark -> addBookmark() R.id.menu_add_bookmark -> addBookmark()
R.id.menu_simulated_reading -> showSimulatedReading()
R.id.menu_edit_content -> showDialogFragment(ContentEditDialog()) R.id.menu_edit_content -> showDialogFragment(ContentEditDialog())
R.id.menu_update_toc -> ReadBook.book?.let { R.id.menu_update_toc -> ReadBook.book?.let {
if (it.isEpub) { if (it.isEpub) {
@@ -233,7 +233,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
&& progress.durChapterPos < book.durChapterPos) && progress.durChapterPos < book.durChapterPos)
) { ) {
alertSync?.invoke(progress) alertSync?.invoke(progress)
} else { } else if (progress.durChapterIndex < book.simulatedTotalChapterNum()) {
ReadBook.setProgress(progress) ReadBook.setProgress(progress)
AppLog.put("自动同步阅读进度成功《${book.name}》 ${progress.durChapterTitle}") AppLog.put("自动同步阅读进度成功《${book.name}》 ${progress.durChapterTitle}")
} }
@@ -708,7 +708,8 @@ class ReadView(context: Context, attrs: AttributeSet) :
} }
override fun hasNextChapter(): Boolean { override fun hasNextChapter(): Boolean {
return ReadBook.durChapterIndex < ReadBook.chapterSize - 1 return ReadBook.durChapterIndex < ((ReadBook.book?.simulatedTotalChapterNum()
?: ReadBook.chapterSize) - 1)
} }
override fun hasPrevChapter(): Boolean { override fun hasPrevChapter(): Boolean {
@@ -118,7 +118,10 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
lifecycleScope.launch { lifecycleScope.launch {
withContext(IO) { withContext(IO) {
when { when {
searchKey.isNullOrBlank() -> appDb.bookChapterDao.getChapterList(viewModel.bookUrl) searchKey.isNullOrBlank() -> appDb.bookChapterDao.getChapterList(
viewModel.bookUrl
).take(book?.simulatedTotalChapterNum() ?: Int.MAX_VALUE)
else -> appDb.bookChapterDao.search(viewModel.bookUrl, searchKey) else -> appDb.bookChapterDao.search(viewModel.bookUrl, searchKey)
} }
}.let { }.let {
@@ -163,9 +166,10 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
override fun openChapter(bookChapter: BookChapter) { override fun openChapter(bookChapter: BookChapter) {
activity?.run { activity?.run {
setResult(RESULT_OK, Intent() setResult(
.putExtra("index", bookChapter.index) RESULT_OK, Intent()
.putExtra("chapterChanged", bookChapter.index != durChapterIndex) .putExtra("index", bookChapter.index)
.putExtra("chapterChanged", bookChapter.index != durChapterIndex)
) )
finish() finish()
} }
@@ -192,7 +192,7 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>(),
ReadBook.book?.let { readBook -> ReadBook.book?.let { readBook ->
if (readBook == book) { if (readBook == book) {
ReadBook.book = book ReadBook.book = book
ReadBook.chapterSize = book.totalChapterNum ReadBook.chapterSize = book.simulatedTotalChapterNum()
ReadBook.upMsg(null) ReadBook.upMsg(null)
ReadBook.loadContent(resetPageOffset = true) ReadBook.loadContent(resetPageOffset = true)
} }
@@ -163,7 +163,7 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
appDb.bookChapterDao.insert(*toc.toTypedArray()) appDb.bookChapterDao.insert(*toc.toTypedArray())
if (book.isSameNameAuthor(ReadBook.book)) { if (book.isSameNameAuthor(ReadBook.book)) {
ReadBook.book = book ReadBook.book = book
ReadBook.chapterSize = book.totalChapterNum ReadBook.chapterSize = book.simulatedTotalChapterNum()
} }
addDownload(source, book) addDownload(source, book)
}.onFailure { }.onFailure {
@@ -0,0 +1,185 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="20dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="60dp"
android:gravity="center"
android:orientation="horizontal"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="2dp"
android:text="@string/switch_on"
android:textColor="@color/primaryText"
android:textSize="16sp"
tools:ignore="RtlHardcoded"
android:minWidth="88dp" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="53dp"
android:layout_height="match_parent"
android:gravity="center_vertical">
<androidx.appcompat.widget.SwitchCompat
android:layout_height="wrap_content"
android:id="@+id/sr_enabled"
android:layout_weight="1"
android:gravity="start|center_vertical"
style="@style/Widget.AppCompat.CompoundButton.Switch"
android:layout_width="match_parent"
android:width="20dp" />
</LinearLayout>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="65dp"
android:orientation="horizontal"
android:padding="16dp"
android:gravity="start|center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/start_from"
android:textColor="@color/primaryText"
android:textSize="16sp"
android:gravity="center_vertical"
android:minWidth="100dp" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<EditText
android:layout_width="match_parent"
android:layout_height="49dp"
android:inputType="none"
android:ems="10"
android:id="@+id/start_date"
android:hint="Select date"
android:layout_weight="1"
android:clickable="true"
android:textSize="16sp"
android:layout_marginRight="68dp" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="63dp"
android:gravity="center_horizontal|left"
android:padding="16dp">
<LinearLayout
android:orientation="horizontal"
android:layout_width="100dp"
android:layout_height="match_parent"
android:minWidth="100dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginRight="2dp"
android:text="@string/start_chapter"
android:textColor="@color/primaryText"
android:textSize="16sp"
tools:ignore="RtlHardcoded" />
</LinearLayout>
<EditText
android:id="@+id/edit_start"
android:layout_width="47dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/bg_edit"
android:hint="@string/start"
android:importantForAutofill="no"
android:inputType="number"
android:lines="1"
android:maxLength="5"
android:minWidth="60dp"
android:paddingLeft="5dp"
android:paddingTop="4dp"
android:paddingRight="5dp"
android:paddingBottom="4dp"
android:textColor="@color/primaryText"
android:textCursorDrawable="@drawable/shape_text_cursor"
android:textSize="14sp"
tools:ignore="TouchTargetSizeCheck,TextContrastCheck" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="end">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="25dp"
android:layout_marginRight="5dp"
android:text="@string/daily_chapters"
android:textColor="@color/primaryText"
android:textSize="16sp" />
<EditText
android:id="@+id/edit_num"
android:layout_width="42dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/bg_edit"
android:hint="3"
android:importantForAutofill="no"
android:inputType="number"
android:lines="1"
android:maxLength="5"
android:minWidth="60dp"
android:paddingLeft="5dp"
android:paddingTop="4dp"
android:paddingRight="5dp"
android:paddingBottom="4dp"
android:textColor="@color/primaryText"
android:textCursorDrawable="@drawable/shape_text_cursor"
android:textSize="14sp"
tools:ignore="TouchTargetSizeCheck,SpeakableTextPresentCheck,TextContrastCheck"
android:textAlignment="center" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
+5
View File
@@ -70,6 +70,11 @@
android:title="@string/reverse_content" android:title="@string/reverse_content"
app:showAsAction="never" /> app:showAsAction="never" />
<item
android:id="@+id/menu_simulated_reading"
android:title="@string/simulated_reading"
app:showAsAction="never" />
<item <item
android:id="@+id/menu_enable_replace" android:id="@+id/menu_enable_replace"
android:checkable="true" android:checkable="true"
@@ -1148,4 +1148,9 @@
<string name="keep_swipe_tip">继续滑动以加载下一章…</string> <string name="keep_swipe_tip">继续滑动以加载下一章…</string>
<string name="enable_optimize_render">启用绘制优化</string> <string name="enable_optimize_render">启用绘制优化</string>
<string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string> <string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string>
<string name="simulated_reading">Lectura Ficticia</string>
<string name="switch_on">Enc/Apag</string>
<string name="start_from">Comenzar</string>
<string name="daily_chapters">Cap. al día</string>
<string name="start_chapter">Cap. inicio</string>
</resources> </resources>
@@ -1151,4 +1151,9 @@
<string name="keep_swipe_tip">继续滑动以加载下一章…</string> <string name="keep_swipe_tip">继续滑动以加载下一章…</string>
<string name="enable_optimize_render">启用绘制优化</string> <string name="enable_optimize_render">启用绘制优化</string>
<string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string> <string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string>
<string name="simulated_reading">模擬追読</string>
<string name="switch_on">スイッチ</string>
<string name="start_from">開始日</string>
<string name="daily_chapters">日更の章数</string>
<string name="start_chapter">開始の章</string>
</resources> </resources>
@@ -1151,4 +1151,9 @@
<string name="keep_swipe_tip">继续滑动以加载下一章…</string> <string name="keep_swipe_tip">继续滑动以加载下一章…</string>
<string name="enable_optimize_render">启用绘制优化</string> <string name="enable_optimize_render">启用绘制优化</string>
<string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string> <string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string>
<string name="simulated_reading">Leitura Fingida</string>
<string name="switch_on">Lig/Desl</string>
<string name="start_from">Começar</string>
<string name="daily_chapters">Cap. por dia</string>
<string name="start_chapter">Início</string>
</resources> </resources>
+5
View File
@@ -1147,4 +1147,9 @@ Còn </string>
<string name="keep_swipe_tip">继续滑动以加载下一章…</string> <string name="keep_swipe_tip">继续滑动以加载下一章…</string>
<string name="enable_optimize_render">启用绘制优化</string> <string name="enable_optimize_render">启用绘制优化</string>
<string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string> <string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string>
<string name="simulated_reading">Đọc giả lập</string>
<string name="switch_on">Bật</string>
<string name="start_from">Bắt đầu</string>
<string name="daily_chapters">Chương/ngày</string>
<string name="start_chapter">Chương đầu</string>
</resources> </resources>
@@ -1148,4 +1148,9 @@
<string name="keep_swipe_tip">继续滑动以加载下一章…</string> <string name="keep_swipe_tip">继续滑动以加载下一章…</string>
<string name="enable_optimize_render">启用绘制优化</string> <string name="enable_optimize_render">启用绘制优化</string>
<string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string> <string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string>
<string name="simulated_reading">模擬追讀</string>
<string name="switch_on">開關</string>
<string name="start_from">起始日期</string>
<string name="daily_chapters">日更章數</string>
<string name="start_chapter">開始篇章</string>
</resources> </resources>
@@ -1150,4 +1150,9 @@
<string name="keep_swipe_tip">继续滑动以加载下一章…</string> <string name="keep_swipe_tip">继续滑动以加载下一章…</string>
<string name="enable_optimize_render">启用绘制优化</string> <string name="enable_optimize_render">启用绘制优化</string>
<string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string> <string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string>
<string name="simulated_reading">模擬追讀</string>
<string name="switch_on">開關</string>
<string name="start_from">起始日期</string>
<string name="daily_chapters">日更章數</string>
<string name="start_chapter">開始篇章</string>
</resources> </resources>
+5
View File
@@ -1150,4 +1150,9 @@
<string name="keep_swipe_tip">继续滑动以加载下一章…</string> <string name="keep_swipe_tip">继续滑动以加载下一章…</string>
<string name="enable_optimize_render">启用绘制优化</string> <string name="enable_optimize_render">启用绘制优化</string>
<string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string> <string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string>
<string name="simulated_reading">模拟追读</string>
<string name="switch_on">开关</string>
<string name="start_from">开始日期</string>
<string name="daily_chapters">日更章数</string>
<string name="start_chapter">起始章节</string>
</resources> </resources>
+5
View File
@@ -1151,4 +1151,9 @@
<string name="keep_swipe_tip">Keep swiping to load the next chapter…</string> <string name="keep_swipe_tip">Keep swiping to load the next chapter…</string>
<string name="enable_optimize_render">启用绘制优化</string> <string name="enable_optimize_render">启用绘制优化</string>
<string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string> <string name="ignore_battery_permission_rationale">阅读需要请求后台权限以保持服务正常运行</string>
<string name="simulated_reading">Simulated Reading</string>
<string name="switch_on">Switch</string>
<string name="start_from">Start from</string>
<string name="daily_chapters">Daily Chapters</string>
<string name="start_chapter">Start Chapter</string>
</resources> </resources>