Update v1.0.49 (#330)

* Added PDF top tab strip visibility toggle and fixed WebView hit test NPE

* Refactored desktop reader screens and state management into specialized components

* Added image gallery to reader sidebar and refactored desktop PDF UI components

* Implemented EPUB image gallery

* Refactored reader and library models to use shared common types, centralizing file type resolution and texture management while removing redundant mapping logic

* Standardized UI styling and refactored app navigation layout

* Implemented auto-hiding reader chrome and activity tracking in desktop

* Refactored reader panels into distinct left and right modal layers with platform-specific sizing and keyboard navigation support.

* Added PPTX support for desktop and refactored parsing into a shared module

* Implement paid AI features and account management for the desktop application.

* Implement AI Hub and enhanced Cloud TTS integration for Desktop

* Implement streaming support for AI definition and summarization features

* Implement support for password-protected PDFs and file actions in the desktop reader.

* Implement cloud synchronization for desktop using Firestore and Google Drive

* Implement PDF reflow and "Text View" for the desktop reader

* Refactor OPDS logic to use SharedOpdsController

* Optimize PDF tile rendering performance

* Implement two-page spread support for PDF pagination

* Implement two-page spread support for the PDF viewer

* Improved shared spread zoom in PDF viewer

* Improve PDF spread navigation with fling support and configurable page gaps

* Add brightness control to PDF and EPUB readers

* Refactor folder synchronization to use shared logic engine

* Implement safe string formatting and validation for localized resources

* Implement TTS chunk skip navigation

* Implement deep-linking and playback controls for TTS media sessions

* Implement start index for TTS playback

* Improve TTS navigation, prefetching, and notification duration reporting

* Implement TTS mini playback bar for background reading

* Implement multi-window reader support for the desktop application

* Improve desktop modal window management and visibility syncing

* Implement localized string support for Desktop and shared UI

* Implement language selection and persistence for Desktop

* Implement plural string support for Desktop and migrate hardcoded counts to plurals.xml

* Implement localized banner messages and UI strings using resource-backed SharedText

* Implement compact badge styling for small book covers

* Refactor PDF native interaction and improve HTML import memory safety

* fix language persistence

* Refactor reader overflow menus to use section-based logic

* Refactor PDF layout remapping and improve text box interaction

* Improve CFI resolution and TTS resume accuracy using dynamic chunk offsets

* Centralize PDF annotation export mapping and improve metadata handling

* Add support for threaded comments in PDF highlight annotations

* Flatten highlight comments into a single thread for PDF export and allow author editing

* Integrate page slider into reader chrome and persist toggle state

* Handle fragments and queries in EPUB chapter paths

* Implement dynamic, theme-aware coloring for the reader slider

* Implement customizable app-wide font preference

* Implement one-hand zoom gestures in the PDF viewer

* Implement File Information dialog for PDF and EPUB readers

* Bump version to 1.0.49 (53)

* Refactor PDF reader logic into modular components

* Add ProGuard rules to prevent R8 optimization issues in EPUB reader screens

* Add option to use PDF filenames as display names

* Fix preservation of PDF filename display preference in library projection
This commit is contained in:
Aryan 2026-05-20 22:14:01 +05:30 committed by GitHub
parent dc5196526f
commit 9510293ac3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
245 changed files with 37538 additions and 12460 deletions

View file

@ -144,6 +144,7 @@ import androidx.core.net.toUri
import androidx.core.text.HtmlCompat
import com.aryan.reader.data.BookMetadataEdit
import com.aryan.reader.data.RecentFileItem
import com.aryan.reader.shared.SharedText
import com.aryan.reader.shared.ui.SharedMarkdownText
import timber.log.Timber
import java.text.SimpleDateFormat
@ -239,7 +240,8 @@ fun rememberFilePickerLauncher(
contract = ActivityResultContracts.OpenMultipleDocuments(),
onResult = { uris: List<Uri> ->
if (uris.isNotEmpty()) {
Timber.d("${uris.size} file(s) selected.")
val fileLabel = if (uris.size == 1) "file" else "files"
Timber.d("${uris.size} $fileLabel selected.")
onFilesSelected(uris)
} else {
Timber.d("File selection cancelled.")
@ -383,6 +385,7 @@ fun DeleteConfirmationDialog(
@Composable
fun FileInfoDialog(
item: RecentFileItem,
usePdfFileNameAsDisplayName: Boolean = false,
onDismiss: () -> Unit,
onSaveMetadata: (BookMetadataEdit) -> Unit,
onSaveDisplayName: (String?) -> Unit,
@ -399,8 +402,8 @@ fun FileInfoDialog(
mutableStateOf(item.seriesIndex?.formatMetadataNumber().orEmpty())
}
var descriptionInput by remember(item.bookId, item.description) { mutableStateOf(item.description.orEmpty()) }
var displayNameInput by remember(item.bookId, item.customName, item.title, item.displayName) {
mutableStateOf(item.customName ?: item.cardTitle())
var displayNameInput by remember(item.bookId, item.customName, item.title, item.displayName, usePdfFileNameAsDisplayName) {
mutableStateOf(item.customName ?: item.cardTitle(usePdfFileNameAsDisplayName))
}
var showRestoreConfirmation by remember(item.bookId) { mutableStateOf(false) }
@ -455,7 +458,7 @@ fun FileInfoDialog(
} else {
stringResource(R.string.file_information)
},
subtitle = item.cardTitle(),
subtitle = item.cardTitle(usePdfFileNameAsDisplayName),
onClose = {
if (isEditing) {
isEditing = false
@ -498,6 +501,7 @@ fun FileInfoDialog(
} else {
BookMetadataInfoContent(
item = item,
usePdfFileNameAsDisplayName = usePdfFileNameAsDisplayName,
formattedDate = formattedDate,
lastModifiedDate = lastModifiedDate,
pathText = pathTextFinal,
@ -611,6 +615,7 @@ private fun FileInfoTopBar(
@Composable
private fun BookMetadataInfoContent(
item: RecentFileItem,
usePdfFileNameAsDisplayName: Boolean,
formattedDate: String,
lastModifiedDate: String?,
pathText: String,
@ -624,7 +629,7 @@ private fun BookMetadataInfoContent(
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(
item.cardTitle(),
item.cardTitle(usePdfFileNameAsDisplayName),
style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.Bold,
maxLines = 3,
@ -1120,6 +1125,8 @@ private fun Double.formatMetadataNumber(): String {
@Composable
fun CustomTopBanner(bannerMessage: BannerMessage?) {
val context = LocalContext.current
val bannerText = bannerMessage?.localizedMessage(context).orEmpty()
AnimatedVisibility(
visible = bannerMessage != null,
enter = slideInVertically(initialOffsetY = { -it }) + fadeIn(),
@ -1138,7 +1145,7 @@ fun CustomTopBanner(bannerMessage: BannerMessage?) {
shadowElevation = 8.dp
) {
Text(
text = bannerMessage?.message ?: "",
text = bannerText,
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
color = if (bannerMessage?.isError == true) MaterialTheme.colorScheme.onErrorContainer else MaterialTheme.colorScheme.onSecondaryContainer,
style = MaterialTheme.typography.bodyMedium,
@ -1149,6 +1156,25 @@ fun CustomTopBanner(bannerMessage: BannerMessage?) {
}
}
private fun BannerMessage.localizedMessage(context: Context): String {
return text?.resolveAndroidText(context) ?: message
}
private fun SharedText.resolveAndroidText(context: Context): String {
val resources = context.resources
val packageName = context.packageName
val formatArgs = args.toTypedArray()
val quantityValue = quantity
val resolved = if (quantityValue == null) {
val id = resources.getIdentifier(name, "string", packageName)
if (id == 0) null else runCatching { resources.getString(id, *formatArgs) }.getOrNull()
} else {
val id = resources.getIdentifier(name, "plurals", packageName)
if (id == 0) null else runCatching { resources.getQuantityString(id, quantityValue, *formatArgs) }.getOrNull()
}
return resolved ?: fallbackMessage()
}
@Suppress("KotlinConstantConditions")
@Composable
fun AboutDialog(onDismiss: () -> Unit) {
@ -1429,7 +1455,12 @@ fun AutoSizeText(
}
@Composable
fun FileTypeBadge(type: FileType, modifier: Modifier = Modifier, overlay: Boolean = false) {
fun FileTypeBadge(
type: FileType,
modifier: Modifier = Modifier,
overlay: Boolean = false,
compact: Boolean = false
) {
val containerColor = if (overlay) Color.Black.copy(alpha = 0.6f) else MaterialTheme.colorScheme.secondaryContainer
val contentColor = if (overlay) Color.White else MaterialTheme.colorScheme.onSecondaryContainer
@ -1442,9 +1473,17 @@ fun FileTypeBadge(type: FileType, modifier: Modifier = Modifier, overlay: Boolea
) {
Text(
text = if (type == FileType.UNKNOWN) "FILE" else type.name.uppercase(),
style = MaterialTheme.typography.labelSmall.copy(letterSpacing = 1.sp),
style = if (compact) {
MaterialTheme.typography.labelSmall.copy(fontSize = 9.sp, letterSpacing = 0.sp)
} else {
MaterialTheme.typography.labelSmall.copy(letterSpacing = 1.sp)
},
fontWeight = FontWeight.ExtraBold,
modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp)
maxLines = 1,
modifier = Modifier.padding(
horizontal = if (compact) 6.dp else 10.dp,
vertical = if (compact) 3.dp else 4.dp
)
)
}
}
@ -1498,8 +1537,12 @@ fun BookTagChipsRow(
private const val UNKNOWN_AUTHOR_LABEL = "No author listed"
fun RecentFileItem.cardTitle(): String {
return customName ?: title?.takeIf { it.isNotBlank() } ?: displayName
fun RecentFileItem.cardTitle(usePdfFileNameAsDisplayName: Boolean = false): String {
customName?.takeIf { it.isNotBlank() }?.let { return it }
if (usePdfFileNameAsDisplayName && type == FileType.PDF) {
return displayName
}
return title?.takeIf { it.isNotBlank() } ?: displayName
}
fun RecentFileItem.cardAuthor(): String {