feat(phase5): reader engine integration — Checkpoint model + ChapterDrawer

- Checkpoint: serializable model for reading position (bookId, chapterIndex, pageIndex, scrollOffset, progress, timestamp).
- ChapterDrawer: ModalBottomSheet with chapter list, current chapter indicator, progress percentage.
- Volume keys and chapter navigation already present in EpubReaderScreen/PdfViewerScreen.
- ':app:assembleOssDebug' passes.
This commit is contained in:
Atte149 2026-06-24 18:08:00 +03:00
parent 1dc3bf2fcb
commit fb6403c4a9
2 changed files with 108 additions and 0 deletions

View file

@ -0,0 +1,95 @@
package org.dueattendant149.bookreader.reader
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
data class ChapterItem(
val title: String,
val pageIndex: Int,
val progress: Float = 0f,
)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ChapterDrawer(
chapters: List<ChapterItem>,
currentChapterIndex: Int,
onChapterSelected: (Int) -> Unit,
onDismiss: () -> Unit,
) {
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
ModalBottomSheet(
onDismissRequest = onDismiss,
sheetState = sheetState,
) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Chapters",
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.padding(bottom = 12.dp)
)
LazyColumn {
itemsIndexed(chapters) { index, chapter ->
val isCurrent = index == currentChapterIndex
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { onChapterSelected(index) }
.padding(vertical = 12.dp, horizontal = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = chapter.title,
style = if (isCurrent) MaterialTheme.typography.titleMedium
else MaterialTheme.typography.bodyMedium,
color = if (isCurrent) MaterialTheme.colorScheme.primary
else MaterialTheme.colorScheme.onSurface,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = "${(chapter.progress * 100).toInt()}%",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
if (isCurrent) {
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "",
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.bodySmall
)
}
}
}
}
}
}
}

View file

@ -0,0 +1,13 @@
package org.dueattendant149.bookreader.reader
import kotlinx.serialization.Serializable
@Serializable
data class Checkpoint(
val bookId: String,
val chapterIndex: Int = 0,
val pageIndex: Int = 0,
val scrollOffset: Int = 0,
val progress: Float = 0f,
val timestamp: Long = System.currentTimeMillis(),
)