mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
android: Link preview with image URLs and more options for finding image on a site (#1207)
This commit is contained in:
committed by
GitHub
parent
799d9443bd
commit
4a259e44b1
@@ -294,6 +294,9 @@ fun ComposeView(
|
||||
if (lp != null && pendingLinkUrl.value == url) {
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(lp))
|
||||
pendingLinkUrl.value = null
|
||||
} else if (pendingLinkUrl.value == url) {
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.NoPreview)
|
||||
pendingLinkUrl.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,29 +26,44 @@ import chat.simplex.app.views.chat.item.SentColorLight
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jsoup.Jsoup
|
||||
import java.net.URL
|
||||
|
||||
private const val OG_SELECT_QUERY = "meta[property^=og:]"
|
||||
private const val ICON_SELECT_QUERY = "link[rel^=icon],link[rel^=apple-touch-icon],link[rel^=shortcut icon]"
|
||||
private val IMAGE_SUFFIXES = listOf(".jpg", ".png", ".ico", ".webp", ".gif")
|
||||
|
||||
suspend fun getLinkPreview(url: String): LinkPreview? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val response = Jsoup.connect(url)
|
||||
.ignoreContentType(true)
|
||||
.timeout(10000)
|
||||
.followRedirects(true)
|
||||
.execute()
|
||||
val doc = response.parse()
|
||||
val ogTags = doc.select(OG_SELECT_QUERY)
|
||||
val imageUri = ogTags.firstOrNull { it.attr("property") == "og:image" }?.attr("content")
|
||||
val title: String?
|
||||
val u = kotlin.runCatching { URL(url) }.getOrNull() ?: return@withContext null
|
||||
var imageUri = when {
|
||||
IMAGE_SUFFIXES.any { u.path.lowercase().endsWith(it) } -> {
|
||||
title = u.path.substringAfterLast("/")
|
||||
url
|
||||
}
|
||||
else -> {
|
||||
val response = Jsoup.connect(url)
|
||||
.ignoreContentType(true)
|
||||
.timeout(10000)
|
||||
.followRedirects(true)
|
||||
.execute()
|
||||
val doc = response.parse()
|
||||
val ogTags = doc.select(OG_SELECT_QUERY)
|
||||
title = ogTags.firstOrNull { it.attr("property") == "og:title" }?.attr("content") ?: doc.title()
|
||||
ogTags.firstOrNull { it.attr("property") == "og:image" }?.attr("content")
|
||||
?: doc.select(ICON_SELECT_QUERY).firstOrNull { it.attr("rel").contains("icon") }?.attr("href")
|
||||
}
|
||||
}
|
||||
if (imageUri != null) {
|
||||
imageUri = normalizeImageUri(u, imageUri)
|
||||
try {
|
||||
val stream = java.net.URL(imageUri).openStream()
|
||||
val stream = URL(imageUri).openStream()
|
||||
val image = resizeImageToStrSize(BitmapFactory.decodeStream(stream), maxDataSize = 14000)
|
||||
// TODO add once supported in iOS
|
||||
// val description = ogTags.firstOrNull {
|
||||
// it.attr("property") == "og:description"
|
||||
// }?.attr("content") ?: ""
|
||||
val title = ogTags.firstOrNull { it.attr("property") == "og:title" }?.attr("content")
|
||||
if (title != null) {
|
||||
return@withContext LinkPreview(url, title, description = "", image)
|
||||
}
|
||||
@@ -63,8 +78,6 @@ suspend fun getLinkPreview(url: String): LinkPreview? {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Composable
|
||||
fun ComposeLinkView(linkPreview: LinkPreview?, cancelPreview: () -> Unit) {
|
||||
Row(
|
||||
@@ -127,6 +140,37 @@ fun ChatItemLinkView(linkPreview: LinkPreview) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun normalizeImageUri(u: URL, imageUri: String) = when {
|
||||
!imageUri.lowercase().startsWith("http") -> {
|
||||
"${u.protocol}://${u.host}" +
|
||||
if (imageUri.startsWith("/"))
|
||||
imageUri
|
||||
else
|
||||
// When an icon is used as an image with relative href path like: site=site.com, <link rel="icon" href="icon.png">
|
||||
if (u.path.endsWith("/")) u.path + imageUri
|
||||
else u.path.substringBeforeLast("/") + "/$imageUri"
|
||||
}
|
||||
else -> imageUri
|
||||
}
|
||||
|
||||
/*fun normalizeImageUriTest() {
|
||||
val expect = mapOf<Pair<URL, String>, String>(
|
||||
URL("https://example.com") to "icon.png" to "https://example.com/icon.png",
|
||||
URL("https://example.com/") to "icon.png" to "https://example.com/icon.png",
|
||||
URL("https://example.com/") to "/icon.png" to "https://example.com/icon.png",
|
||||
URL("https://example.com") to "assets/images/favicon.png" to "https://example.com/assets/images/favicon.png",
|
||||
URL("https://example.com/") to "assets/images/favicon.png" to "https://example.com/assets/images/favicon.png",
|
||||
URL("https://example.com/dir") to "/favicon.png" to "https://example.com/favicon.png",
|
||||
URL("https://example.com/dir/") to "favicon.png" to "https://example.com/dir/favicon.png",
|
||||
URL("https://example.com/dir/") to "/favicon.png" to "https://example.com/favicon.png",
|
||||
URL("https://example.com/dir/page") to "favicon.png" to "https://example.com/dir/favicon.png",
|
||||
URL("https://example.com/abcde.gif") to "https://example.com/abcde.gif" to "https://example.com/abcde.gif",
|
||||
URL("https://example.com/abcde.gif?a=b") to "https://example.com/abcde.gif?a=b" to "https://example.com/abcde.gif?a=b",
|
||||
)
|
||||
expect.forEach {
|
||||
Log.d(TAG, "Image URI ${normalizeImageUri(it.key.first, it.key.second)} == ${normalizeImageUri(it.key.first, it.key.second) == it.value}")
|
||||
}
|
||||
}*/
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(
|
||||
|
||||
Reference in New Issue
Block a user