模拟追读界面与功能 #131
This commit is contained in:
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>
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user