mirror of
https://github.com/TwidereProject/Twidere-Android
synced 2025-02-07 15:28:51 +01:00
Initial work for link preview
This commit is contained in:
parent
d88e7f331a
commit
cc38f01c5e
@ -367,7 +367,7 @@ private inline val Status.inferredExternalUrl
|
||||
noticeUriRegex.matchEntire(uri)?.let { result: MatchResult ->
|
||||
"https://${result.groups[1]?.value}/notice/${result.groups[3]?.value}"
|
||||
}
|
||||
}
|
||||
} ?: entities?.urls?.firstOrNull()?.expandedUrl
|
||||
|
||||
private val Status.parcelableLocation: ParcelableLocation?
|
||||
get() {
|
||||
|
@ -0,0 +1,67 @@
|
||||
package org.mariotaku.twidere.task
|
||||
|
||||
import android.content.Context
|
||||
import androidx.collection.LruCache
|
||||
import org.attoparser.config.ParseConfiguration
|
||||
import org.attoparser.dom.DOMMarkupParser
|
||||
import org.attoparser.dom.Document
|
||||
import org.mariotaku.ktextension.toString
|
||||
import org.mariotaku.restfu.http.HttpRequest
|
||||
import org.mariotaku.twidere.extension.atto.firstElementOrNull
|
||||
import org.mariotaku.twidere.view.LinkPreviewData
|
||||
|
||||
class LinkPreviewTask(
|
||||
context: Context
|
||||
) : BaseAbstractTask<String, LinkPreviewData, Any>(context) {
|
||||
|
||||
override fun doLongOperation(url: String?): LinkPreviewData? {
|
||||
if (url == null) {
|
||||
return null
|
||||
}
|
||||
loadingList.add(url)
|
||||
val response = restHttpClient.newCall(
|
||||
HttpRequest
|
||||
.Builder()
|
||||
.url(url.replace("http:", "https:"))
|
||||
.method("GET")
|
||||
.build()
|
||||
).execute()
|
||||
//TODO: exception handling
|
||||
return response.body.stream().toString(charset = Charsets.UTF_8, close = true).let {
|
||||
val parser = DOMMarkupParser(ParseConfiguration.htmlConfiguration())
|
||||
parser.parse(it)
|
||||
}?.let { doc ->
|
||||
val title = doc.getMeta("og:title")
|
||||
val desc = doc.getMeta("og:description")
|
||||
val img = doc.getMeta("og:image")
|
||||
LinkPreviewData(
|
||||
title, desc, img
|
||||
)
|
||||
}?.also {
|
||||
cacheData.put(url, it)
|
||||
loadingList.remove(url)
|
||||
//TODO: send the result back to bus
|
||||
}
|
||||
}
|
||||
|
||||
private fun Document.getMeta(name: String): String? {
|
||||
return firstElementOrNull {
|
||||
it.elementNameMatches("meta") &&
|
||||
it.hasAttribute("property") &&
|
||||
it.getAttributeValue("property") == name
|
||||
}?.getAttributeValue("content")
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val loadingList = arrayListOf<String>()
|
||||
private val cacheData = LruCache<String, LinkPreviewData>(100)
|
||||
|
||||
fun isInLoading(url: String): Boolean {
|
||||
return loadingList.contains(url)
|
||||
}
|
||||
|
||||
fun getCached(url: String): LinkPreviewData? {
|
||||
return cacheData[url]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package org.mariotaku.twidere.view
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.isVisible
|
||||
import kotlinx.android.synthetic.main.layout_link_preview.view.*
|
||||
import org.mariotaku.twidere.R
|
||||
|
||||
data class LinkPreviewData(
|
||||
val title: String?,
|
||||
val desc: String?,
|
||||
val img: String?
|
||||
)
|
||||
|
||||
|
||||
class LinkPreviewView : FrameLayout {
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)
|
||||
|
||||
init {
|
||||
LayoutInflater.from(context).inflate(R.layout.layout_link_preview, this)
|
||||
}
|
||||
|
||||
fun displayData(value: String, result: LinkPreviewData) {
|
||||
link_preview_title.isVisible = true
|
||||
link_preview_link.isVisible = true
|
||||
link_preview_loader.isVisible = false
|
||||
link_preview_title.text = result.title
|
||||
link_preview_link.text = value
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
link_preview_title.isVisible = false
|
||||
link_preview_link.isVisible = false
|
||||
link_preview_loader.isVisible = true
|
||||
link_preview_title.text = ""
|
||||
link_preview_link.text = ""
|
||||
}
|
||||
}
|
@ -11,11 +11,13 @@ import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.bumptech.glide.RequestManager
|
||||
import kotlinx.android.synthetic.main.list_item_status.view.*
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.ktextension.*
|
||||
import org.mariotaku.microblog.library.mastodon.annotation.StatusVisibility
|
||||
import org.mariotaku.twidere.Constants.*
|
||||
@ -36,6 +38,7 @@ import org.mariotaku.twidere.model.ParcelableStatus
|
||||
import org.mariotaku.twidere.model.UserKey
|
||||
import org.mariotaku.twidere.task.CreateFavoriteTask
|
||||
import org.mariotaku.twidere.task.DestroyFavoriteTask
|
||||
import org.mariotaku.twidere.task.LinkPreviewTask
|
||||
import org.mariotaku.twidere.task.RetweetStatusTask
|
||||
import org.mariotaku.twidere.text.TwidereClickableSpan
|
||||
import org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText
|
||||
@ -61,6 +64,7 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
||||
|
||||
private val itemContent by lazy { itemView.itemContent }
|
||||
private val mediaPreview by lazy { itemView.mediaPreview }
|
||||
private val linkPreview by lazy { itemView.linkPreview }
|
||||
private val statusContentUpperSpace by lazy { itemView.statusContentUpperSpace }
|
||||
private val summaryView by lazy { itemView.summary }
|
||||
private val textView by lazy { itemView.text }
|
||||
@ -146,6 +150,7 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
||||
|
||||
val context = itemView.context
|
||||
val requestManager = adapter.requestManager
|
||||
|
||||
val twitter = adapter.twitterWrapper
|
||||
val linkify = adapter.twidereLinkify
|
||||
val formatter = adapter.bidiFormatter
|
||||
@ -344,6 +349,27 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
||||
mediaPreview.visibility = View.GONE
|
||||
}
|
||||
|
||||
val url = status.extras?.external_url
|
||||
linkPreview.isVisible = url != null
|
||||
if (url != null) {
|
||||
if (!LinkPreviewTask.isInLoading(url)) {
|
||||
val linkPreviewData = LinkPreviewTask.getCached(url)
|
||||
if (linkPreviewData != null) {
|
||||
linkPreview.displayData(url, linkPreviewData)
|
||||
} else {
|
||||
LinkPreviewTask(context).let {
|
||||
it.params = url
|
||||
TaskStarter.execute(it)
|
||||
}
|
||||
linkPreview.reset()
|
||||
}
|
||||
} else {
|
||||
linkPreview.reset()
|
||||
}
|
||||
} else {
|
||||
linkPreview.reset()
|
||||
}
|
||||
|
||||
summaryView.spannable = status.extras?.summary_text
|
||||
summaryView.hideIfEmpty()
|
||||
|
||||
|
25
twidere/src/main/res/layout/layout_link_preview.xml
Normal file
25
twidere/src/main/res/layout/layout_link_preview.xml
Normal file
@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<TextView
|
||||
style="@style/MaterialAlertDialog.MaterialComponents.Title.Text"
|
||||
tools:text="Google"
|
||||
android:id="@+id/link_preview_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<TextView
|
||||
android:id="@+id/link_preview_link"
|
||||
tools:text="https://www.google.com"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<org.mariotaku.chameleon.view.ChameleonProgressBar
|
||||
tools:visibility="gone"
|
||||
android:id="@+id/link_preview_loader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
@ -209,6 +209,13 @@
|
||||
|
||||
</org.mariotaku.twidere.view.CardMediaContainer>
|
||||
|
||||
|
||||
<org.mariotaku.twidere.view.LinkPreviewView
|
||||
android:id="@+id/linkPreview"
|
||||
android:layout_below="@+id/mediaPreview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<org.mariotaku.twidere.view.ColorLabelRelativeLayout
|
||||
android:id="@+id/quotedView"
|
||||
android:layout_width="match_parent"
|
||||
|
Loading…
x
Reference in New Issue
Block a user