Add hardcoded websearch url template strings for popular web searches that

do not implement the OpenSearch standard
This commit is contained in:
MM20 2022-11-19 14:55:45 +01:00
parent 75be571e53
commit bf89134326
No known key found for this signature in database
GPG Key ID: 0B61A8F2DEAFA389
2 changed files with 116 additions and 80 deletions

View File

@ -0,0 +1,21 @@
package de.mm20.launcher2.searchactions
import de.mm20.launcher2.searchactions.builders.WebsearchActionBuilder
fun knownWebsearchByHostname(hostname: String): WebsearchActionBuilder? {
// List of popular web search engines that do not implement the OpenSearch standard
return when(hostname) {
"google.com" -> WebsearchActionBuilder(label = "Google", urlTemplate = "https://google.com/search?q=\${1}")
"bing.com" -> WebsearchActionBuilder(label = "Google", urlTemplate = "https://bing.com/search?q=\${1}")
"amazon.com" -> WebsearchActionBuilder(label = "Amazon", urlTemplate = "https://www.amazon.com/s?k=\${1}")
"amazon.de" -> WebsearchActionBuilder(label = "Amazon DE", urlTemplate = "https://www.amazon.de/s?k=\${1}")
"amazon.co.uk" -> WebsearchActionBuilder(label = "Amazon UK", urlTemplate = "https://www.amazon.co.uk/s?k=\${1}")
"amazon.fr" -> WebsearchActionBuilder(label = "Amazon FR", urlTemplate = "https://www.amazon.fr/s?k=\${1}")
"amazon.co.jp" -> WebsearchActionBuilder(label = "Amazon JP", urlTemplate = "https://www.amazon.co.jp/s?k=\${1}")
"amazon.ca" -> WebsearchActionBuilder(label = "Amazon CA", urlTemplate = "https://www.amazon.ca/s?k=\${1}")
"amazon.cn" -> WebsearchActionBuilder(label = "Amazon CN", urlTemplate = "https://www.amazon.cn/s?k=\${1}")
"duckduckgo.com" -> WebsearchActionBuilder(label = "DuckDuckGo", urlTemplate = "https://duckduckgo.com/?q=\${1}")
"yahoo.com" -> WebsearchActionBuilder(label = "DuckDuckGo", urlTemplate = "https://search.yahoo.com/search?p=\${1}")
else -> null
}
}

View File

@ -119,88 +119,18 @@ internal class SearchActionServiceImpl(
.getOrNull(0)
?.absUrl("href")
?.takeIf { it.isNotEmpty() }
?: return@withContext run {
Log.d("MM20", "Specified URL does not implement the OpenSearch protocol")
null
}
val httpClient = OkHttpClient()
val request = Request.Builder()
.url(openSearchHref)
.build()
val response = httpClient.newCall(request).execute()
val inputStream = response.body?.byteStream() ?: return@withContext null
var label: String? = null
var urlTemplate: String? = null
var icon: String? = null
var iconSize: Int = 0
var iconUrl: String? = null
inputStream.use {
val parser = Xml.newPullParser()
parser.setInput(inputStream.reader())
while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.eventType == XmlPullParser.START_TAG) {
when (parser.name) {
"ShortName" -> {
parser.next()
if (parser.eventType == XmlPullParser.TEXT) {
label = parser.text
}
}
"LongName" -> {
parser.next()
if (parser.eventType == XmlPullParser.TEXT) {
if (label != null) label = parser.text
}
}
"Image" -> {
val size =
parser.getAttributeValue(null, "width")?.toIntOrNull() ?: 0
if (size > iconSize || iconUrl == null) {
parser.next()
if (parser.eventType == XmlPullParser.TEXT) {
iconUrl = parser.text
iconSize = size
}
}
}
"Url" -> {
if (parser.getAttributeValue(null, "type") == "text/html") {
val rel = parser.getAttributeValue(null, "rel")
if (rel == null || rel == "results") {
val template =
parser.getAttributeValue(null, "template")
?.takeIf { it.isNotEmpty() } ?: continue
urlTemplate = template
.replace("{searchTerms}", "\${1}")
.replace("{startPage?}", "1")
}
}
}
else -> continue
}
}
}
val localIconUrl = iconUrl?.let {
val uri = Uri.parse(it)
createIcon(uri, iconSize)
}
return@withContext WebsearchActionBuilder(
label = label ?: "",
icon = if (localIconUrl == null) SearchActionIcon.Search else SearchActionIcon.Custom,
customIcon = localIconUrl,
iconColor = if (localIconUrl == null) 0 else 1,
urlTemplate = urlTemplate ?: ""
)
var action = openSearchHref?.let {
importOpenSearch(it)
}
if (action != null) {
return@withContext action
}
val host = URL(u).host ?: return@withContext null
return@withContext knownWebsearchByHostname(host)
} catch (e: IOException) {
CrashReporter.logException(e)
} catch (e: XmlPullParserException) {
@ -209,6 +139,91 @@ internal class SearchActionServiceImpl(
return@withContext null
}
private suspend fun importOpenSearch(openSearchHref: String): WebsearchActionBuilder? {
try {
val httpClient = OkHttpClient()
val request = Request.Builder()
.url(openSearchHref)
.build()
val response = httpClient.newCall(request).execute()
val inputStream = response.body?.byteStream() ?: return null
var label: String? = null
var urlTemplate: String? = null
var icon: String? = null
var iconSize: Int = 0
var iconUrl: String? = null
inputStream.use {
val parser = Xml.newPullParser()
parser.setInput(inputStream.reader())
while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.eventType == XmlPullParser.START_TAG) {
when (parser.name) {
"ShortName" -> {
parser.next()
if (parser.eventType == XmlPullParser.TEXT) {
label = parser.text
}
}
"LongName" -> {
parser.next()
if (parser.eventType == XmlPullParser.TEXT) {
if (label != null) label = parser.text
}
}
"Image" -> {
val size =
parser.getAttributeValue(null, "width")?.toIntOrNull() ?: 0
if (size > iconSize || iconUrl == null) {
parser.next()
if (parser.eventType == XmlPullParser.TEXT) {
iconUrl = parser.text
iconSize = size
}
}
}
"Url" -> {
if (parser.getAttributeValue(null, "type") == "text/html") {
val rel = parser.getAttributeValue(null, "rel")
if (rel == null || rel == "results") {
val template =
parser.getAttributeValue(null, "template")
?.takeIf { it.isNotEmpty() } ?: continue
urlTemplate = template
.replace("{searchTerms}", "\${1}")
.replace("{startPage?}", "1")
}
}
}
else -> continue
}
}
}
val localIconUrl = iconUrl?.let {
val uri = Uri.parse(it)
createIcon(uri, iconSize)
}
return WebsearchActionBuilder(
label = label ?: "",
icon = if (localIconUrl == null) SearchActionIcon.Search else SearchActionIcon.Custom,
customIcon = localIconUrl,
iconColor = if (localIconUrl == null) 0 else 1,
urlTemplate = urlTemplate ?: ""
)
}
} catch (e: IOException) {
} catch (e: XmlPullParserException) {}
return null
}
override suspend fun createIcon(uri: Uri, size: Int): String? = withContext(
Dispatchers.IO
) {