Offline dictionary (#45)
* Added support for external dictionary apps and a dictionary settings sheet. * Refactored external dictionary integration and improved package visibility - Updated `AndroidManifest.xml` with `<queries>` for common dictionary apps and intent actions (`PROCESS_TEXT`, `SEARCH`, `SEND`) to support Android 11+ package visibility. - Refactored `ExternalDictionaryHelper` to streamline dictionary discovery using `Intent.ACTION_PROCESS_TEXT` and `colordict.intent.action.SEARCH`. - Added a package blocklist in `ExternalDictionaryHelper` to filter out system services (e.g., Samsung Pass, GMS) from dictionary lists. - Simplified `launchDictionary` logic to prioritize `PROCESS_TEXT` intents. - Fixed a bug in `PdfViewerScreen` to only show the dictionary upsell dialog when `useOnlineDictionary` is enabled. * Refactored dictionary settings into a dialog and updated dictionary selection logic. - Renamed `DictionarySettingsSheet` to `DictionarySettingsDialog` and converted it from a bottom sheet to a formal dialog. - Redesigned the dictionary settings UI with a clearer distinction between AI and external app modes. - Added a "Search" option to the external dictionary list that triggers a web search. - Updated `ExternalDictionaryHelper` to include a package blocklist and support for generic web search intents. - Simplified `PdfHelper` by removing Pro user checks for the "Dictionary" action. - Updated icons for dictionary settings across the EPUB and PDF readers.
This commit is contained in:
parent
dff236fa7f
commit
76780bff31
12 changed files with 759 additions and 171 deletions
|
|
@ -0,0 +1,154 @@
|
|||
// ExternalDictionaryHelper.kt
|
||||
package com.aryan.reader.epubreader
|
||||
|
||||
import android.app.SearchManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import timber.log.Timber
|
||||
|
||||
data class ExternalDictionaryApp(
|
||||
val label: String,
|
||||
val packageName: String,
|
||||
val icon: Drawable?
|
||||
)
|
||||
|
||||
object ExternalDictionaryHelper {
|
||||
const val GOOGLE_SEARCH_PKG = "app.internal.google_search"
|
||||
|
||||
private val PACKAGE_BLOCKLIST = setOf(
|
||||
"com.samsung.android.samsungpassautofill",
|
||||
"com.samsung.android.samsungpass",
|
||||
"com.samsung.android.app.pass",
|
||||
"com.google.android.gms",
|
||||
"com.truecaller",
|
||||
"com.adobe.reader",
|
||||
"com.reddit.frontpage"
|
||||
)
|
||||
|
||||
fun getAvailableDictionaries(context: Context): List<ExternalDictionaryApp> {
|
||||
val pm = context.packageManager
|
||||
val apps = mutableListOf<ExternalDictionaryApp>()
|
||||
val addedPackages = mutableSetOf<String>()
|
||||
|
||||
val processTextIntent = Intent(Intent.ACTION_PROCESS_TEXT).setType("text/plain")
|
||||
val textResolvers = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
pm.queryIntentActivities(processTextIntent, PackageManager.ResolveInfoFlags.of(0))
|
||||
} else {
|
||||
@Suppress("DEPRECATION") pm.queryIntentActivities(processTextIntent, 0)
|
||||
}
|
||||
|
||||
textResolvers.forEach { ri ->
|
||||
val pkg = ri.activityInfo.packageName
|
||||
if (!PACKAGE_BLOCKLIST.contains(pkg) && addedPackages.add(pkg)) {
|
||||
apps.add(ExternalDictionaryApp(
|
||||
label = ri.loadLabel(pm).toString(),
|
||||
packageName = pkg,
|
||||
icon = ri.loadIcon(pm)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
val colorDictIntent = Intent("colordict.intent.action.SEARCH")
|
||||
val colorResolvers = pm.queryIntentActivities(colorDictIntent, 0)
|
||||
colorResolvers.forEach { ri ->
|
||||
val pkg = ri.activityInfo.packageName
|
||||
if (!PACKAGE_BLOCKLIST.contains(pkg) && addedPackages.add(pkg)) {
|
||||
apps.add(ExternalDictionaryApp(
|
||||
label = ri.loadLabel(pm).toString(),
|
||||
packageName = pkg,
|
||||
icon = ri.loadIcon(pm)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
val sortedApps = apps.sortedBy { it.label }.toMutableList()
|
||||
|
||||
// Inject Google Search at the top
|
||||
sortedApps.add(
|
||||
0,
|
||||
ExternalDictionaryApp(
|
||||
label = "Search",
|
||||
packageName = GOOGLE_SEARCH_PKG,
|
||||
icon = null
|
||||
)
|
||||
)
|
||||
|
||||
return sortedApps
|
||||
}
|
||||
|
||||
fun launchDictionary(context: Context, packageName: String, query: String) {
|
||||
val pm = context.packageManager
|
||||
try {
|
||||
if (packageName == GOOGLE_SEARCH_PKG) {
|
||||
val searchIntent = Intent(Intent.ACTION_WEB_SEARCH).apply {
|
||||
putExtra(SearchManager.QUERY, query)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(searchIntent)
|
||||
return
|
||||
}
|
||||
|
||||
val processTextIntent = Intent(Intent.ACTION_PROCESS_TEXT).apply {
|
||||
type = "text/plain"
|
||||
putExtra(Intent.EXTRA_PROCESS_TEXT, query)
|
||||
putExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, true)
|
||||
setPackage(packageName)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
|
||||
if (processTextIntent.resolveActivity(pm) != null) {
|
||||
context.startActivity(processTextIntent)
|
||||
return
|
||||
}
|
||||
|
||||
val dictIntent = Intent("colordict.intent.action.SEARCH").apply {
|
||||
putExtra("EXTRA_QUERY", query)
|
||||
setPackage(packageName)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
|
||||
if (dictIntent.resolveActivity(pm) != null) {
|
||||
context.startActivity(dictIntent)
|
||||
return
|
||||
}
|
||||
|
||||
if (packageName == "it.t_arn.aard2") {
|
||||
val aardIntent = Intent("aard2.lookup").apply {
|
||||
putExtra("query", query)
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
}
|
||||
context.startActivity(aardIntent)
|
||||
return
|
||||
}
|
||||
|
||||
launchGenericSend(context, packageName, query)
|
||||
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to launch dictionary app: $packageName")
|
||||
Toast.makeText(context, "Error opening dictionary", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchGenericSend(context: Context, packageName: String, query: String) {
|
||||
val sendIntent = Intent(Intent.ACTION_SEND)
|
||||
sendIntent.type = "text/plain"
|
||||
sendIntent.putExtra(Intent.EXTRA_TEXT, query)
|
||||
sendIntent.setPackage(packageName)
|
||||
sendIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
try {
|
||||
context.startActivity(sendIntent)
|
||||
} catch (e: Exception) {
|
||||
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
|
||||
if (launchIntent != null) {
|
||||
context.startActivity(launchIntent)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue