Text lookup upgrade (#66)

* Added `TooltipIconButton` and integrated tooltips for tool icons in app bars

* Added external translate and search support for EPUB and PDF readers

* docs: refine project overview and update build instructions in readme
This commit is contained in:
Aryan 2026-03-14 13:01:46 +05:30 committed by GitHub
parent 74e2cec415
commit f2c5ae25d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 1172 additions and 506 deletions

View file

@ -291,6 +291,8 @@ private fun saveTtsMode(context: Context, modeName: String) {
private const val PREF_USE_ONLINE_DICT = "use_online_dictionary"
private const val PREF_EXTERNAL_DICT_PKG = "external_dictionary_package"
private const val PREF_EXTERNAL_TRANSLATE_PKG = "external_translate_package"
private const val PREF_EXTERNAL_SEARCH_PKG = "external_search_package"
private fun loadUseOnlineDict(context: Context): Boolean {
@Suppress("KotlinConstantConditions") if (BuildConfig.FLAVOR == "oss") return false
@ -313,6 +315,26 @@ private fun saveExternalDictPackage(context: Context, packageName: String) {
prefs.edit { putString(PREF_EXTERNAL_DICT_PKG, packageName) }
}
private fun loadExternalTranslatePackage(context: Context): String? {
val prefs = context.getSharedPreferences("reader_prefs", Context.MODE_PRIVATE)
return prefs.getString(PREF_EXTERNAL_TRANSLATE_PKG, null)
}
private fun saveExternalTranslatePackage(context: Context, packageName: String) {
val prefs = context.getSharedPreferences("reader_prefs", Context.MODE_PRIVATE)
prefs.edit { putString(PREF_EXTERNAL_TRANSLATE_PKG, packageName) }
}
private fun loadExternalSearchPackage(context: Context): String? {
val prefs = context.getSharedPreferences("reader_prefs", Context.MODE_PRIVATE)
return prefs.getString(PREF_EXTERNAL_SEARCH_PKG, null)
}
private fun saveExternalSearchPackage(context: Context, packageName: String) {
val prefs = context.getSharedPreferences("reader_prefs", Context.MODE_PRIVATE)
prefs.edit { putString(PREF_EXTERNAL_SEARCH_PKG, packageName) }
}
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
@Composable
fun EpubReaderScreen(
@ -573,6 +595,12 @@ fun EpubReaderHost(
var selectedDictPackage by remember {
mutableStateOf(loadExternalDictPackage(context))
}
var selectedTranslatePackage by remember {
mutableStateOf(loadExternalTranslatePackage(context))
}
var selectedSearchPackage by remember {
mutableStateOf(loadExternalSearchPackage(context))
}
var showDictionaryUpsellDialog by remember { mutableStateOf(false) }
var showSummarizationUpsellDialog by remember { mutableStateOf(false) }
@ -605,7 +633,7 @@ fun EpubReaderHost(
showDictionaryUpsellDialog = true
}
} else {
if (selectedDictPackage != null) {
if (!selectedDictPackage.isNullOrEmpty()) {
ExternalDictionaryHelper.launchDictionary(context, selectedDictPackage!!, word)
} else {
Toast.makeText(context, "Please select a dictionary app first.", Toast.LENGTH_SHORT).show()
@ -614,6 +642,24 @@ fun EpubReaderHost(
}
}
val onTranslateLookup = { text: String ->
if (!selectedTranslatePackage.isNullOrEmpty()) {
ExternalDictionaryHelper.launchTranslate(context, selectedTranslatePackage!!, text)
} else {
Toast.makeText(context, "Please select a translate app first.", Toast.LENGTH_SHORT).show()
showDictionarySettingsSheet = true
}
}
val onSearchLookup = { text: String ->
if (!selectedSearchPackage.isNullOrEmpty()) {
ExternalDictionaryHelper.launchSearch(context, selectedSearchPackage!!, text)
} else {
Toast.makeText(context, "Please select a search app first.", Toast.LENGTH_SHORT).show()
showDictionarySettingsSheet = true
}
}
val summaryCacheManager = remember(context) { SummaryCacheManager(context) }
var showRecapPopup by remember { mutableStateOf(false) }
var recapResult by remember { mutableStateOf<SummarizationResult?>(null) }
@ -2256,6 +2302,12 @@ fun EpubReaderHost(
onWordSelectedForAiDefinition = { text ->
onDictionaryLookup(text)
},
onTranslate = { text ->
onTranslateLookup(text)
},
onSearch = { text ->
onSearchLookup(text)
},
onContentReadyForSummarization = { content ->
Timber.d("Content received for summarization")
scope.launch {
@ -2591,6 +2643,12 @@ fun EpubReaderHost(
onWordSelectedForAiDefinition = { text ->
onDictionaryLookup(text)
},
onTranslate = { text ->
onTranslateLookup(text)
},
onSearch = { text ->
onSearchLookup(text)
},
userHighlights = userHighlights.filter { it.chapterIndex == (currentChapterInPaginatedMode ?: -1) },
onHighlightCreated = { cfi, text, colorId ->
Timber.d("EpubReaderScreen: onHighlightCreated. CFI: $cfi")
@ -3532,7 +3590,7 @@ fun EpubReaderHost(
onNavigateToPro = onNavigateToPro,
isTtsSessionActive = isTtsSessionActive,
onOpenExternalDictionary = { text ->
if (selectedDictPackage != null) {
if (!selectedDictPackage.isNullOrEmpty()) {
ExternalDictionaryHelper.launchDictionary(context, selectedDictPackage!!, text)
} else {
Toast.makeText(context, "Select an offline dictionary first.", Toast.LENGTH_SHORT).show()
@ -3707,10 +3765,20 @@ fun EpubReaderHost(
useOnlineDictionary = newState
saveUseOnlineDict(context, newState)
},
selectedPackageName = selectedDictPackage,
onSelectPackage = { pkg ->
selectedDictionaryPackageName = selectedDictPackage,
onSelectDictionaryPackage = { pkg ->
selectedDictPackage = pkg
saveExternalDictPackage(context, pkg)
},
selectedTranslatePackageName = selectedTranslatePackage,
onSelectTranslatePackage = { pkg ->
selectedTranslatePackage = pkg
saveExternalTranslatePackage(context, pkg)
},
selectedSearchPackageName = selectedSearchPackage,
onSelectSearchPackage = { pkg ->
selectedSearchPackage = pkg
saveExternalSearchPackage(context, pkg)
}
)
}