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:
parent
dc5196526f
commit
9510293ac3
245 changed files with 37538 additions and 12460 deletions
|
|
@ -8,9 +8,9 @@ import androidx.compose.ui.graphics.toArgb
|
|||
import androidx.core.content.edit
|
||||
import com.aryan.reader.BuildConfig
|
||||
import com.aryan.reader.R
|
||||
import com.aryan.reader.ReaderTheme
|
||||
import com.aryan.reader.ReaderTexture
|
||||
import com.aryan.reader.epubreader.SystemUiMode
|
||||
import com.aryan.reader.shared.BuiltInPdfReaderThemes
|
||||
import com.aryan.reader.shared.reader.ReaderPageSpreadMode
|
||||
|
||||
internal const val VERTICAL_SCROLL_TAG = "PdfVerticalScroll"
|
||||
internal const val SETTINGS_PREFS_NAME = "epub_reader_settings"
|
||||
|
|
@ -33,7 +33,6 @@ private const val PDF_AUTO_SCROLL_LOCAL_SPEED_PREFIX = "pdf_as_local_speed_"
|
|||
private const val PDF_AUTO_SCROLL_LOCAL_MIN_PREFIX = "pdf_as_local_min_"
|
||||
private const val PDF_AUTO_SCROLL_LOCAL_MAX_PREFIX = "pdf_as_local_max_"
|
||||
private const val PDF_SCROLL_LOCKED_PREFIX = "pdf_sl_local_"
|
||||
internal const val PDF_FULL_SCREEN_PREFIX = "pdf_fs_local_"
|
||||
private const val PDF_MUSICIAN_MODE_KEY = "pdf_musician_mode_enabled"
|
||||
private const val PREF_USE_ONLINE_DICT = "use_online_dictionary"
|
||||
private const val PREF_EXTERNAL_DICT_PKG = "external_dictionary_package"
|
||||
|
|
@ -47,17 +46,21 @@ internal const val PDF_BOTTOM_TOOLS_KEY = "pdf_bottom_tools"
|
|||
internal const val PDF_SYSTEM_UI_MODE_KEY = "pdf_system_ui_mode"
|
||||
internal const val PDF_VERTICAL_PAGE_GAP_VISIBLE_KEY = "pdf_vertical_page_gap_visible"
|
||||
internal const val PDF_PAGE_NUMBER_OVERLAY_VISIBLE_KEY = "pdf_page_number_overlay_visible"
|
||||
internal const val PDF_TOP_TAB_STRIP_VISIBLE_KEY = "pdf_top_tab_strip_visible"
|
||||
internal const val PDF_PAGE_SPREAD_MODE_KEY = "pdf_page_spread_mode"
|
||||
internal const val PDF_FIRST_PAGE_STANDALONE_IN_SPREAD_KEY = "pdf_first_page_standalone_in_spread"
|
||||
internal const val PDF_LAYOUT_DEBUG_TAG = "PdfLayoutDebug"
|
||||
private const val PDF_HIDDEN_TOOLS_DEFAULTS_VERSION_KEY = "pdf_hidden_tools_defaults_version"
|
||||
private const val PDF_HIDDEN_TOOLS_DEFAULTS_VERSION = 2
|
||||
private const val PDF_HIDDEN_TOOLS_DEFAULTS_VERSION = 3
|
||||
|
||||
enum class PdfReaderTool(@StringRes val titleRes: Int, val category: String) {
|
||||
DICTIONARY(R.string.tool_external_apps, "Top Bar"),
|
||||
THEME(R.string.tooltip_theme_desc, "Top Bar"),
|
||||
BRIGHTNESS(R.string.tool_brightness, "Top Bar"),
|
||||
LOCK_PANNING(R.string.tooltip_lock_pan, "Top Bar"),
|
||||
FILE_INFO(R.string.file_information, "Overflow Menu"),
|
||||
VISUAL_OPTIONS(R.string.menu_visual_options, "Overflow Menu"),
|
||||
TAP_TO_TURN(R.string.menu_tap_to_turn_pages, "Overflow Menu"),
|
||||
FULL_SCREEN(R.string.tooltip_fullscreen, "Top Bar"),
|
||||
SLIDER(R.string.tool_navigation_slider, "Bottom Bar"),
|
||||
TOC(R.string.tool_sidebar, "Bottom Bar"),
|
||||
SEARCH(R.string.action_search, "Bottom Bar"),
|
||||
|
|
@ -83,38 +86,41 @@ enum class PdfReaderTool(@StringRes val titleRes: Int, val category: String) {
|
|||
internal fun defaultPdfHiddenTools(): Set<String> {
|
||||
return setOf(
|
||||
PdfReaderTool.SCREEN_ORIENTATION.name,
|
||||
PdfReaderTool.HIGHLIGHT_ALL.name
|
||||
PdfReaderTool.HIGHLIGHT_ALL.name,
|
||||
PdfReaderTool.BRIGHTNESS.name
|
||||
)
|
||||
}
|
||||
|
||||
internal fun defaultPdfToolOrder(): List<PdfReaderTool> = PdfReaderTool.entries.toList()
|
||||
|
||||
internal fun defaultPdfBottomTools(): Set<String> {
|
||||
return PdfReaderTool.entries.filter { it.category == "Bottom Bar" }.map { it.name }.toSet()
|
||||
internal fun isPdfReaderToolAvailable(tool: PdfReaderTool): Boolean {
|
||||
return BuildConfig.IS_PRO || tool != PdfReaderTool.OCR_LANGUAGE
|
||||
}
|
||||
|
||||
val PdfBuiltInThemes = listOf(
|
||||
ReaderTheme("no_theme", "No Theme", Color.Unspecified, Color.Unspecified, false),
|
||||
ReaderTheme("reverse", "Reverse", Color.Black, Color.White, true),
|
||||
ReaderTheme("light", "Light", Color(0xFFFFFFFF), Color(0xFF000000), false),
|
||||
ReaderTheme("dark", "Dark", Color(0xFF121212), Color(0xFFE0E0E0), true),
|
||||
ReaderTheme("sepia", "Sepia", Color(0xFFFBF0D9), Color(0xFF5F4B32), false),
|
||||
ReaderTheme("slate", "Slate", Color(0xFF2E3440), Color(0xFFECEFF4), true),
|
||||
ReaderTheme("oled", "OLED", Color(0xFF000000), Color(0xFFB0B0B0), true),
|
||||
ReaderTheme("pdf_natural_white_texture", "Natural White", Color(0xFFF7F1E5), Color(0xFF1D1B18), false, textureId = ReaderTexture.NATURAL_WHITE.id),
|
||||
ReaderTheme("pdf_retina_texture", "Retina", Color(0xFFF1E4CD), Color(0xFF2A2119), false, textureId = ReaderTexture.RETINA_WOOD.id),
|
||||
ReaderTheme("pdf_veneer_texture", "Veneer", Color(0xFFF4E7CF), Color(0xFF2A2119), false, textureId = ReaderTexture.LIGHT_VENEER.id),
|
||||
ReaderTheme("pdf_grey_wash_texture", "Grey Wash", Color(0xFF202124), Color(0xFFFFFFFF), true, textureId = ReaderTexture.GREY_WASH.id),
|
||||
ReaderTheme("pdf_fabric_texture", "Fabric", Color(0xFF262626), Color(0xFFE8E2D8), true, textureId = ReaderTexture.CLASSY_FABRIC.id),
|
||||
ReaderTheme("pdf_retro_texture", "Retro", Color(0xFFF6ECD8), Color(0xFF2F2118), false, textureId = ReaderTexture.RETRO_INTRO.id)
|
||||
)
|
||||
internal fun defaultPdfToolOrder(): List<PdfReaderTool> = PdfReaderTool.entries.filter(::isPdfReaderToolAvailable)
|
||||
|
||||
internal fun defaultPdfBottomTools(): Set<String> {
|
||||
return defaultPdfToolOrder().filter { it.category == "Bottom Bar" }.map { it.name }.toSet()
|
||||
}
|
||||
|
||||
val PdfBuiltInThemes = BuiltInPdfReaderThemes
|
||||
|
||||
private fun sanitizePdfToolNameSet(
|
||||
toolNames: Set<String>,
|
||||
includeTool: (PdfReaderTool) -> Boolean = { true }
|
||||
): Set<String> {
|
||||
return toolNames.mapNotNull { toolName ->
|
||||
PdfReaderTool.entries
|
||||
.firstOrNull { it.name == toolName }
|
||||
?.takeIf { isPdfReaderToolAvailable(it) && includeTool(it) }
|
||||
?.name
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
internal fun loadPdfHiddenTools(context: Context): Set<String> {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
val savedHiddenTools = prefs.getStringSet(PDF_HIDDEN_TOOLS_KEY, emptySet()).orEmpty()
|
||||
val savedHiddenTools = sanitizePdfToolNameSet(prefs.getStringSet(PDF_HIDDEN_TOOLS_KEY, emptySet()).orEmpty())
|
||||
val defaultsVersion = prefs.getInt(PDF_HIDDEN_TOOLS_DEFAULTS_VERSION_KEY, 0)
|
||||
if (defaultsVersion < PDF_HIDDEN_TOOLS_DEFAULTS_VERSION) {
|
||||
val migratedHiddenTools = savedHiddenTools + defaultPdfHiddenTools()
|
||||
val migratedHiddenTools = sanitizePdfToolNameSet(savedHiddenTools + pdfHiddenToolsIntroducedAfter(defaultsVersion))
|
||||
prefs.edit {
|
||||
putStringSet(PDF_HIDDEN_TOOLS_KEY, migratedHiddenTools)
|
||||
putInt(PDF_HIDDEN_TOOLS_DEFAULTS_VERSION_KEY, PDF_HIDDEN_TOOLS_DEFAULTS_VERSION)
|
||||
|
|
@ -124,10 +130,20 @@ internal fun loadPdfHiddenTools(context: Context): Set<String> {
|
|||
return savedHiddenTools
|
||||
}
|
||||
|
||||
private fun pdfHiddenToolsIntroducedAfter(defaultsVersion: Int): Set<String> {
|
||||
return buildSet {
|
||||
if (defaultsVersion < 2) {
|
||||
add(PdfReaderTool.SCREEN_ORIENTATION.name)
|
||||
add(PdfReaderTool.HIGHLIGHT_ALL.name)
|
||||
}
|
||||
if (defaultsVersion < 3) add(PdfReaderTool.BRIGHTNESS.name)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun savePdfHiddenTools(context: Context, hiddenTools: Set<String>) {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.edit {
|
||||
putStringSet(PDF_HIDDEN_TOOLS_KEY, hiddenTools)
|
||||
putStringSet(PDF_HIDDEN_TOOLS_KEY, sanitizePdfToolNameSet(hiddenTools))
|
||||
putInt(PDF_HIDDEN_TOOLS_DEFAULTS_VERSION_KEY, PDF_HIDDEN_TOOLS_DEFAULTS_VERSION)
|
||||
}
|
||||
}
|
||||
|
|
@ -138,24 +154,41 @@ internal fun loadPdfToolOrder(context: Context): List<PdfReaderTool> {
|
|||
?.split(',')
|
||||
?.filter { it.isNotBlank() }
|
||||
?.mapNotNull { name -> PdfReaderTool.entries.firstOrNull { it.name == name } }
|
||||
?.filter(::isPdfReaderToolAvailable)
|
||||
.orEmpty()
|
||||
return (savedTools + defaultPdfToolOrder().filterNot { it in savedTools }).distinct()
|
||||
}
|
||||
|
||||
internal fun savePdfToolOrder(context: Context, toolOrder: List<PdfReaderTool>) {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.edit { putString(PDF_TOOL_ORDER_KEY, toolOrder.joinToString(",") { it.name }) }
|
||||
prefs.edit {
|
||||
putString(
|
||||
PDF_TOOL_ORDER_KEY,
|
||||
toolOrder.filter(::isPdfReaderToolAvailable).joinToString(",") { it.name }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun loadPdfBottomTools(context: Context): Set<String> {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
val defaultBottomTools = defaultPdfBottomTools()
|
||||
return prefs.getStringSet(PDF_BOTTOM_TOOLS_KEY, defaultBottomTools) ?: defaultBottomTools
|
||||
return sanitizePdfToolNameSet(
|
||||
toolNames = prefs.getStringSet(PDF_BOTTOM_TOOLS_KEY, defaultBottomTools) ?: defaultBottomTools,
|
||||
includeTool = { it.category == "Bottom Bar" }
|
||||
)
|
||||
}
|
||||
|
||||
internal fun savePdfBottomTools(context: Context, bottomTools: Set<String>) {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.edit { putStringSet(PDF_BOTTOM_TOOLS_KEY, bottomTools) }
|
||||
prefs.edit {
|
||||
putStringSet(
|
||||
PDF_BOTTOM_TOOLS_KEY,
|
||||
sanitizePdfToolNameSet(
|
||||
toolNames = bottomTools,
|
||||
includeTool = { it.category == "Bottom Bar" }
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun loadCustomHighlightColors(context: Context): Map<PdfHighlightColor, Color> {
|
||||
|
|
@ -217,6 +250,38 @@ internal fun loadPdfPageNumberOverlayVisible(context: Context): Boolean {
|
|||
return prefs.getBoolean(PDF_PAGE_NUMBER_OVERLAY_VISIBLE_KEY, true)
|
||||
}
|
||||
|
||||
internal fun savePdfPageSpreadMode(context: Context, mode: ReaderPageSpreadMode) {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.edit { putString(PDF_PAGE_SPREAD_MODE_KEY, mode.name) }
|
||||
}
|
||||
|
||||
internal fun loadPdfPageSpreadMode(context: Context): ReaderPageSpreadMode {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
val modeName = prefs.getString(PDF_PAGE_SPREAD_MODE_KEY, ReaderPageSpreadMode.SINGLE.name)
|
||||
return runCatching { ReaderPageSpreadMode.valueOf(modeName ?: ReaderPageSpreadMode.SINGLE.name) }
|
||||
.getOrDefault(ReaderPageSpreadMode.SINGLE)
|
||||
}
|
||||
|
||||
internal fun savePdfFirstPageStandaloneInSpread(context: Context, isEnabled: Boolean) {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.edit { putBoolean(PDF_FIRST_PAGE_STANDALONE_IN_SPREAD_KEY, isEnabled) }
|
||||
}
|
||||
|
||||
internal fun loadPdfFirstPageStandaloneInSpread(context: Context): Boolean {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
return prefs.getBoolean(PDF_FIRST_PAGE_STANDALONE_IN_SPREAD_KEY, false)
|
||||
}
|
||||
|
||||
internal fun savePdfTopTabStripVisible(context: Context, isVisible: Boolean) {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.edit { putBoolean(PDF_TOP_TAB_STRIP_VISIBLE_KEY, isVisible) }
|
||||
}
|
||||
|
||||
internal fun loadPdfTopTabStripVisible(context: Context): Boolean {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
return prefs.getBoolean(PDF_TOP_TAB_STRIP_VISIBLE_KEY, true)
|
||||
}
|
||||
|
||||
internal fun savePdfThemeId(context: Context, themeId: String) {
|
||||
val prefs = context.getSharedPreferences(SETTINGS_PREFS_NAME, Context.MODE_PRIVATE)
|
||||
prefs.edit { putString(PDF_THEME_KEY, themeId) }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue