mirror of
https://github.com/gedoor/legado.git
synced 2025-08-10 00:52:30 +00:00
Merge pull request #4021 from xuanyue202/simulated_reading
feat(app): 增加模拟追读功能
This commit is contained in:
@@ -16,18 +16,18 @@ import io.legado.app.help.book.BookHelp
|
||||
import io.legado.app.help.book.ContentProcessor
|
||||
import io.legado.app.help.book.isEpub
|
||||
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.config.AppConfig
|
||||
import io.legado.app.help.config.ReadBookConfig
|
||||
import io.legado.app.model.ReadBook
|
||||
import io.legado.app.model.localBook.LocalBook
|
||||
import io.legado.app.utils.GSON
|
||||
import io.legado.app.utils.MD5Utils
|
||||
import io.legado.app.utils.fromJsonObject
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.nio.charset.Charset
|
||||
import java.time.LocalDate
|
||||
import java.time.Period.between
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@@ -160,9 +160,21 @@ data class Book(
|
||||
@IgnoredOnParcel
|
||||
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 getUnreadChapterNum() = max(totalChapterNum - durChapterIndex - 1, 0)
|
||||
fun getUnreadChapterNum() = max(simulatedTotalChapterNum() - durChapterIndex - 1, 0)
|
||||
|
||||
fun getDisplayCover() = if (customCoverUrl.isNullOrEmpty()) coverUrl else customCoverUrl
|
||||
|
||||
@@ -257,6 +269,46 @@ data class Book(
|
||||
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 {
|
||||
return config.delTag and tag == tag
|
||||
}
|
||||
@@ -385,7 +437,11 @@ data class Book(
|
||||
var useReplaceRule: Boolean? = null,// 正文使用净化替换规则
|
||||
var delTag: Long = 0L,//去除标签
|
||||
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
|
||||
|
||||
class Converters {
|
||||
|
||||
@@ -165,7 +165,7 @@ object AudioPlay {
|
||||
|
||||
fun next(context: Context) {
|
||||
book?.let { book ->
|
||||
if (book.durChapterIndex + 1 < book.totalChapterNum) {
|
||||
if (book.durChapterIndex + 1 < book.simulatedTotalChapterNum()) {
|
||||
book.durChapterIndex += 1
|
||||
book.durChapterPos = 0
|
||||
durChapterIndex = book.durChapterIndex
|
||||
|
||||
@@ -151,7 +151,7 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
fun setProgress(progress: BookProgress) {
|
||||
if (progress.durChapterIndex < chapterSize &&
|
||||
(durChapterIndex != progress.durChapterIndex
|
||||
|| durChapterPos != progress.durChapterPos)
|
||||
|| durChapterPos != progress.durChapterPos)
|
||||
) {
|
||||
durChapterIndex = progress.durChapterIndex
|
||||
durChapterPos = progress.durChapterPos
|
||||
@@ -229,7 +229,7 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
}
|
||||
|
||||
fun moveToNextChapter(upContent: Boolean, upContentInPlace: Boolean = true): Boolean {
|
||||
if (durChapterIndex < chapterSize - 1) {
|
||||
if (durChapterIndex < (book?.simulatedTotalChapterNum()?: chapterSize) - 1) {
|
||||
durChapterPos = 0
|
||||
durChapterIndex++
|
||||
prevTextChapter?.cancelLayout()
|
||||
@@ -590,6 +590,8 @@ object ReadBook : CoroutineScope by MainScope() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return@async
|
||||
}.onError {
|
||||
AppLog.put("ChapterProvider ERROR", it)
|
||||
appCtx.toastOnUi("ChapterProvider ERROR:\n${it.stackTraceStr}")
|
||||
|
||||
@@ -113,11 +113,11 @@ object BookChapterList {
|
||||
if (chapterList.isEmpty()) {
|
||||
throw TocEmptyException(appCtx.getString(R.string.chapter_list_empty))
|
||||
}
|
||||
//去重
|
||||
if (!reverse) {
|
||||
chapterList.reverse()
|
||||
}
|
||||
coroutineContext.ensureActive()
|
||||
//去重
|
||||
val lh = LinkedHashSet(chapterList)
|
||||
val list = ArrayList(lh)
|
||||
if (!book.getReverseToc()) {
|
||||
@@ -145,7 +145,8 @@ object BookChapterList {
|
||||
}
|
||||
val replaceRules = ContentProcessor.get(book.name, book.origin).getTitleReplaceRules()
|
||||
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() }
|
||||
.getDisplayTitle(replaceRules, book.getUseReplaceRule())
|
||||
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.login.SourceLoginActivity
|
||||
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.visible
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
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 {
|
||||
showDialogFragment(ChangeBookSourceDialog(it.name, it.author))
|
||||
}
|
||||
|
||||
R.id.menu_login -> AudioPlay.bookSource?.let {
|
||||
startActivity<SourceLoginActivity> {
|
||||
putExtra("type", "bookSource")
|
||||
putExtra("key", it.bookSourceUrl)
|
||||
}
|
||||
}
|
||||
|
||||
R.id.menu_wake_lock -> AppConfig.audioPlayUseWakeLock = !AppConfig.audioPlayUseWakeLock
|
||||
R.id.menu_copy_audio_url -> sendToClip(AudioPlayService.url)
|
||||
R.id.menu_edit_source -> AudioPlay.bookSource?.let {
|
||||
@@ -121,6 +131,7 @@ class AudioPlayActivity :
|
||||
putExtra("sourceUrl", it.bookSourceUrl)
|
||||
}
|
||||
}
|
||||
|
||||
R.id.menu_log -> showDialogFragment<AppLogDialog>()
|
||||
}
|
||||
return super.onCompatOptionsItemSelected(item)
|
||||
@@ -258,7 +269,8 @@ class AudioPlayActivity :
|
||||
binding.tvSubTitle.text = it
|
||||
AudioPlay.book?.let { book ->
|
||||
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) {
|
||||
|
||||
@@ -251,7 +251,7 @@ class BookInfoViewModel(application: Application) : BaseViewModel(application) {
|
||||
appDb.bookChapterDao.insert(*it.toTypedArray())
|
||||
if (book.isSameNameAuthor(ReadBook.book)) {
|
||||
ReadBook.book = book
|
||||
ReadBook.chapterSize = book.totalChapterNum
|
||||
ReadBook.chapterSize = book.simulatedTotalChapterNum()
|
||||
}
|
||||
}
|
||||
bookData.postValue(book)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.legado.app.ui.book.read
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.DatePickerDialog
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.os.Build
|
||||
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.DialogDownloadChoiceBinding
|
||||
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.LocalConfig
|
||||
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.viewbindingdelegate.viewBinding
|
||||
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 viewModel by viewModels<ReadBookViewModel>()
|
||||
|
||||
var bottomDialog = 0
|
||||
set(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() {
|
||||
alert(R.string.set_charset) {
|
||||
val alertBinding = DialogEditTextBinding.inflate(layoutInflater).apply {
|
||||
|
||||
@@ -473,6 +473,7 @@ class ReadBookActivity : BaseReadBookActivity(),
|
||||
|
||||
R.id.menu_download -> showDownloadDialog()
|
||||
R.id.menu_add_bookmark -> addBookmark()
|
||||
R.id.menu_simulated_reading -> showSimulatedReading()
|
||||
R.id.menu_edit_content -> showDialogFragment(ContentEditDialog())
|
||||
R.id.menu_update_toc -> ReadBook.book?.let {
|
||||
if (it.isEpub) {
|
||||
|
||||
@@ -233,7 +233,7 @@ class ReadBookViewModel(application: Application) : BaseViewModel(application) {
|
||||
&& progress.durChapterPos < book.durChapterPos)
|
||||
) {
|
||||
alertSync?.invoke(progress)
|
||||
} else {
|
||||
} else if (progress.durChapterIndex < book.simulatedTotalChapterNum()) {
|
||||
ReadBook.setProgress(progress)
|
||||
AppLog.put("自动同步阅读进度成功《${book.name}》 ${progress.durChapterTitle}")
|
||||
}
|
||||
|
||||
@@ -708,7 +708,8 @@ class ReadView(context: Context, attrs: AttributeSet) :
|
||||
}
|
||||
|
||||
override fun hasNextChapter(): Boolean {
|
||||
return ReadBook.durChapterIndex < ReadBook.chapterSize - 1
|
||||
return ReadBook.durChapterIndex < ((ReadBook.book?.simulatedTotalChapterNum()
|
||||
?: ReadBook.chapterSize) - 1)
|
||||
}
|
||||
|
||||
override fun hasPrevChapter(): Boolean {
|
||||
|
||||
@@ -118,7 +118,10 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
|
||||
lifecycleScope.launch {
|
||||
withContext(IO) {
|
||||
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)
|
||||
}
|
||||
}.let {
|
||||
@@ -163,9 +166,10 @@ class ChapterListFragment : VMBaseFragment<TocViewModel>(R.layout.fragment_chapt
|
||||
|
||||
override fun openChapter(bookChapter: BookChapter) {
|
||||
activity?.run {
|
||||
setResult(RESULT_OK, Intent()
|
||||
.putExtra("index", bookChapter.index)
|
||||
.putExtra("chapterChanged", bookChapter.index != durChapterIndex)
|
||||
setResult(
|
||||
RESULT_OK, Intent()
|
||||
.putExtra("index", bookChapter.index)
|
||||
.putExtra("chapterChanged", bookChapter.index != durChapterIndex)
|
||||
)
|
||||
finish()
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ class TocActivity : VMBaseActivity<ActivityChapterListBinding, TocViewModel>(),
|
||||
ReadBook.book?.let { readBook ->
|
||||
if (readBook == book) {
|
||||
ReadBook.book = book
|
||||
ReadBook.chapterSize = book.totalChapterNum
|
||||
ReadBook.chapterSize = book.simulatedTotalChapterNum()
|
||||
ReadBook.upMsg(null)
|
||||
ReadBook.loadContent(resetPageOffset = true)
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ class MainViewModel(application: Application) : BaseViewModel(application) {
|
||||
appDb.bookChapterDao.insert(*toc.toTypedArray())
|
||||
if (book.isSameNameAuthor(ReadBook.book)) {
|
||||
ReadBook.book = book
|
||||
ReadBook.chapterSize = book.totalChapterNum
|
||||
ReadBook.chapterSize = book.simulatedTotalChapterNum()
|
||||
}
|
||||
addDownload(source, book)
|
||||
}.onFailure {
|
||||
|
||||
@@ -83,4 +83,4 @@ abstract class BaseBooksAdapter<VB : ViewBinding>(context: Context) :
|
||||
fun openBookInfo(book: Book)
|
||||
fun isUpdate(bookUrl: String): Boolean
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
185
app/src/main/res/layout/dialog_simulated_reading.xml
Normal file
185
app/src/main/res/layout/dialog_simulated_reading.xml
Normal file
@@ -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>
|
||||
@@ -70,6 +70,11 @@
|
||||
android:title="@string/reverse_content"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_simulated_reading"
|
||||
android:title="@string/simulated_reading"
|
||||
app:showAsAction="never" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_enable_replace"
|
||||
android:checkable="true"
|
||||
|
||||
@@ -683,7 +683,7 @@
|
||||
<string name="alouding_disable">The selected text cannot be spoken in full text speech</string>
|
||||
<string name="read_body_to_lh">Extend to cutout</string>
|
||||
<string name="toc_updateing">Updating Chapters</string>
|
||||
|
||||
|
||||
<string name="media_button_on_exit_title">Headset buttons are always available</string>
|
||||
<string name="media_button_on_exit_summary">Headset buttons are available even exit the app.</string>
|
||||
<string name="contributors">Contributors</string>
|
||||
@@ -1148,4 +1148,9 @@
|
||||
<string name="keep_swipe_tip">继续滑动以加载下一章…</string>
|
||||
<string name="enable_optimize_render">启用绘制优化</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>
|
||||
|
||||
@@ -1151,4 +1151,9 @@
|
||||
<string name="keep_swipe_tip">继续滑动以加载下一章…</string>
|
||||
<string name="enable_optimize_render">启用绘制优化</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>
|
||||
|
||||
@@ -1151,4 +1151,9 @@
|
||||
<string name="keep_swipe_tip">继续滑动以加载下一章…</string>
|
||||
<string name="enable_optimize_render">启用绘制优化</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>
|
||||
|
||||
@@ -1147,4 +1147,9 @@ Còn </string>
|
||||
<string name="keep_swipe_tip">继续滑动以加载下一章…</string>
|
||||
<string name="enable_optimize_render">启用绘制优化</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>
|
||||
|
||||
@@ -1148,4 +1148,9 @@
|
||||
<string name="keep_swipe_tip">继续滑动以加载下一章…</string>
|
||||
<string name="enable_optimize_render">启用绘制优化</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>
|
||||
|
||||
@@ -1150,4 +1150,9 @@
|
||||
<string name="keep_swipe_tip">继续滑动以加载下一章…</string>
|
||||
<string name="enable_optimize_render">启用绘制优化</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>
|
||||
|
||||
@@ -1150,4 +1150,9 @@
|
||||
<string name="keep_swipe_tip">继续滑动以加载下一章…</string>
|
||||
<string name="enable_optimize_render">启用绘制优化</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>
|
||||
|
||||
@@ -1151,4 +1151,9 @@
|
||||
<string name="keep_swipe_tip">Keep swiping to load the next chapter…</string>
|
||||
<string name="enable_optimize_render">启用绘制优化</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>
|
||||
|
||||
Reference in New Issue
Block a user