Windows ga (#358)

* Enhance Cloud TTS with navigation controls and shared UI overlay in desktop app

* Refactor Cloud TTS voice settings and remove standalone settings overlay on desktop app

* Persist reader window state and improve slider interaction in desktop app

* Improve PDF page transitions and refine focus management in desktop app

* Refactor scrollbar interaction and adjust desktop modal focus handling

* Improve PDF sidecar synchronization and cross-platform metadata compatibility

* Refactor PDF annotation comment logic to shared module and implement Desktop UI

* Refactor reader screen to use tap-to-toggle and full-width styling in desktop app

* Refactor reader workspace layout and chrome-panel interactions

* Implement global search keyboard shortcuts and focusable chrome layers

* Implement flavor-specific legal links and update the About UI

* Refactor reader UI controls on desktop app

* Enhance desktop folder sync with background metadata extraction and improved error handling

* Refactor Library UI and remove redundant Home tab in desktop app

* Add custom tooltips to reader icon buttons in desktop app

* Integrate app theme controls into reader interfaces on desktop

* Add right-to-left pagination support and improve focus restoration on desktop app

* Improve EPUB pagination geometry and diagnostic logging for layout cutoffs on desktop app

* Update desktop reader defaults and implement settings migration

* Implement block-based position tracking in ReaderLocator

* Enhance EPUB highlighting reliability in desktop app

* Add support for custom reader themes and update highlight palette logic in desktop app

* Replace the Tools panel with a "More" dropdown menu and refactor account UI

* Implement account profile header in desktop sidebar

* Implement cloud sync reliability improvements and sidebar toggle on desktop app

* Improve EPUB annotation synchronization and highlight mapping accuracy in desktop app

* Integrate WebView2 for EPUB vertical rendering on Windows

* Refactor reader layout logic and enhance WebView2 diagnostics

* Improve vertical reading layout and WebView2 resizing on Desktop

* Refine vertical reading mode layout and margin handling

* Enhance reader locator precision and Desktop mode-switching reliability

* Implement chapter-level caching and warm-start pagination in desktop app

* Replace bundled KCEF with native system webviews via SWT

* Refactor EPUB page info bar visibility and layout logic

* Improve PDF toolbar persistence and fix tab reactivation logic

* Enable multi-selection and bulk operations for custom fonts

* Refactor instrumentation tests

* Add EPUB UI test fixture and initial instrumentation tests

* Expand EpubReader UI tests and improve accessibility

* Add instrumentation tests and test tags for library and reader screens

* Enhance OPDS parser logic and catalog integration

* Add support for toggling local synchronization on a per-folder basis.

* Implement tri-state sizing for the TTS overlay

* Persist TTS overlay size across sessions

* Refactor reader brightness control and add incremental step buttons

* Improve CSS support, pagination control, and style-aware semantic caching

* Improve link handling, interaction, and diagnostics in the paginated reader

* crash fixes

* Implement persistent pending removal for external files

* Implement book-specific word replacements

* Add native vertical reading mode with custom renderer

* Implement text selection and navigation improvements for the native vertical reader

* Implement locator-based navigation and improved vertical scrolling in native vertical mode in epub

* Implement lazy loading and chapter prefetching for native vertical reader

* Improve window lifecycle and disposal handling on Desktop

* Optimize vertical reading performance in desktop app

* Enhance TTS start accuracy and diagnostic logging on desktop

* Refactor AI settings visibility on desktop

* Improve pagination height measurement and enhance cutoff diagnostics

* Implement lifecycle management and improve justified text splitting for pagination

* Refine AI usage tracking and force AI feature visibility on Desktop

* Add descriptive context comments and usage examples to string and plural resources.

* Optimize performance and memory usage in search and state mapping

* Replace reader page sliders with minimal slider and navigation controls

* Add support for CBT comic archives

* Harden file path validation and XML parsing to prevent security vulnerabilities

* Implement local account profile caching and optimize desktop performance

* Improve desktop persistence reliability and add Linux secure storage support

* Improved PDF zoom stability and layout prediction during zoom commits

* Improved PDF spread layout prediction, reader focus restoration, and account profile caching

* Enhance highlight precision and scoping using block-local offsets and CFIs

* Enhance cloud book content synchronization and background downloads

* Implement granular timestamp tracking for reading positions and PDF annotations

* Restrict diagnostic logging and stack traces to debug builds

* Refine PDF page gaps and reader chrome interaction logic

* Refactor PDF highlight rendering and overhaul Desktop sidebar UI

* Implement a new interaction dock and undo/redo history for PDF annotations in desktop

* Enhance PDF color picker and improve navigation scroll restoration

* Add highlight palette customization and improve selection menu UI in desktop app epub reader

* Enhance desktop shelf management and library organization
This commit is contained in:
Aryan 2026-06-02 00:51:42 +05:30 committed by GitHub
parent 5971eaa571
commit 83dcafa4b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
444 changed files with 47279 additions and 8096 deletions

View file

@ -3,8 +3,11 @@
package com.aryan.reader
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@ -27,10 +30,12 @@ import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.CloudDownload
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.SelectAll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
@ -46,6 +51,7 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@ -80,37 +86,67 @@ fun FontsScreen(
val showGoogleFontsOption = !(BuildConfig.FLAVOR == "oss" && BuildConfig.IS_OFFLINE)
// Dialog state
var showDeleteDialog by remember { mutableStateOf(false) }
var fontToDelete by remember { mutableStateOf<CustomFontEntity?>(null) }
var fontsPendingDelete by remember { mutableStateOf<List<CustomFontEntity>>(emptyList()) }
var showGoogleFontsSheet by remember { mutableStateOf(false) }
var selectedSection by remember { mutableStateOf(SharedFontSettingsSection.READER_FONTS) }
var selectedFontIds by remember { mutableStateOf<Set<String>>(emptySet()) }
val pickFontLauncher = rememberFilePickerLauncher { uris ->
uris.firstOrNull()?.let { viewModel.importFont(it) }
val pickFontLauncher = rememberFilePickerLauncher(viewModel::importFonts)
val fontMimeTypes = remember { supportedFontMimeTypes() }
val allFontIds = remember(fonts) { fonts.mapTo(mutableSetOf()) { it.id } }
val selectedFonts = remember(fonts, selectedFontIds) {
fonts.filter { it.id in selectedFontIds }
}
val isFontSelectionMode = selectedSection == SharedFontSettingsSection.READER_FONTS && selectedFonts.isNotEmpty()
LaunchedEffect(fonts) {
selectedFontIds = selectedFontIds.intersect(allFontIds)
}
val fontMimeTypes = arrayOf(
"font/ttf", "font/otf", "font/woff2",
"application/x-font-ttf", "application/x-font-otf",
"application/font-woff2", "application/vnd.ms-opentype",
"application/x-font-opentype"
)
BackHandler(enabled = isFontSelectionMode) {
selectedFontIds = emptySet()
}
Scaffold(
modifier = Modifier.statusBarsPadding(),
topBar = {
CustomTopAppBar(
title = { Text(stringResource(R.string.custom_fonts)) },
navigationIcon = {
IconButton(onClick = onBackClick) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.action_back))
if (isFontSelectionMode) {
ContextualTopAppBar(
selectedItemCount = selectedFonts.size,
onNavIconClick = { selectedFontIds = emptySet() },
onSelectAllClick = {
selectedFontIds = if (selectedFontIds.containsAll(allFontIds)) {
emptySet()
} else {
allFontIds
}
},
onDeleteClick = {
if (selectedFonts.isNotEmpty()) {
fontsPendingDelete = selectedFonts
}
}
}
)
)
} else {
CustomTopAppBar(
title = { Text(stringResource(R.string.custom_fonts)) },
navigationIcon = {
IconButton(onClick = onBackClick) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.action_back))
}
},
actions = {
if (selectedSection == SharedFontSettingsSection.READER_FONTS && fonts.isNotEmpty()) {
IconButton(onClick = { selectedFontIds = allFontIds }) {
Icon(Icons.Default.SelectAll, contentDescription = stringResource(R.string.select_all))
}
}
}
)
}
},
floatingActionButton = {
if (selectedSection == SharedFontSettingsSection.READER_FONTS && fonts.isNotEmpty()) {
if (selectedSection == SharedFontSettingsSection.READER_FONTS && fonts.isNotEmpty() && !isFontSelectionMode) {
Column(
horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.spacedBy(16.dp)
@ -139,7 +175,10 @@ fun FontsScreen(
Column(modifier = Modifier.fillMaxSize()) {
SharedFontSettingsTabs(
selectedSection = selectedSection,
onSectionChange = { selectedSection = it },
onSectionChange = {
selectedFontIds = emptySet()
selectedSection = it
},
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 16.dp)
)
@ -166,9 +205,13 @@ fun FontsScreen(
items(fonts, key = { it.id }) { font ->
FontListItem(
font = font,
isSelected = font.id in selectedFontIds,
isSelectionMode = isFontSelectionMode,
onSelectionToggle = {
selectedFontIds = selectedFontIds.toggle(font.id)
},
onDelete = {
fontToDelete = font
showDeleteDialog = true
fontsPendingDelete = listOf(font)
}
)
}
@ -208,17 +251,17 @@ fun FontsScreen(
}
}
if (showDeleteDialog && fontToDelete != null) {
DeleteFontConfirmationDialog(
fontName = fontToDelete!!.displayName,
if (fontsPendingDelete.isNotEmpty()) {
DeleteFontsConfirmationDialog(
fonts = fontsPendingDelete,
onConfirm = {
fontToDelete?.let { viewModel.deleteFont(it.id) }
showDeleteDialog = false
fontToDelete = null
val pendingIds = fontsPendingDelete.map { it.id }
viewModel.deleteFonts(pendingIds)
selectedFontIds = selectedFontIds - pendingIds.toSet()
fontsPendingDelete = emptyList()
},
onDismiss = {
showDeleteDialog = false
fontToDelete = null
fontsPendingDelete = emptyList()
}
)
}
@ -388,9 +431,13 @@ fun GoogleFontsBottomSheet(
}
// Existing unchanged components
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun FontListItem(
font: CustomFontEntity,
isSelected: Boolean,
isSelectionMode: Boolean,
onSelectionToggle: () -> Unit,
onDelete: () -> Unit
) {
val customTypeface = remember(font.path) {
@ -402,8 +449,23 @@ fun FontListItem(
}
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface)
modifier = Modifier
.fillMaxWidth()
.combinedClickable(
onClick = {
if (isSelectionMode) {
onSelectionToggle()
}
},
onLongClick = onSelectionToggle
),
colors = CardDefaults.cardColors(
containerColor = if (isSelected) {
MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.45f)
} else {
MaterialTheme.colorScheme.surface
}
)
) {
Column(modifier = Modifier.padding(16.dp)) {
Row(
@ -411,17 +473,27 @@ fun FontListItem(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
if (isSelectionMode) {
Checkbox(
checked = isSelected,
onCheckedChange = { onSelectionToggle() },
modifier = Modifier.padding(end = 8.dp)
)
}
Text(
text = font.displayName,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold
fontWeight = FontWeight.Bold,
modifier = Modifier.weight(1f)
)
IconButton(onClick = onDelete, modifier = Modifier.size(24.dp)) {
Icon(
Icons.Default.Delete,
contentDescription = stringResource(R.string.action_delete),
tint = MaterialTheme.colorScheme.error
)
if (!isSelectionMode) {
IconButton(onClick = onDelete, modifier = Modifier.size(24.dp)) {
Icon(
Icons.Default.Delete,
contentDescription = stringResource(R.string.action_delete),
tint = MaterialTheme.colorScheme.error
)
}
}
}
@ -476,15 +548,32 @@ private fun List<CustomFontEntity>.toSharedCustomFontItems(): List<CustomFontIte
}
@Composable
fun DeleteFontConfirmationDialog(
fontName: String,
fun DeleteFontsConfirmationDialog(
fonts: List<CustomFontEntity>,
onConfirm: () -> Unit,
onDismiss: () -> Unit
) {
val isSingleFont = fonts.size == 1
AlertDialog(
onDismissRequest = onDismiss,
title = { Text(stringResource(R.string.dialog_delete_font)) },
text = { Text(stringResource(R.string.dialog_delete_font_desc, fontName)) },
title = {
Text(
if (isSingleFont) {
stringResource(R.string.dialog_delete_font)
} else {
stringResource(R.string.dialog_delete_fonts)
}
)
},
text = {
Text(
if (isSingleFont) {
stringResource(R.string.dialog_delete_font_desc, fonts.first().displayName)
} else {
stringResource(R.string.dialog_delete_fonts_desc, fonts.size)
}
)
},
confirmButton = {
TextButton(
onClick = onConfirm,
@ -498,3 +587,7 @@ fun DeleteFontConfirmationDialog(
}
)
}
private fun Set<String>.toggle(id: String): Set<String> {
return if (id in this) this - id else this + id
}