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:
parent
5971eaa571
commit
83dcafa4b6
444 changed files with 47279 additions and 8096 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue