模拟追读界面与功能 #131

This commit is contained in:
HapeLee committed 2025-10-24 01:52:54 +08:00
1 parent 135657ac83
commit a73e3db0fc
4 files changed
+114 -159

No files matched your search

@@ -265,14 +265,19 @@ data class Book(
// startDate 的 setter 和 getter // startDate 的 setter 和 getter
fun setStartDate(startDate: LocalDate?) { fun setStartDate(startDate: LocalDate?) {
config.startDate = startDate config.startDate = startDate?.toString()
} }
fun getStartDate(): LocalDate? { fun getStartDate(): LocalDate? {
if (!config.readSimulating || config.startDate == null) { if (!config.readSimulating || config.startDate == null) {
return LocalDate.now() return LocalDate.now()
} }
return config.startDate return try {
LocalDate.parse(config.startDate)
} catch (e: Exception) {
println("解析日期失败: ${config.startDate}, 错误: ${e.message}")
LocalDate.now()
}
} }
// startChapter 的 setter 和 getter // startChapter 的 setter 和 getter
@@ -400,7 +405,7 @@ data class Book(
var ttsEngine: String? = null, var ttsEngine: String? = null,
var splitLongChapter: Boolean = true, var splitLongChapter: Boolean = true,
var readSimulating: Boolean = false, var readSimulating: Boolean = false,
var startDate: LocalDate? = null, var startDate: String? = null,
var startChapter: Int? = null, // 用户设置的起始章节 var startChapter: Int? = null, // 用户设置的起始章节
var dailyChapters: Int = 3, // 用户设置的每日更新章节数 var dailyChapters: Int = 3, // 用户设置的每日更新章节数
@@ -28,7 +28,7 @@ import io.legato.kazusa.utils.toastOnUi
import splitties.init.appCtx import splitties.init.appCtx
import java.io.File import java.io.File
import java.time.LocalDate import java.time.LocalDate
import java.time.Period.between import java.time.temporal.ChronoUnit
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
@@ -353,7 +353,17 @@ fun Book.getExportFileName(
fun Book.simulatedTotalChapterNum(): Int { fun Book.simulatedTotalChapterNum(): Int {
return if (readSimulating()) { return if (readSimulating()) {
val currentDate = LocalDate.now() val currentDate = LocalDate.now()
val daysPassed = between(config.startDate, currentDate).days + 1 val daysPassed = if (config.startDate != null) {
try {
val startDate = LocalDate.parse(config.startDate)
ChronoUnit.DAYS.between(startDate, currentDate).toInt() + 1
} catch (e: Exception) {
println("解析起始日期失败: ${config.startDate}, 错误: ${e.message}")
1 // 解析失败时返回默认值1
}
} else {
1 // 没有设置起始日期时返回默认值1
}
// 计算当前应该解锁到哪一章 // 计算当前应该解锁到哪一章
val chaptersToUnlock = val chaptersToUnlock =
max(0, (config.startChapter ?: 0) + (daysPassed * config.dailyChapters)) max(0, (config.startChapter ?: 0) + (daysPassed * config.dailyChapters))
@@ -1,7 +1,6 @@
package io.legato.kazusa.ui.book.read package io.legato.kazusa.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
@@ -10,10 +9,12 @@ import android.view.View
import android.view.WindowInsets import android.view.WindowInsets
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.doOnDetach import androidx.core.view.doOnDetach
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import com.google.android.material.datepicker.MaterialDatePicker
import io.legato.kazusa.R import io.legato.kazusa.R
import io.legato.kazusa.base.VMBaseActivity import io.legato.kazusa.base.VMBaseActivity
import io.legato.kazusa.constant.AppConst.charsets import io.legato.kazusa.constant.AppConst.charsets
@@ -27,7 +28,6 @@ import io.legato.kazusa.help.config.LocalConfig
import io.legato.kazusa.help.config.ReadBookConfig import io.legato.kazusa.help.config.ReadBookConfig
import io.legato.kazusa.lib.dialogs.alert import io.legato.kazusa.lib.dialogs.alert
import io.legato.kazusa.lib.dialogs.selector import io.legato.kazusa.lib.dialogs.selector
//import io.legado.app.lib.theme.bottomBackground
import io.legato.kazusa.model.CacheBook import io.legato.kazusa.model.CacheBook
import io.legato.kazusa.model.ReadBook import io.legato.kazusa.model.ReadBook
import io.legato.kazusa.ui.book.read.config.BgTextConfigDialog import io.legato.kazusa.ui.book.read.config.BgTextConfigDialog
@@ -47,7 +47,9 @@ import io.legato.kazusa.utils.setOnApplyWindowInsetsListenerCompat
import io.legato.kazusa.utils.showDialogFragment import io.legato.kazusa.utils.showDialogFragment
import io.legato.kazusa.utils.themeColor import io.legato.kazusa.utils.themeColor
import io.legato.kazusa.utils.viewbindingdelegate.viewBinding import io.legato.kazusa.utils.viewbindingdelegate.viewBinding
import java.time.Instant
import java.time.LocalDate import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
/** /**
@@ -288,46 +290,57 @@ abstract class BaseReadBookActivity :
fun showSimulatedReading() { fun showSimulatedReading() {
val book = ReadBook.book ?: return val book = ReadBook.book ?: return
val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd")
val alertBinding = DialogSimulatedReadingBinding.inflate(layoutInflater).apply { val alertBinding = DialogSimulatedReadingBinding.inflate(layoutInflater).apply {
srEnabled.isChecked = book.getReadSimulating() srEnabled.isChecked = book.getReadSimulating()
editStart.setText(book.getStartChapter().toString()) editStart.setText(book.getStartChapter().toString())
editNum.setText(book.getDailyChapters().toString()) editNum.setText(book.getDailyChapters().toString())
startDate.setText(book.getStartDate()?.format(dateFormatter))
startDate.isFocusable = false // 设置为false,不允许获得焦点 // 安全地设置初始日期
startDate.isCursorVisible = false // 不显示光标 val safeDate = book.getStartDate() ?: LocalDate.now()
startDate.setText(safeDate.format(dateFormatter))
// 让 EditText 不可直接编辑,只能通过选择器
startDate.isFocusable = false
startDate.isCursorVisible = false
startDate.setOnClickListener { startDate.setOnClickListener {
// 获取当前日期 val currentDate = try {
val localStartDate = LocalDate.parse(startDate.text) LocalDate.parse(startDate.text.toString(), dateFormatter)
// 创建 DatePickerDialog } catch (e: Exception) {
val datePickerDialog = DatePickerDialog( LocalDate.now()
root.context, }
{ _, yy, mm, dayOfMonth -> val initialSelection = currentDate
// 使用Java 8的日期和时间API来格式化日期 .atStartOfDay(ZoneId.systemDefault())
val date = LocalDate.of(yy, mm + 1, dayOfMonth) // Java 8的LocalDate,月份从1开始 .toInstant()
val formattedDate = date.format(dateFormatter) .toEpochMilli()
startDate.setText(formattedDate) val picker = MaterialDatePicker.Builder.datePicker()
}, localStartDate.year, .setTitleText("选择开始日期")
localStartDate.monthValue - 1, .setSelection(initialSelection)
localStartDate.dayOfMonth .build()
) picker.addOnPositiveButtonClickListener { selection ->
datePickerDialog.show() val date = Instant.ofEpochMilli(selection)
.atZone(ZoneId.systemDefault())
.toLocalDate()
startDate.setText(date.format(dateFormatter))
}
picker.show((root.context as AppCompatActivity).supportFragmentManager, "md3_date_picker")
} }
} }
alert(titleResource = R.string.simulated_reading) { alert(titleResource = R.string.simulated_reading) {
customView { alertBinding.root } customView { alertBinding.root }
okButton { okButton {
alertBinding.run { alertBinding.run {
val start = editStart.text!!.toString().let { val start = editStart.text.toString().toIntOrNull() ?: 0
if (it.isEmpty()) 0 else it.toInt() val num = editNum.text.toString().toIntOrNull() ?: book.totalChapterNum
}
val num = editNum.text!!.toString().let {
if (it.isEmpty()) book.totalChapterNum else it.toInt()
}
val enabled = srEnabled.isChecked val enabled = srEnabled.isChecked
val date = startDate.text!!.toString().let {
if (it.isEmpty()) LocalDate.now() val date = try {
else LocalDate.parse(it, dateFormatter) LocalDate.parse(startDate.text.toString(), dateFormatter)
} catch (e: Exception) {
LocalDate.now()
} }
book.setStartDate(date) book.setStartDate(date)
book.setDailyChapters(num) book.setDailyChapters(num)
book.setStartChapter(start) book.setStartChapter(start)
@@ -1,159 +1,86 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll_content" android:id="@+id/ll_content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:padding="16dp" android:padding="24dp"
android:orientation="vertical"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="start" android:layout_gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <com.google.android.material.textfield.TextInputLayout
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_weight="1"
android:layout_marginEnd="16dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_vertical" style="@style/Widget.Material3.TextInputLayout.FilledBox.Dense">
android:layout_marginRight="2dp" <com.google.android.material.textfield.TextInputEditText
android:text="@string/switch_on" android:layout_width="match_parent"
android:textSize="16sp" android:layout_height="wrap_content"
tools:ignore="RtlHardcoded" android:inputType="date"
android:minWidth="88dp" /> android:ems="10"
android:id="@+id/start_date"
android:hint="@string/start_from"
android:layout_weight="1"
android:clickable="true"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.materialswitch.MaterialSwitch <com.google.android.material.materialswitch.MaterialSwitch
android:layout_height="wrap_content"
android:id="@+id/sr_enabled"
android:gravity="start|center_vertical"
android:layout_width="wrap_content" />
<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:gravity="start|center_vertical">
<TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_gravity="center_vertical" android:id="@+id/sr_enabled" />
android:text="@string/start_from"
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>
<LinearLayout <LinearLayout
android:baselineAligned="false"
android:orientation="horizontal" android:orientation="horizontal"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:gravity="start"> android:gravity="start">
<LinearLayout <com.google.android.material.textfield.TextInputLayout
android:orientation="horizontal" android:layout_width="0dp"
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:textSize="16sp"
tools:ignore="RtlHardcoded" />
</LinearLayout>
<EditText
android:id="@+id/edit_start"
android:layout_width="47dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_weight="1"
android:background="@drawable/bg_edit" android:layout_marginEnd="32dp"
android:hint="@string/start" style="@style/Widget.Material3.TextInputLayout.FilledBox.Dense">
android:importantForAutofill="no" <com.google.android.material.textfield.TextInputEditText
android:inputType="number" android:id="@+id/edit_start"
android:lines="1" android:layout_width="match_parent"
android:maxLength="5"
android:minWidth="60dp"
android:paddingLeft="5dp"
android:paddingTop="4dp"
android:paddingRight="5dp"
android:paddingBottom="4dp"
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_height="wrap_content"
android:layout_gravity="center_vertical" android:hint="@string/start_chapter"
android:layout_marginLeft="16dp"
android:layout_marginRight="5dp"
android:text="@string/daily_chapters"
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:importantForAutofill="no"
android:inputType="number" android:inputType="number"
android:lines="1" android:lines="1"
android:maxLength="5" android:maxLength="5" />
android:minWidth="60dp" </com.google.android.material.textfield.TextInputLayout>
android:paddingLeft="5dp"
android:paddingTop="4dp" <com.google.android.material.textfield.TextInputLayout
android:paddingRight="5dp" android:layout_width="0dp"
android:paddingBottom="4dp" android:layout_height="wrap_content"
android:textSize="14sp" android:layout_weight="1"
tools:ignore="TouchTargetSizeCheck,SpeakableTextPresentCheck,TextContrastCheck" style="@style/Widget.Material3.TextInputLayout.FilledBox.Dense">
android:textAlignment="center" /> <com.google.android.material.textfield.TextInputEditText
</LinearLayout> android:id="@+id/edit_num"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/daily_chapters"
android:importantForAutofill="no"
android:inputType="number"
android:lines="1"
android:maxLength="5" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout> </LinearLayout>