New languages (#256)
* Remove custom-onnxruntime-arm64.aar * Refactor remaining, hardcoded UI strings into localizable resources and added French and Russian to language list * Fix background auto-advance for Text-to-Speech (TTS)
This commit is contained in:
parent
224d12d1fb
commit
4582b506f3
33 changed files with 940 additions and 488 deletions
|
|
@ -395,7 +395,7 @@ fun PenPlayground(onClose: () -> Unit) {
|
|||
painter = painterResource(
|
||||
id = R.drawable.close
|
||||
),
|
||||
contentDescription = "Close", tint = Color.Gray
|
||||
contentDescription = stringResource(R.string.action_close), tint = Color.Gray
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -505,4 +505,4 @@ fun PenPlayground(onClose: () -> Unit) {
|
|||
Spacer(Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ internal fun OcrLanguageSelectionDialog(
|
|||
) {
|
||||
RadioButton(selected = (language == currentLanguage), onClick = null)
|
||||
Text(
|
||||
text = language.displayName,
|
||||
text = stringResource(language.displayNameRes),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.padding(start = 16.dp)
|
||||
)
|
||||
|
|
@ -146,4 +146,4 @@ internal fun OcrLanguageSelectionDialog(
|
|||
}
|
||||
}
|
||||
}, confirmButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.action_cancel)) } })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,12 +60,14 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.aryan.reader.R
|
||||
import io.legere.pdfiumandroid.api.Bookmark
|
||||
import io.legere.pdfiumandroid.suspend.PdfDocumentKt
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -255,7 +257,9 @@ internal fun PdfTocTreeItem(
|
|||
if (hasChildren) {
|
||||
Icon(
|
||||
imageVector = if (isExpanded) Icons.Default.KeyboardArrowDown else Icons.AutoMirrored.Filled.KeyboardArrowRight,
|
||||
contentDescription = if (isExpanded) "Collapse" else "Expand",
|
||||
contentDescription = stringResource(
|
||||
if (isExpanded) R.string.content_desc_collapse else R.string.content_desc_expand
|
||||
),
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
|
@ -301,13 +305,13 @@ internal fun PdfNavigationDrawerContent(
|
|||
) {
|
||||
Tab(selected = drawerPagerState.currentPage == 0, onClick = {
|
||||
drawerScope.launch { drawerPagerState.animateScrollToPage(0) }
|
||||
}, text = { Text("Chapters") })
|
||||
}, text = { Text(stringResource(R.string.tab_chapters)) })
|
||||
Tab(
|
||||
selected = drawerPagerState.currentPage == 1,
|
||||
onClick = {
|
||||
drawerScope.launch { drawerPagerState.animateScrollToPage(1) }
|
||||
},
|
||||
text = { Text("Bookmarks") },
|
||||
text = { Text(stringResource(R.string.tab_bookmarks)) },
|
||||
modifier = Modifier.testTag("BookmarksTab")
|
||||
)
|
||||
Tab(
|
||||
|
|
@ -315,7 +319,7 @@ internal fun PdfNavigationDrawerContent(
|
|||
onClick = {
|
||||
drawerScope.launch { drawerPagerState.animateScrollToPage(2) }
|
||||
},
|
||||
text = { Text("Highlights") },
|
||||
text = { Text(stringResource(R.string.tab_highlights)) },
|
||||
modifier = Modifier.testTag("HighlightsTab")
|
||||
)
|
||||
Tab(
|
||||
|
|
@ -323,7 +327,7 @@ internal fun PdfNavigationDrawerContent(
|
|||
onClick = {
|
||||
drawerScope.launch { drawerPagerState.animateScrollToPage(3) }
|
||||
},
|
||||
text = { Text("Pages") },
|
||||
text = { Text(stringResource(R.string.tab_pages)) },
|
||||
modifier = Modifier.testTag("PagesTab")
|
||||
)
|
||||
}
|
||||
|
|
@ -340,7 +344,7 @@ internal fun PdfNavigationDrawerContent(
|
|||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
"Chapters are not available for this book.",
|
||||
stringResource(R.string.msg_chapters_not_available),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
|
@ -434,13 +438,13 @@ internal fun PdfNavigationDrawerContent(
|
|||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
TextButton(onClick = { expandedEntryIndices = flatTableOfContents.indices.toSet() }) {
|
||||
Text("Expand All")
|
||||
Text(stringResource(R.string.action_expand_all))
|
||||
}
|
||||
TextButton(onClick = { expandedEntryIndices = emptySet() }) {
|
||||
Text("Collapse All")
|
||||
Text(stringResource(R.string.action_collapse_all))
|
||||
}
|
||||
TextButton(onClick = onScrollToCurrent) {
|
||||
Text("Locate")
|
||||
Text(stringResource(R.string.action_locate))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -503,7 +507,7 @@ internal fun PdfNavigationDrawerContent(
|
|||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
"You haven't added any bookmarks yet.",
|
||||
stringResource(R.string.no_bookmarks_yet),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
|
@ -531,7 +535,7 @@ internal fun PdfNavigationDrawerContent(
|
|||
)
|
||||
}, supportingContent = {
|
||||
Text(
|
||||
"Page ${bookmark.pageIndex + 1} of ${bookmark.totalPages}",
|
||||
stringResource(R.string.page_of_pages, bookmark.pageIndex + 1, bookmark.totalPages),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}, trailingContent = {
|
||||
|
|
@ -542,7 +546,7 @@ internal fun PdfNavigationDrawerContent(
|
|||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = "More options for bookmark"
|
||||
contentDescription = stringResource(R.string.content_desc_more_options_bookmark)
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
|
|
@ -551,13 +555,13 @@ internal fun PdfNavigationDrawerContent(
|
|||
bookmarkMenuExpandedFor = null
|
||||
}) {
|
||||
DropdownMenuItem(text = {
|
||||
Text("Rename")
|
||||
Text(stringResource(R.string.action_rename))
|
||||
}, onClick = {
|
||||
showRenameBookmarkDialog = bookmark
|
||||
bookmarkMenuExpandedFor = null
|
||||
})
|
||||
DropdownMenuItem(text = {
|
||||
Text("Delete")
|
||||
Text(stringResource(R.string.action_delete))
|
||||
}, onClick = {
|
||||
showDeleteConfirmDialogFor = bookmark
|
||||
bookmarkMenuExpandedFor = null
|
||||
|
|
@ -581,11 +585,11 @@ internal fun PdfNavigationDrawerContent(
|
|||
|
||||
AlertDialog(onDismissRequest = {
|
||||
showRenameBookmarkDialog = null
|
||||
}, title = { Text("Rename Bookmark") }, text = {
|
||||
}, title = { Text(stringResource(R.string.dialog_rename_bookmark)) }, text = {
|
||||
OutlinedTextField(
|
||||
value = newTitle,
|
||||
onValueChange = { newTitle = it },
|
||||
label = { Text("New Title") },
|
||||
label = { Text(stringResource(R.string.label_new_title)) },
|
||||
placeholder = {
|
||||
Text(
|
||||
text = bookmarkToRename.title,
|
||||
|
|
@ -605,33 +609,33 @@ internal fun PdfNavigationDrawerContent(
|
|||
onClick = {
|
||||
onRenameBookmark(bookmarkToRename, newTitle)
|
||||
showRenameBookmarkDialog = null
|
||||
}) { Text("Save") }
|
||||
}) { Text(stringResource(R.string.action_save)) }
|
||||
}, dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
showRenameBookmarkDialog = null
|
||||
}) { Text("Cancel") }
|
||||
}) { Text(stringResource(R.string.action_cancel)) }
|
||||
})
|
||||
}
|
||||
|
||||
showDeleteConfirmDialogFor?.let { bookmarkToDelete ->
|
||||
AlertDialog(onDismissRequest = {
|
||||
showDeleteConfirmDialogFor = null
|
||||
}, title = { Text("Delete Bookmark?") }, text = {
|
||||
}, title = { Text(stringResource(R.string.dialog_delete_bookmark)) }, text = {
|
||||
Text(
|
||||
"Are you sure you want to permanently delete this bookmark?"
|
||||
stringResource(R.string.dialog_delete_bookmark_desc)
|
||||
)
|
||||
}, confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onDeleteBookmark(bookmarkToDelete)
|
||||
showDeleteConfirmDialogFor = null
|
||||
}) { Text("Delete") }
|
||||
}) { Text(stringResource(R.string.action_delete)) }
|
||||
}, dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
showDeleteConfirmDialogFor = null
|
||||
}) { Text("Cancel") }
|
||||
}) { Text(stringResource(R.string.action_cancel)) }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -645,7 +649,7 @@ internal fun PdfNavigationDrawerContent(
|
|||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
"You haven't added any highlights yet.",
|
||||
stringResource(R.string.no_highlights_yet),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
|
@ -662,12 +666,12 @@ internal fun PdfNavigationDrawerContent(
|
|||
FilterChip(
|
||||
selected = !filterWithNotesOnly,
|
||||
onClick = { filterWithNotesOnly = false },
|
||||
label = { Text("All") }
|
||||
label = { Text(stringResource(R.string.read_status_all)) }
|
||||
)
|
||||
FilterChip(
|
||||
selected = filterWithNotesOnly,
|
||||
onClick = { filterWithNotesOnly = true },
|
||||
label = { Text("With Notes") }
|
||||
label = { Text(stringResource(R.string.filter_with_notes)) }
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -689,7 +693,7 @@ internal fun PdfNavigationDrawerContent(
|
|||
ListItem(
|
||||
headlineContent = {
|
||||
Text(
|
||||
text = highlight.text.ifBlank { "Highlighted section" },
|
||||
text = highlight.text.ifBlank { stringResource(R.string.msg_highlighted_section_default) },
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
|
|
@ -707,7 +711,7 @@ internal fun PdfNavigationDrawerContent(
|
|||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(
|
||||
"Page ${highlight.pageIndex + 1}",
|
||||
stringResource(R.string.pdf_page_short, highlight.pageIndex + 1),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
|
@ -733,14 +737,20 @@ internal fun PdfNavigationDrawerContent(
|
|||
Box {
|
||||
var highlightMenuExpanded by remember { mutableStateOf(false) }
|
||||
IconButton(onClick = { highlightMenuExpanded = true }) {
|
||||
Icon(Icons.Default.MoreVert, contentDescription = "Options")
|
||||
Icon(Icons.Default.MoreVert, contentDescription = stringResource(R.string.content_desc_options))
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = highlightMenuExpanded,
|
||||
onDismissRequest = { highlightMenuExpanded = false }
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(if (highlight.note.isNullOrBlank()) "Add Note" else "Edit Note") },
|
||||
text = {
|
||||
Text(
|
||||
stringResource(
|
||||
if (highlight.note.isNullOrBlank()) R.string.menu_add_note else R.string.menu_edit_note
|
||||
)
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
onNoteRequested(highlight.id)
|
||||
highlightMenuExpanded = false
|
||||
|
|
@ -748,7 +758,7 @@ internal fun PdfNavigationDrawerContent(
|
|||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("Delete") },
|
||||
text = { Text(stringResource(R.string.action_delete)) },
|
||||
onClick = {
|
||||
showDeleteConfirmDialogFor = highlight
|
||||
highlightMenuExpanded = false
|
||||
|
|
@ -770,20 +780,20 @@ internal fun PdfNavigationDrawerContent(
|
|||
showDeleteConfirmDialogFor?.let { highlightToDelete ->
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDeleteConfirmDialogFor = null },
|
||||
title = { Text("Delete Highlight?") },
|
||||
text = { Text("Are you sure you want to permanently delete this highlight?") },
|
||||
title = { Text(stringResource(R.string.dialog_delete_highlight)) },
|
||||
text = { Text(stringResource(R.string.dialog_delete_highlight_desc)) },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onDeleteHighlight(highlightToDelete)
|
||||
showDeleteConfirmDialogFor = null
|
||||
}
|
||||
) { Text("Delete") }
|
||||
) { Text(stringResource(R.string.action_delete)) }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = { showDeleteConfirmDialogFor = null }
|
||||
) { Text("Cancel") }
|
||||
) { Text(stringResource(R.string.action_cancel)) }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
@ -809,7 +819,7 @@ internal fun PdfNavigationDrawerContent(
|
|||
}
|
||||
}
|
||||
) {
|
||||
Text("Locate")
|
||||
Text(stringResource(R.string.action_locate))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -880,7 +890,7 @@ internal fun PdfNavigationDrawerContent(
|
|||
if (thumb != null) {
|
||||
Image(
|
||||
bitmap = thumb!!.asImageBitmap(),
|
||||
contentDescription = "Page ${pageIdx + 1}",
|
||||
contentDescription = stringResource(R.string.pdf_page_short, pageIdx + 1),
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
|
|
@ -916,4 +926,4 @@ internal fun PdfNavigationDrawerContent(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ package com.aryan.reader.pdf
|
|||
import android.graphics.Bitmap
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
|
|
@ -78,7 +79,9 @@ import androidx.compose.ui.graphics.Brush
|
|||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
|
|
@ -95,12 +98,12 @@ import com.aryan.reader.pdf.ocr.OcrSymbol
|
|||
import timber.log.Timber
|
||||
import java.util.UUID
|
||||
|
||||
enum class OcrLanguage(val displayName: String) {
|
||||
LATIN("English, Spanish, French, etc."),
|
||||
DEVANAGARI("Hindi, Marathi, Sanskrit + English"),
|
||||
CHINESE("Chinese + English"),
|
||||
JAPANESE("Japanese + English"),
|
||||
KOREAN("Korean + English")
|
||||
enum class OcrLanguage(@StringRes val displayNameRes: Int) {
|
||||
LATIN(R.string.ocr_language_latin),
|
||||
DEVANAGARI(R.string.ocr_language_devanagari),
|
||||
CHINESE(R.string.ocr_language_chinese),
|
||||
JAPANESE(R.string.ocr_language_japanese),
|
||||
KOREAN(R.string.ocr_language_korean)
|
||||
}
|
||||
|
||||
internal data class OcrSymbolInfo(
|
||||
|
|
@ -232,6 +235,8 @@ internal fun PdfSelectionMenuPopup(
|
|||
onTts: (() -> Unit)? = null,
|
||||
onNote: (() -> Unit)? = null
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Popup(
|
||||
popupPositionProvider = popupPositionProvider,
|
||||
onDismissRequest = onDismiss,
|
||||
|
|
@ -266,7 +271,7 @@ internal fun PdfSelectionMenuPopup(
|
|||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
contentDescription = "Note",
|
||||
contentDescription = stringResource(R.string.label_note),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(14.dp)
|
||||
)
|
||||
|
|
@ -321,7 +326,7 @@ internal fun PdfSelectionMenuPopup(
|
|||
) {
|
||||
Icon(Icons.Default.CopyAll, contentDescription = null, modifier = Modifier.size(18.dp))
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text("Copy Thread")
|
||||
Text(stringResource(R.string.action_copy_thread))
|
||||
}
|
||||
} else {
|
||||
Row(
|
||||
|
|
@ -360,23 +365,23 @@ internal fun PdfSelectionMenuPopup(
|
|||
HorizontalDivider()
|
||||
|
||||
val actions = mutableListOf<MenuActionItem>()
|
||||
actions.add(MenuActionItem(iconRes = R.drawable.copy, label = "Copy", onClick = { onCopy(menuState.selectedText) }))
|
||||
actions.add(MenuActionItem(iconRes = R.drawable.copy, label = context.getString(R.string.action_copy), onClick = { onCopy(menuState.selectedText) }))
|
||||
if (onTts != null) {
|
||||
actions.add(MenuActionItem(imageVector = Icons.AutoMirrored.Filled.VolumeUp, label = "Speak", onClick = onTts))
|
||||
actions.add(MenuActionItem(imageVector = Icons.AutoMirrored.Filled.VolumeUp, label = context.getString(R.string.label_speak), onClick = onTts))
|
||||
}
|
||||
if (menuState.selectedText.length <= 2000) {
|
||||
actions.add(MenuActionItem(iconRes = R.drawable.dictionary, label = "Dict", onClick = { onAiDefine(menuState.selectedText) }))
|
||||
actions.add(MenuActionItem(iconRes = R.drawable.translate, label = "Translate", onClick = { onTranslate(menuState.selectedText) }))
|
||||
actions.add(MenuActionItem(imageVector = Icons.Default.Search, label = "Search", onClick = { onSearch(menuState.selectedText) }))
|
||||
actions.add(MenuActionItem(iconRes = R.drawable.dictionary, label = context.getString(R.string.label_dict), onClick = { onAiDefine(menuState.selectedText) }))
|
||||
actions.add(MenuActionItem(iconRes = R.drawable.translate, label = context.getString(R.string.action_translate), onClick = { onTranslate(menuState.selectedText) }))
|
||||
actions.add(MenuActionItem(imageVector = Icons.Default.Search, label = context.getString(R.string.action_search), onClick = { onSearch(menuState.selectedText) }))
|
||||
}
|
||||
|
||||
if (onNote != null) {
|
||||
val noteLabel = if (menuState.note.isNullOrBlank()) "Note" else "Edit"
|
||||
val noteLabel = context.getString(if (menuState.note.isNullOrBlank()) R.string.label_note else R.string.label_edit)
|
||||
actions.add(MenuActionItem(imageVector = Icons.Default.Edit, label = noteLabel, onClick = onNote))
|
||||
}
|
||||
|
||||
if (!menuState.isExistingHighlight) {
|
||||
actions.add(MenuActionItem(iconRes = R.drawable.select_all, label = "Select All", onClick = { onSelectAll() }))
|
||||
actions.add(MenuActionItem(iconRes = R.drawable.select_all, label = context.getString(R.string.select_all), onClick = { onSelectAll() }))
|
||||
}
|
||||
if (menuState.isExistingHighlight) {
|
||||
actions.add(MenuActionItem(imageVector = Icons.Default.Delete, label = "Remove", onClick = { onDelete() }, isError = true))
|
||||
|
|
@ -659,7 +664,7 @@ fun PdfHighlightColorRow(
|
|||
if (selectedColor == colorEnum) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = "Selected",
|
||||
contentDescription = stringResource(R.string.content_desc_selected),
|
||||
tint = if (displayColor.luminance() > 0.5f) Color.Black else Color.White,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
|
|
@ -752,10 +757,10 @@ fun PdfAnnotationBottomSheet(
|
|||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
PdfBottomSheetToolButton(icon = R.drawable.copy, label = "Copy", effectiveText = effectiveText, onClick = onCopy)
|
||||
PdfBottomSheetToolButton(icon = R.drawable.dictionary, label = "Dict", effectiveText = effectiveText, onClick = onDictionary)
|
||||
PdfBottomSheetToolButton(icon = R.drawable.translate, label = "Translate", effectiveText = effectiveText, onClick = onTranslate)
|
||||
PdfBottomSheetToolButton(icon = R.drawable.search, label = "Search", effectiveText = effectiveText, onClick = onSearch)
|
||||
PdfBottomSheetToolButton(icon = R.drawable.copy, label = stringResource(R.string.action_copy), effectiveText = effectiveText, onClick = onCopy)
|
||||
PdfBottomSheetToolButton(icon = R.drawable.dictionary, label = stringResource(R.string.label_dict), effectiveText = effectiveText, onClick = onDictionary)
|
||||
PdfBottomSheetToolButton(icon = R.drawable.translate, label = stringResource(R.string.action_translate), effectiveText = effectiveText, onClick = onTranslate)
|
||||
PdfBottomSheetToolButton(icon = R.drawable.search, label = stringResource(R.string.action_search), effectiveText = effectiveText, onClick = onSearch)
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(16.dp))
|
||||
|
|
@ -763,7 +768,7 @@ fun PdfAnnotationBottomSheet(
|
|||
OutlinedTextField(
|
||||
value = noteText,
|
||||
onValueChange = { noteText = it },
|
||||
placeholder = { Text("Add a note...", color = effectiveText.copy(alpha = 0.5f)) },
|
||||
placeholder = { Text(stringResource(R.string.placeholder_add_note), color = effectiveText.copy(alpha = 0.5f)) },
|
||||
modifier = Modifier.fillMaxWidth().heightIn(min = 100.dp),
|
||||
maxLines = 5,
|
||||
colors = OutlinedTextFieldDefaults.colors(
|
||||
|
|
@ -793,7 +798,7 @@ fun PdfAnnotationBottomSheet(
|
|||
) {
|
||||
Icon(Icons.Default.Delete, contentDescription = null, modifier = Modifier.size(18.dp))
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text("Delete")
|
||||
Text(stringResource(R.string.action_delete))
|
||||
}
|
||||
Button(
|
||||
onClick = { onSave(noteText) },
|
||||
|
|
@ -802,7 +807,7 @@ fun PdfAnnotationBottomSheet(
|
|||
contentColor = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
) {
|
||||
Text("Save Note")
|
||||
Text(stringResource(R.string.action_save_note))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -833,4 +838,4 @@ private fun PdfBottomSheetToolButton(
|
|||
color = effectiveText.copy(alpha = 0.8f)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ import androidx.compose.ui.graphics.graphicsLayer
|
|||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
|
|
@ -199,7 +200,7 @@ internal fun ThumbnailWithIndicator(
|
|||
) {
|
||||
Image(
|
||||
bitmap = thumbnail.asImageBitmap(),
|
||||
contentDescription = "Start page thumbnail",
|
||||
contentDescription = stringResource(R.string.content_desc_start_page_thumbnail),
|
||||
contentScale = ContentScale.FillBounds,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
|
|
@ -230,7 +231,7 @@ internal fun BookmarkButton(
|
|||
AnimatedVisibility(visible = isBookmarked, enter = fadeIn(), exit = fadeOut()) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.bookmark),
|
||||
contentDescription = "Bookmark",
|
||||
contentDescription = stringResource(R.string.content_desc_bookmark),
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
|
@ -271,7 +272,7 @@ internal fun ZoomPercentageIndicator(
|
|||
// Reset Zoom Button
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.zoom_out),
|
||||
contentDescription = "Reset Zoom",
|
||||
contentDescription = stringResource(R.string.content_desc_reset_zoom),
|
||||
tint = Color.White,
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
|
|
@ -280,4 +281,4 @@ internal fun ZoomPercentageIndicator(
|
|||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.testTag
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
|
|
@ -73,7 +74,7 @@ internal fun SearchNavigationPill(
|
|||
Icon(
|
||||
imageVector = if (mode == SearchHighlightMode.ALL) Icons.Default.Visibility
|
||||
else Icons.Default.VisibilityOff,
|
||||
contentDescription = "Toggle Highlights",
|
||||
contentDescription = stringResource(R.string.content_desc_toggle_search_highlights),
|
||||
tint = if (mode == SearchHighlightMode.ALL) MaterialTheme.colorScheme.primary
|
||||
else MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
|
@ -95,7 +96,7 @@ internal fun SearchNavigationPill(
|
|||
IconButton(onClick = onPrev, enabled = isPrevEnabled) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowUp,
|
||||
contentDescription = "Previous",
|
||||
contentDescription = stringResource(R.string.tooltip_prev_result),
|
||||
tint = if (isPrevEnabled) MaterialTheme.colorScheme.onSurface
|
||||
else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
|
||||
)
|
||||
|
|
@ -122,7 +123,7 @@ internal fun SearchNavigationPill(
|
|||
IconButton(onClick = onNext, enabled = isNextEnabled) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowDown,
|
||||
contentDescription = "Next",
|
||||
contentDescription = stringResource(R.string.tooltip_next_result),
|
||||
tint = if (isNextEnabled) MaterialTheme.colorScheme.onSurface
|
||||
else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
|
||||
)
|
||||
|
|
@ -141,12 +142,12 @@ fun PdfSearchResultsPanel(
|
|||
Surface(modifier = modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
|
||||
if (lazyResults.itemCount == 0 && lazyResults.loadState.refresh !is LoadState.Loading) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text("No results found.", style = MaterialTheme.typography.bodyLarge)
|
||||
Text(stringResource(R.string.search_no_results_simple), style = MaterialTheme.typography.bodyLarge)
|
||||
}
|
||||
} else {
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.msg_results_found_pages),
|
||||
text = stringResource(R.string.msg_results_found_pages, totalPageCount),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||
)
|
||||
|
|
@ -195,6 +196,7 @@ fun PdfSearchResultsList(
|
|||
onResultClick: (SearchResult) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
Surface(modifier = modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
|
||||
if (results.isEmpty()) {
|
||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
|
|
@ -203,7 +205,11 @@ fun PdfSearchResultsList(
|
|||
} else {
|
||||
Column {
|
||||
Text(
|
||||
text = "${results.size} matches found",
|
||||
text = context.resources.getQuantityString(
|
||||
R.plurals.search_matches_count,
|
||||
results.size,
|
||||
results.size
|
||||
),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||
)
|
||||
|
|
@ -233,4 +239,4 @@ fun PdfSearchResultsList(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ import androidx.compose.ui.input.pointer.pointerInput
|
|||
import androidx.compose.ui.input.pointer.positionChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
|
|
@ -450,9 +451,9 @@ private fun DragPill(
|
|||
Box(contentAlignment = Alignment.Center) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.drag_handle),
|
||||
contentDescription = "Drag to move text box",
|
||||
contentDescription = stringResource(R.string.content_desc_drag_text_box),
|
||||
modifier = Modifier.size((20f / scale).dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,14 +136,14 @@ internal fun PdfTopBar(
|
|||
description = stringResource(R.string.tooltip_back_desc),
|
||||
onClick = onNavigateBack
|
||||
) {
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back")
|
||||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.action_back))
|
||||
}
|
||||
val titleText = when {
|
||||
isLoadingDocument -> stringResource(R.string.loading_pdf)
|
||||
errorMessage != null -> stringResource(R.string.error_loading_pdf)
|
||||
totalPages > 0 && pagerStatePageCount > 0 -> "Page ${currentPageForDisplay + 1} of $totalPages"
|
||||
totalPages > 0 && pagerStatePageCount > 0 -> stringResource(R.string.page_of_pages, currentPageForDisplay + 1, totalPages)
|
||||
totalPages > 0 && pagerStatePageCount == 0 -> stringResource(R.string.loading_page)
|
||||
else -> "PDF Viewer"
|
||||
else -> stringResource(R.string.pdf_viewer)
|
||||
}
|
||||
Text(
|
||||
text = titleText,
|
||||
|
|
@ -179,16 +179,16 @@ internal fun PdfTopBar(
|
|||
description = stringResource(R.string.tooltip_dictionary_desc),
|
||||
onClick = onShowDictionarySettings
|
||||
) {
|
||||
Icon(painterResource(id = R.drawable.dictionary), contentDescription = "Dictionary Settings", tint = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
Icon(painterResource(id = R.drawable.dictionary), contentDescription = stringResource(R.string.content_desc_dictionary_settings), tint = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
}
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
TooltipIconButton(text = "Demo Annotations", onClick = onGenerateDemoAnnotations) {
|
||||
Icon(Icons.Default.BugReport, contentDescription = "Generate Demo Annotations", tint = MaterialTheme.colorScheme.secondary)
|
||||
TooltipIconButton(text = stringResource(R.string.tooltip_demo_annotations), onClick = onGenerateDemoAnnotations) {
|
||||
Icon(Icons.Default.BugReport, contentDescription = stringResource(R.string.content_desc_generate_demo_annotations), tint = MaterialTheme.colorScheme.secondary)
|
||||
}
|
||||
TooltipIconButton(text = stringResource(R.string.pen_playground), onClick = onShowPenPlayground) {
|
||||
Icon(Icons.Default.Star, contentDescription = "Open Pen Playground", tint = MaterialTheme.colorScheme.primary)
|
||||
Icon(Icons.Default.Star, contentDescription = stringResource(R.string.content_desc_open_pen_playground), tint = MaterialTheme.colorScheme.primary)
|
||||
}
|
||||
TooltipIconButton(text = stringResource(R.string.import_svg), onClick = onImportSvg) {
|
||||
Icon(Icons.Default.Brush, contentDescription = stringResource(R.string.import_svg), tint = Color(0xFFE91E63))
|
||||
|
|
@ -392,7 +392,7 @@ internal fun PdfTopBar(
|
|||
|
||||
item {
|
||||
IconButton(onClick = onNewTabClick, modifier = Modifier.padding(start = 8.dp, bottom = 4.dp).size(36.dp)) {
|
||||
Icon(Icons.Default.Add, contentDescription = "New Tab", tint = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
Icon(Icons.Default.Add, contentDescription = stringResource(R.string.content_desc_new_tab), tint = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -492,8 +492,8 @@ fun PdfBottomBar(
|
|||
) {
|
||||
if (jumpBackPage != null) {
|
||||
TooltipIconButton(
|
||||
text = "Jump Back to Page ${jumpBackPage + 1}",
|
||||
description = "Return to previous page",
|
||||
text = stringResource(R.string.action_jump_back_to_page, jumpBackPage + 1),
|
||||
description = stringResource(R.string.desc_return_to_previous_page),
|
||||
onClick = onJumpBack
|
||||
) {
|
||||
Column(
|
||||
|
|
@ -502,7 +502,7 @@ fun PdfBottomBar(
|
|||
) {
|
||||
Icon(
|
||||
Icons.AutoMirrored.Filled.Undo,
|
||||
contentDescription = "Jump Back",
|
||||
contentDescription = stringResource(R.string.content_desc_jump_back),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
|
|
@ -535,7 +535,7 @@ fun PdfBottomBar(
|
|||
enabled = !isTtsPlayingOrLoading,
|
||||
modifier = Modifier.testTag("TocButton")
|
||||
) {
|
||||
Icon(Icons.Default.Menu, contentDescription = "Table of Contents")
|
||||
Icon(Icons.Default.Menu, contentDescription = stringResource(R.string.content_desc_table_of_contents))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -557,7 +557,7 @@ fun PdfBottomBar(
|
|||
onClick = onToggleHighlights
|
||||
) {
|
||||
if (isHighlightingLoading) CircularProgressIndicator(Modifier.size(24.dp))
|
||||
else Icon(painterResource(id = R.drawable.highlight_text), contentDescription = "Highlight all text", tint = if (showAllTextHighlights) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
else Icon(painterResource(id = R.drawable.highlight_text), contentDescription = stringResource(R.string.content_desc_highlight_all_text), tint = if (showAllTextHighlights) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -577,7 +577,7 @@ fun PdfBottomBar(
|
|||
description = if (isEditMode) stringResource(R.string.tooltip_edit_mode_exit_desc) else stringResource(R.string.tooltip_edit_mode_desc),
|
||||
onClick = onToggleEditMode
|
||||
) {
|
||||
Icon(Icons.Default.Edit, contentDescription = "Toggle Editing Mode", tint = if (isEditMode) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
Icon(Icons.Default.Edit, contentDescription = stringResource(R.string.content_desc_toggle_editing_mode), tint = if (isEditMode) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -587,19 +587,19 @@ fun PdfBottomBar(
|
|||
description = if (isTtsSessionActive) stringResource(R.string.tooltip_tts_stop_desc) else stringResource(R.string.tooltip_tts_start_desc),
|
||||
onClick = onToggleTts
|
||||
) {
|
||||
Icon(if (isTtsSessionActive) painterResource(id = R.drawable.close) else painterResource(id = R.drawable.text_to_speech), contentDescription = if (isTtsSessionActive) "Stop TTS" else "Start TTS", tint = if (isTtsSessionActive) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
Icon(if (isTtsSessionActive) painterResource(id = R.drawable.close) else painterResource(id = R.drawable.text_to_speech), contentDescription = if (isTtsSessionActive) stringResource(R.string.content_desc_stop_tts) else stringResource(R.string.content_desc_start_tts), tint = if (isTtsSessionActive) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant)
|
||||
}
|
||||
}
|
||||
|
||||
if (BuildConfig.FLAVOR != "oss") {
|
||||
TooltipIconButton(
|
||||
text = if (isBubbleZoomModeActive) "Exit Smart Zoom" else "Smart Comic Zoom",
|
||||
description = "Toggle Smart Comic Zoom",
|
||||
text = if (isBubbleZoomModeActive) stringResource(R.string.action_exit_smart_zoom) else stringResource(R.string.action_smart_comic_zoom),
|
||||
description = stringResource(R.string.desc_toggle_smart_comic_zoom),
|
||||
onClick = onToggleBubbleZoom
|
||||
) {
|
||||
Icon(
|
||||
painterResource(R.drawable.comic_bubble),
|
||||
contentDescription = "Smart Comic Zoom",
|
||||
contentDescription = stringResource(R.string.content_desc_smart_comic_zoom),
|
||||
tint = if (isBubbleZoomModeActive) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2089,7 +2089,7 @@ fun PdfViewerScreen(
|
|||
if (!selectedDictPackage.isNullOrEmpty()) {
|
||||
ExternalDictionaryHelper.launchDictionary(context, selectedDictPackage!!, text)
|
||||
} else {
|
||||
Toast.makeText(context, "Please select a dictionary app first.", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, context.getString(R.string.toast_select_dictionary_first), Toast.LENGTH_SHORT).show()
|
||||
showDictionarySettingsSheet = true
|
||||
}
|
||||
}
|
||||
|
|
@ -2102,7 +2102,7 @@ fun PdfViewerScreen(
|
|||
if (!selectedTranslatePackage.isNullOrEmpty()) {
|
||||
ExternalDictionaryHelper.launchTranslate(context, selectedTranslatePackage!!, text)
|
||||
} else {
|
||||
Toast.makeText(context, "Please select a translate app first.", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, context.getString(R.string.toast_select_translate_first), Toast.LENGTH_SHORT).show()
|
||||
showDictionarySettingsSheet = true
|
||||
}
|
||||
}
|
||||
|
|
@ -2113,7 +2113,7 @@ fun PdfViewerScreen(
|
|||
if (!selectedSearchPackage.isNullOrEmpty()) {
|
||||
ExternalDictionaryHelper.launchSearch(context, selectedSearchPackage!!, text)
|
||||
} else {
|
||||
Toast.makeText(context, "Please select a search app first.", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, context.getString(R.string.toast_select_search_first), Toast.LENGTH_SHORT).show()
|
||||
showDictionarySettingsSheet = true
|
||||
}
|
||||
}
|
||||
|
|
@ -3386,7 +3386,7 @@ fun PdfViewerScreen(
|
|||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = errorMessage ?: "Failed to load PDF.",
|
||||
text = errorMessage ?: stringResource(R.string.error_failed_load_pdf),
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
|
|
@ -4380,7 +4380,10 @@ fun PdfViewerScreen(
|
|||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Text(
|
||||
text = "Downloading ${ocrLanguage.displayName.substringBefore("(")} language pack...",
|
||||
text = stringResource(
|
||||
R.string.msg_downloading_language_pack,
|
||||
stringResource(ocrLanguage.displayNameRes).substringBefore("(").trim()
|
||||
),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.onTertiaryContainer
|
||||
)
|
||||
|
|
@ -4429,7 +4432,10 @@ fun PdfViewerScreen(
|
|||
}
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Text(
|
||||
text = "Downloading Bubble Zoom model... ${(progress * 100).toInt()}%",
|
||||
text = stringResource(
|
||||
R.string.msg_downloading_bubble_zoom_model_progress,
|
||||
(progress * 100).toInt()
|
||||
),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.onTertiaryContainer
|
||||
)
|
||||
|
|
@ -4473,7 +4479,7 @@ fun PdfViewerScreen(
|
|||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Exit slider navigation"
|
||||
contentDescription = stringResource(R.string.content_desc_exit_slider_navigation)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -4913,15 +4919,23 @@ fun PdfViewerScreen(
|
|||
)
|
||||
else -1
|
||||
val text =
|
||||
if (index >= 0) "Result ${index + 1} / ${searchData.matches.size}"
|
||||
else "${searchData.matches.size} Results"
|
||||
if (index >= 0) context.getString(
|
||||
R.string.pdf_search_result_position,
|
||||
index + 1,
|
||||
searchData.matches.size
|
||||
)
|
||||
else context.resources.getQuantityString(
|
||||
R.plurals.search_results_count,
|
||||
searchData.matches.size,
|
||||
searchData.matches.size
|
||||
)
|
||||
Triple(text, index > 0, index < searchData.matches.size - 1)
|
||||
}
|
||||
|
||||
is SmartSearchResult.Paged -> {
|
||||
val page = currentResult?.locationInSource
|
||||
val text = if (page != null) "Page ${page + 1}"
|
||||
else "${searchData.totalPageCount}+ Pages"
|
||||
val text = if (page != null) context.getString(R.string.pdf_page_short, page + 1)
|
||||
else context.getString(R.string.msg_search_pages_count, searchData.totalPageCount)
|
||||
Triple(text, true, true)
|
||||
}
|
||||
|
||||
|
|
@ -5046,9 +5060,9 @@ fun PdfViewerScreen(
|
|||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(Icons.AutoMirrored.Filled.Undo, contentDescription = "Jump Back", modifier = Modifier.size(18.dp))
|
||||
Icon(Icons.AutoMirrored.Filled.Undo, contentDescription = stringResource(R.string.content_desc_jump_back), modifier = Modifier.size(18.dp))
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text("Back to Pg ${lastPage + 1}", style = MaterialTheme.typography.labelLarge)
|
||||
Text(stringResource(R.string.pdf_back_to_page_short, lastPage + 1), style = MaterialTheme.typography.labelLarge)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5535,7 +5549,7 @@ fun PdfViewerScreen(
|
|||
Icon(
|
||||
imageVector = if (isTtsPageBelow) Icons.Default.ArrowDownward
|
||||
else Icons.Default.ArrowUpward,
|
||||
contentDescription = "Scroll to reading page"
|
||||
contentDescription = stringResource(R.string.content_desc_scroll_to_reading_page)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5817,10 +5831,10 @@ fun PdfViewerScreen(
|
|||
contentDescription = null
|
||||
)
|
||||
},
|
||||
title = { Text("Unlock Page Summarization") },
|
||||
title = { Text(stringResource(R.string.dialog_unlock_page_summarization)) },
|
||||
text = {
|
||||
Text(
|
||||
"Get concise summaries of any page with Episteme Pro. Upgrade to start using this feature."
|
||||
stringResource(R.string.dialog_unlock_page_summarization_desc)
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
|
|
@ -5841,13 +5855,13 @@ fun PdfViewerScreen(
|
|||
AlertDialog(
|
||||
onDismissRequest = { showInsufficientCreditsDialog = false },
|
||||
icon = { Icon(painterResource(id = R.drawable.crown), contentDescription = null) },
|
||||
title = { Text("Out of Credits") },
|
||||
text = { Text("You don't have enough credits. Get Episteme Pro for 10 free Summaries per day, or add more credits to use Summaries, Cloud TTS and Story Recap.") },
|
||||
title = { Text(stringResource(R.string.dialog_out_of_credits_title)) },
|
||||
text = { Text(stringResource(R.string.dialog_out_of_credits_desc)) },
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
showInsufficientCreditsDialog = false
|
||||
onNavigateToPro()
|
||||
}) { Text("Get Pro / Add Credits") }
|
||||
}) { Text(stringResource(R.string.action_get_pro_or_add_credits)) }
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showInsufficientCreditsDialog = false }) {
|
||||
|
|
@ -5874,7 +5888,7 @@ fun PdfViewerScreen(
|
|||
) {
|
||||
Image(
|
||||
bitmap = poppedUpPanelBitmap!!.asImageBitmap(),
|
||||
contentDescription = "Annotated Page",
|
||||
contentDescription = stringResource(R.string.content_desc_annotated_page),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
|
|
@ -5894,7 +5908,7 @@ fun PdfViewerScreen(
|
|||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = "Close Image",
|
||||
contentDescription = stringResource(R.string.content_desc_close_image),
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
|
|
@ -5913,16 +5927,16 @@ fun PdfViewerScreen(
|
|||
AlertDialog(
|
||||
onDismissRequest = { showBubbleZoomDownloadDialog = false },
|
||||
icon = { Icon(Icons.Default.Info, contentDescription = null) },
|
||||
title = { Text("Download Bubble Zoom Model") },
|
||||
title = { Text(stringResource(R.string.dialog_download_bubble_zoom_model)) },
|
||||
text = {
|
||||
Text("To use the Bubble Zoom feature, an AI model needs to be downloaded (~134 MB). Do you want to download it now?")
|
||||
Text(stringResource(R.string.dialog_download_bubble_zoom_model_desc))
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = {
|
||||
showBubbleZoomDownloadDialog = false
|
||||
viewModel.downloadSpeechBubbleModel(context)
|
||||
}) {
|
||||
Text("Download")
|
||||
Text(stringResource(R.string.action_download))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.work.CoroutineWorker
|
|||
import androidx.work.WorkerParameters
|
||||
import androidx.work.workDataOf
|
||||
import com.aryan.reader.FileType
|
||||
import com.aryan.reader.R
|
||||
import com.aryan.reader.data.RecentFileItem
|
||||
import com.aryan.reader.data.RecentFilesRepository
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -32,7 +33,8 @@ class ReflowWorker(
|
|||
Timber.tag("PdfToHtmlPerf").e("FAILURE: KEY_PDF_URI is null | bookId=$bookId")
|
||||
return@withContext Result.failure()
|
||||
}
|
||||
val originalTitle = inputData.getString(KEY_ORIGINAL_TITLE) ?: "Document"
|
||||
val originalTitle = inputData.getString(KEY_ORIGINAL_TITLE)
|
||||
?: applicationContext.getString(R.string.default_document_title)
|
||||
val reflowBookId = "${bookId}_reflow"
|
||||
|
||||
Timber.tag("PdfToHtmlPerf").d(
|
||||
|
|
@ -67,11 +69,11 @@ class ReflowWorker(
|
|||
bookId = reflowBookId,
|
||||
uriString = destFile.toUri().toString(),
|
||||
type = FileType.HTML,
|
||||
displayName = "$originalTitle (Text View)",
|
||||
displayName = applicationContext.getString(R.string.reflow_display_name_format, originalTitle),
|
||||
timestamp = System.currentTimeMillis(),
|
||||
coverImagePath = null,
|
||||
title = "$originalTitle (Reflow)",
|
||||
author = "Generated",
|
||||
title = applicationContext.getString(R.string.reflow_title_format, originalTitle),
|
||||
author = applicationContext.getString(R.string.generated_author),
|
||||
isAvailable = true,
|
||||
isRecent = true,
|
||||
lastModifiedTimestamp = System.currentTimeMillis(),
|
||||
|
|
@ -101,4 +103,4 @@ class ReflowWorker(
|
|||
const val KEY_ORIGINAL_TITLE = "original_title"
|
||||
const val KEY_PROGRESS = "progress"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -409,7 +409,7 @@ fun TextAnnotationDock(
|
|||
FormattingIconButton(
|
||||
isSelected = activePopup == ActivePopup.FONT_FAMILY,
|
||||
iconRes = R.drawable.fonts,
|
||||
contentDescription = "Select Font Family",
|
||||
contentDescription = stringResource(R.string.content_desc_select_font_family),
|
||||
onClick = {
|
||||
activePopup =
|
||||
if (activePopup == ActivePopup.FONT_FAMILY) ActivePopup.NONE else ActivePopup.FONT_FAMILY
|
||||
|
|
@ -481,7 +481,7 @@ fun TextAnnotationDock(
|
|||
)
|
||||
Icon(
|
||||
imageVector = Icons.Default.KeyboardArrowDown,
|
||||
contentDescription = "Select Font Size",
|
||||
contentDescription = stringResource(R.string.content_desc_select_font_size),
|
||||
tint = Color.Gray,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
|
|
@ -561,7 +561,7 @@ fun TextAnnotationDock(
|
|||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.font_background),
|
||||
contentDescription = "Font Background",
|
||||
contentDescription = stringResource(R.string.content_desc_font_background),
|
||||
modifier = Modifier.size(17.dp),
|
||||
tint = Color.Black
|
||||
)
|
||||
|
|
@ -587,7 +587,7 @@ fun TextAnnotationDock(
|
|||
FormattingIconButton(
|
||||
isSelected = isBold,
|
||||
iconRes = R.drawable.format_bold,
|
||||
contentDescription = "Bold",
|
||||
contentDescription = stringResource(R.string.content_desc_bold),
|
||||
onClick = {
|
||||
val newW = if (isBold) FontWeight.Normal else FontWeight.Bold
|
||||
onUpdateStyle(currentStyle.copy(fontWeight = newW, fontFamily = currentStyle.fontFamily))
|
||||
|
|
@ -605,7 +605,7 @@ fun TextAnnotationDock(
|
|||
FormattingIconButton(
|
||||
isSelected = isItalic,
|
||||
iconRes = R.drawable.format_italic,
|
||||
contentDescription = "Italic",
|
||||
contentDescription = stringResource(R.string.content_desc_italic),
|
||||
onClick = {
|
||||
val newStyle = if (isItalic) FontStyle.Normal else FontStyle.Italic
|
||||
onUpdateStyle(currentStyle.copy(fontStyle = newStyle, fontFamily = currentStyle.fontFamily))
|
||||
|
|
@ -625,7 +625,7 @@ fun TextAnnotationDock(
|
|||
FormattingIconButton(
|
||||
isSelected = isUnderline,
|
||||
iconRes = R.drawable.format_underlined,
|
||||
contentDescription = "Underline",
|
||||
contentDescription = stringResource(R.string.content_desc_underline),
|
||||
onClick = {
|
||||
val hasStrike = currentDec.contains(TextDecoration.LineThrough)
|
||||
val newDec = if (isUnderline) {
|
||||
|
|
@ -654,7 +654,7 @@ fun TextAnnotationDock(
|
|||
FormattingIconButton(
|
||||
isSelected = isStrike,
|
||||
iconRes = R.drawable.format_strikethrough,
|
||||
contentDescription = "Strikethrough",
|
||||
contentDescription = stringResource(R.string.content_desc_strikethrough),
|
||||
onClick = {
|
||||
val hasUnd = currentDec.contains(TextDecoration.Underline)
|
||||
val newDec = if (isStrike) {
|
||||
|
|
@ -682,7 +682,7 @@ fun TextAnnotationDock(
|
|||
FormattingIconButton(
|
||||
isSelected = false,
|
||||
iconRes = R.drawable.text_box,
|
||||
contentDescription = "Insert Text Box",
|
||||
contentDescription = stringResource(R.string.content_desc_insert_text_box),
|
||||
onClick = {
|
||||
Timber.tag("PdfTextBoxDebug").d("Dock: Insert Text Box icon clicked")
|
||||
onInsertTextBox()
|
||||
|
|
@ -698,7 +698,7 @@ fun TextAnnotationDock(
|
|||
FormattingIconButton(
|
||||
isSelected = false,
|
||||
iconVector = Icons.Default.Close,
|
||||
contentDescription = "Close",
|
||||
contentDescription = stringResource(R.string.action_close),
|
||||
onClick = {
|
||||
focusManager.clearFocus()
|
||||
onClearTextBoxSelection()
|
||||
|
|
@ -739,7 +739,7 @@ private fun FontItem(
|
|||
if (isSelected) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Check,
|
||||
contentDescription = "Selected",
|
||||
contentDescription = stringResource(R.string.content_desc_selected),
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
|
|
@ -814,7 +814,7 @@ private fun ColorPickerBubble(
|
|||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Close,
|
||||
contentDescription = "Close",
|
||||
contentDescription = stringResource(R.string.action_close),
|
||||
tint = Color.White,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
|
|
@ -1193,12 +1193,12 @@ private fun ColorPickerSpectrumContent(
|
|||
IconButton(onClick = onBack, modifier = Modifier.size(24.dp)) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = "Back",
|
||||
contentDescription = stringResource(R.string.action_back),
|
||||
tint = Color.White
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = "Spectrum",
|
||||
text = stringResource(R.string.label_spectrum),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color.White,
|
||||
|
|
@ -1264,7 +1264,7 @@ private fun ColorPickerSpectrumContent(
|
|||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
"Hex",
|
||||
stringResource(R.string.theme_color_hex),
|
||||
color = Color.Gray,
|
||||
fontSize = 11.sp,
|
||||
maxLines = 1
|
||||
|
|
@ -1282,19 +1282,19 @@ private fun ColorPickerSpectrumContent(
|
|||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
RgbInputColumn(
|
||||
label = "R",
|
||||
label = stringResource(R.string.color_r),
|
||||
value = currentColor.red,
|
||||
onValueChange = { r -> updateFromColor(currentColor.copy(red = r)) },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
RgbInputColumn(
|
||||
label = "G",
|
||||
label = stringResource(R.string.color_g),
|
||||
value = currentColor.green,
|
||||
onValueChange = { g -> updateFromColor(currentColor.copy(green = g)) },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
RgbInputColumn(
|
||||
label = "B",
|
||||
label = stringResource(R.string.color_b),
|
||||
value = currentColor.blue,
|
||||
onValueChange = { b -> updateFromColor(currentColor.copy(blue = b)) },
|
||||
modifier = Modifier.weight(1f)
|
||||
|
|
@ -1313,7 +1313,7 @@ private fun ColorPickerSpectrumContent(
|
|||
modifier = Modifier.fillMaxWidth().height(40.dp),
|
||||
contentPadding = PaddingValues(0.dp)
|
||||
) {
|
||||
Text("Done", fontSize = 14.sp)
|
||||
Text(stringResource(R.string.action_done), fontSize = 14.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1552,4 +1552,4 @@ private fun ColorComparePill(
|
|||
size = androidx.compose.ui.geometry.Size(size.width / 2, size.height)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import android.graphics.RectF
|
|||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import com.aryan.reader.FileType
|
||||
import com.aryan.reader.R
|
||||
import io.legere.pdfiumandroid.api.Bookmark
|
||||
import io.legere.pdfiumandroid.suspend.PdfDocumentKt
|
||||
import io.legere.pdfiumandroid.suspend.PdfPageKt
|
||||
|
|
@ -452,7 +453,7 @@ class OpdsStreamDocumentWrapper(
|
|||
textSize = 40f
|
||||
textAlign = Paint.Align.CENTER
|
||||
}
|
||||
canvas.drawText("Page Unavailable", 400f, 600f, paint)
|
||||
canvas.drawText(context.getString(R.string.msg_page_unavailable), 400f, 600f, paint)
|
||||
val stream = java.io.ByteArrayOutputStream()
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, stream)
|
||||
return stream.toByteArray()
|
||||
|
|
@ -509,4 +510,4 @@ class OpdsStreamDocumentWrapper(
|
|||
override suspend fun getTableOfContents() = emptyList<Bookmark>()
|
||||
|
||||
override fun close() {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue