General fixes (#76)
* Added print functionality to PDF viewer * feat(pdf): add interactive button support and visibility reveal heuristic * Moved search state management to MainViewModel and improved search UI interaction. * Updated `pdfiumandroid` to version 2.0.0 and handled resulting API changes, including nullable page/text page returns and revised native pointer access. increased compileSdk to 36. * Fixed Table of Contents (TOC) truncation bug and improved the TOC UI in `PdfViewerScreen`. - Implemented `getFixedTableOfContents` using reflection to bypass a library issue where sibling nodes were incorrectly truncated during traversal. - Enhanced the TOC drawer with a nested, expandable tree structure using the new `PdfTocTreeItem` component. - Added a custom `VerticalScrollbar` with draggable support for better navigation within long TOC lists. - Integrated `animateColorAsState` and `animateFloatAsState` for smoother UI transitions in the TOC and scrollbar. - Optimized TOC loading by flattening the tree structure and managing expansion states with `rememberSaveable`. * Improved zoom pivot calculation and interaction handling in PdfVerticalReader * Fixed high-res PDF tile bleeding by implementing clipRect in PdfBitmapLayer * fix(pdf): resolve zoom stuttering, in pagination mode, by removing eager scale snapping
This commit is contained in:
parent
dece09fec0
commit
1884ace646
12 changed files with 1207 additions and 653 deletions
|
|
@ -20,8 +20,6 @@
|
|||
// LibraryScreen.kt
|
||||
package com.aryan.reader
|
||||
|
||||
import android.content.Context
|
||||
import android.provider.DocumentsContract
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
|
|
@ -64,7 +62,6 @@ import androidx.compose.material3.CircularProgressIndicator
|
|||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
|
|
@ -99,8 +96,6 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.net.toUri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import coil.compose.AsyncImage
|
||||
import coil.request.ImageRequest
|
||||
|
|
@ -146,7 +141,7 @@ fun LibraryScreen(
|
|||
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
var isSearchActive by remember { mutableStateOf(false) }
|
||||
val isSearchActive = uiState.isSearchActive
|
||||
val searchQuery = uiState.searchQuery
|
||||
|
||||
val pickFolderLauncher = rememberLauncherForActivityResult(
|
||||
|
|
@ -222,8 +217,7 @@ fun LibraryScreen(
|
|||
}
|
||||
|
||||
BackHandler(enabled = isSearchActive) {
|
||||
isSearchActive = false
|
||||
viewModel.onSearchQueryChange("")
|
||||
viewModel.setSearchActive(false)
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
|
|
@ -238,10 +232,7 @@ fun LibraryScreen(
|
|||
searchQuery = searchQuery,
|
||||
isSearchActive = isSearchActive,
|
||||
onSearchQueryChange = viewModel::onSearchQueryChange,
|
||||
onSearchActiveChange = { active ->
|
||||
isSearchActive = active
|
||||
if (!active) viewModel.onSearchQueryChange("")
|
||||
},
|
||||
onSearchActiveChange = viewModel::setSearchActive,
|
||||
onSortOrderChange = viewModel::setSortOrder,
|
||||
onClearSelection = { viewModel.clearContextualAction() },
|
||||
onItemClick = viewModel::onRecentFileClicked,
|
||||
|
|
@ -474,6 +465,19 @@ fun LibraryScreenContent(
|
|||
val tabTitles = listOf("All Books", "Shelves", "Folders")
|
||||
val searchFocusRequester = remember { FocusRequester() }
|
||||
|
||||
var textFieldValue by remember(isSearchActive) {
|
||||
mutableStateOf(TextFieldValue(searchQuery, TextRange(searchQuery.length)))
|
||||
}
|
||||
|
||||
LaunchedEffect(searchQuery) {
|
||||
if (textFieldValue.text != searchQuery) {
|
||||
textFieldValue = textFieldValue.copy(
|
||||
text = searchQuery,
|
||||
selection = TextRange(searchQuery.length)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(isSearchActive) {
|
||||
if (isSearchActive) {
|
||||
searchFocusRequester.requestFocus()
|
||||
|
|
@ -501,7 +505,7 @@ fun LibraryScreenContent(
|
|||
} else if (isSearchActive) {
|
||||
Surface(
|
||||
shadowElevation = 4.dp,
|
||||
modifier = Modifier.fillMaxWidth().statusBarsPadding()
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
|
@ -513,8 +517,11 @@ fun LibraryScreenContent(
|
|||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Close search")
|
||||
}
|
||||
OutlinedTextField(
|
||||
value = searchQuery,
|
||||
onValueChange = onSearchQueryChange,
|
||||
value = textFieldValue,
|
||||
onValueChange = {
|
||||
textFieldValue = it
|
||||
onSearchQueryChange(it.text)
|
||||
},
|
||||
placeholder = { Text("Search title or author...") },
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
|
|
@ -866,9 +873,11 @@ private fun ShelfDetailScreen(
|
|||
},
|
||||
floatingActionButton = {
|
||||
if (shelf.name != "Unshelved" && !isContextualModeActive) {
|
||||
FloatingActionButton(onClick = onAddBooksClick) {
|
||||
Icon(Icons.Default.Add, contentDescription = "Add books")
|
||||
}
|
||||
ExtendedFloatingActionButton(
|
||||
onClick = onAddBooksClick,
|
||||
icon = { Icon(Icons.Default.Add, contentDescription = null) },
|
||||
text = { Text("Add books") }
|
||||
)
|
||||
}
|
||||
}
|
||||
) { paddingValues ->
|
||||
|
|
@ -1362,25 +1371,6 @@ private fun DeleteShelvesConfirmationDialog(
|
|||
)
|
||||
}
|
||||
|
||||
private fun getDisplayPathFromUri(context: Context, uriString: String): String {
|
||||
val uri = uriString.toUri()
|
||||
val fallbackName = DocumentFile.fromTreeUri(context, uri)?.name ?: "Unknown Folder"
|
||||
|
||||
if (DocumentsContract.isTreeUri(uri) && DocumentsContract.getTreeDocumentId(uri).isNotEmpty()) {
|
||||
val documentId = DocumentsContract.getTreeDocumentId(uri)
|
||||
val split = documentId.split(":")
|
||||
if (split.size > 1) {
|
||||
val type = split[0]
|
||||
val path = split[1]
|
||||
return when (type) {
|
||||
"primary" -> "Internal Storage ▸ $path"
|
||||
else -> path
|
||||
}
|
||||
}
|
||||
}
|
||||
return fallbackName
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FolderSyncScreen(
|
||||
syncedFolders: List<SyncedFolder>,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue