complete link preview
This commit is contained in:
parent
cc38f01c5e
commit
d345ad3d25
|
@ -291,6 +291,8 @@ public interface SharedPreferenceConstants {
|
|||
String KEY_AUTO_HIDE_TABS = "auto_hide_tabs";
|
||||
@ExportablePreference(BOOLEAN)
|
||||
String KEY_HIDE_CARD_NUMBERS = "hide_card_numbers";
|
||||
@ExportablePreference(BOOLEAN)
|
||||
String KEY_SHOW_LINK_PREVIEW = "show_link_preview";
|
||||
|
||||
// Internal preferences
|
||||
|
||||
|
|
|
@ -20,11 +20,11 @@ package org.mariotaku.twidere.model;
|
|||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import androidx.annotation.IntDef;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.LongDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.bluelinelabs.logansquare.annotation.JsonField;
|
||||
import com.bluelinelabs.logansquare.annotation.JsonObject;
|
||||
|
@ -527,6 +527,9 @@ public class ParcelableStatus implements Parcelable, Comparable<ParcelableStatus
|
|||
@JsonField(name = "external_url")
|
||||
public String external_url;
|
||||
|
||||
@JsonField(name = "entities_url")
|
||||
public String[] entities_url;
|
||||
|
||||
@JsonField(name = "quoted_external_url")
|
||||
public String quoted_external_url;
|
||||
|
||||
|
|
|
@ -230,6 +230,7 @@ dependencies {
|
|||
implementation "com.google.dagger:dagger:${libVersions['Dagger']}"
|
||||
kapt "com.google.dagger:dagger-compiler:${libVersions['Dagger']}"
|
||||
implementation 'org.attoparser:attoparser:2.0.4.RELEASE'
|
||||
implementation 'org.jsoup:jsoup:1.13.1'
|
||||
implementation 'com.getkeepsafe.taptargetview:taptargetview:1.9.1'
|
||||
implementation 'net.ypresto.androidtranscoder:android-transcoder:0.2.0'
|
||||
implementation "com.google.android.exoplayer:exoplayer-core:${libVersions['Exoplayer']}"
|
||||
|
|
|
@ -91,6 +91,7 @@ public interface Constants extends TwidereConstants {
|
|||
String TWIDERE_PREVIEW_NAME = "Twidere Project";
|
||||
String TWIDERE_PREVIEW_SCREEN_NAME = "TwidereProject";
|
||||
String TWIDERE_PREVIEW_TEXT_HTML = "Twidere is an open source twitter client for Android, see <a href='https://github.com/mariotaku/twidere'>github.com/mariotak…</a>";
|
||||
String TWIDERE_PREVIEW_LINK_URI = "https://github.com/TwidereProject/Twidere-Android";
|
||||
String TWIDERE_PREVIEW_TEXT_UNESCAPED = "Twidere is an open source twitter client for Android, see github.com/mariotak…";
|
||||
String TWIDERE_PREVIEW_SOURCE = "Twidere for Android";
|
||||
|
||||
|
|
|
@ -65,6 +65,8 @@ class DummyItemAdapter(
|
|||
|
||||
var showCardNumbers: Boolean = false
|
||||
|
||||
var showLinkPreview: Boolean = false
|
||||
|
||||
private var showingActionCardPosition = RecyclerView.NO_POSITION
|
||||
private val showingFullTextStates = SparseBooleanArray()
|
||||
|
||||
|
@ -110,6 +112,10 @@ class DummyItemAdapter(
|
|||
return showCardActions || showingActionCardPosition == position
|
||||
}
|
||||
|
||||
override fun isLinkPreviewShown(position: Int): Boolean {
|
||||
return showLinkPreview
|
||||
}
|
||||
|
||||
override fun showCardActions(position: Int) {
|
||||
if (showingActionCardPosition != RecyclerView.NO_POSITION && adapter != null) {
|
||||
adapter.notifyItemChanged(showingActionCardPosition)
|
||||
|
@ -189,6 +195,7 @@ class DummyItemAdapter(
|
|||
sensitiveContentEnabled = preferences[displaySensitiveContentsKey]
|
||||
showCardActions = !preferences[hideCardActionsKey]
|
||||
showCardNumbers = !preferences[hideCardNumbersKey]
|
||||
showLinkPreview = preferences[showLinkPreviewKey]
|
||||
linkHighlightingStyle = preferences[linkHighlightOptionKey]
|
||||
lightFont = preferences[lightFontKey]
|
||||
useStarsForLikes = preferences[iWantMyStarsBackKey]
|
||||
|
|
|
@ -21,11 +21,11 @@ package org.mariotaku.twidere.adapter
|
|||
|
||||
import android.content.Context
|
||||
import android.database.CursorIndexOutOfBoundsException
|
||||
import androidx.legacy.widget.Space
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.util.SparseBooleanArray
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.legacy.widget.Space
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.bumptech.glide.RequestManager
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.*
|
||||
|
@ -80,6 +80,7 @@ abstract class ParcelableStatusesAdapter(
|
|||
final override val sensitiveContentEnabled: Boolean = preferences.getBoolean(KEY_DISPLAY_SENSITIVE_CONTENTS, false)
|
||||
private val showCardActions: Boolean = !preferences[hideCardActionsKey]
|
||||
private val showCardNumbers: Boolean = !preferences[hideCardNumbersKey]
|
||||
private val showLinkPreview: Boolean = preferences[showLinkPreviewKey]
|
||||
|
||||
private val gapLoadingIds: MutableSet<ObjectId> = HashSet()
|
||||
|
||||
|
@ -267,6 +268,10 @@ abstract class ParcelableStatusesAdapter(
|
|||
return showCardNumbers || showingActionCardId == getItemId(position)
|
||||
}
|
||||
|
||||
override fun isLinkPreviewShown(position: Int): Boolean {
|
||||
return showLinkPreview
|
||||
}
|
||||
|
||||
override fun isCardActionsShown(position: Int): Boolean {
|
||||
if (position == RecyclerView.NO_POSITION) return showCardActions
|
||||
return showCardActions || showingActionCardId == getItemId(position)
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
package org.mariotaku.twidere.adapter
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.text.TextUtils
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.util.SparseBooleanArray
|
||||
|
@ -28,6 +27,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.Space
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.contains
|
||||
import org.mariotaku.microblog.library.twitter.model.TranslationResult
|
||||
|
@ -72,6 +72,7 @@ class StatusDetailsAdapter(
|
|||
private val cardBackgroundColor: Int
|
||||
private val showCardActions = !preferences[hideCardActionsKey]
|
||||
private val showCardNumbers = !preferences[hideCardNumbersKey]
|
||||
private val showLinkPreview = preferences[showLinkPreviewKey]
|
||||
private var recyclerView: RecyclerView? = null
|
||||
private var detailMediaExpanded: Boolean = false
|
||||
|
||||
|
@ -179,6 +180,10 @@ class StatusDetailsAdapter(
|
|||
return showCardNumbers || showingActionCardPosition == position
|
||||
}
|
||||
|
||||
override fun isLinkPreviewShown(position: Int): Boolean {
|
||||
return showLinkPreview
|
||||
}
|
||||
|
||||
override fun isCardActionsShown(position: Int): Boolean {
|
||||
if (position == RecyclerView.NO_POSITION) return showCardActions
|
||||
return showCardActions || showingActionCardPosition == position
|
||||
|
|
|
@ -34,6 +34,8 @@ interface IStatusesAdapter<in Data> : IContentAdapter, IGapSupportedAdapter {
|
|||
val statusClickListener: IStatusViewHolder.StatusClickListener?
|
||||
|
||||
fun isCardNumbersShown(position: Int): Boolean
|
||||
|
||||
fun isLinkPreviewShown(position: Int): Boolean
|
||||
|
||||
fun isCardActionsShown(position: Int): Boolean
|
||||
|
||||
|
|
|
@ -90,6 +90,7 @@ val tabPositionKey = KStringKey(KEY_TAB_POSITION, SharedPreferenceConstants.DEFA
|
|||
val yandexKeyKey = KStringKey(SharedPreferenceConstants.KEY_YANDEX_KEY, TwidereConstants.YANDEX_KEY)
|
||||
val autoHideTabs = KBooleanKey(SharedPreferenceConstants.KEY_AUTO_HIDE_TABS, true)
|
||||
val hideCardNumbersKey = KBooleanKey(KEY_HIDE_CARD_NUMBERS, false)
|
||||
val showLinkPreviewKey = KBooleanKey(KEY_SHOW_LINK_PREVIEW, false)
|
||||
|
||||
|
||||
object cacheSizeLimitKey : KSimpleKey<Int>(KEY_CACHE_SIZE_LIMIT, 300) {
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.mariotaku.twidere.util.HtmlSpanBuilder
|
|||
import org.mariotaku.twidere.util.InternalTwitterContentUtils
|
||||
import org.mariotaku.twidere.util.InternalTwitterContentUtils.getMediaUrl
|
||||
import org.mariotaku.twidere.util.InternalTwitterContentUtils.getStartEndForEntity
|
||||
import kotlin.math.max
|
||||
|
||||
fun Status.toParcelable(details: AccountDetails, profileImageSize: String = "normal",
|
||||
updateFilterInfoAction: (Status, ParcelableStatus) -> Unit = ::updateFilterInfoDefault): ParcelableStatus {
|
||||
|
@ -71,6 +72,13 @@ fun Status.applyTo(accountKey: UserKey, accountType: String, profileImageSize: S
|
|||
result.timestamp = createdAt?.time ?: 0
|
||||
|
||||
extras.external_url = inferredExternalUrl
|
||||
extras.entities_url = entities?.urls?.map { it.expandedUrl }?.let {
|
||||
if (isQuoteStatus) {
|
||||
it.take(max(0, it.count() - 1))
|
||||
} else {
|
||||
it
|
||||
}
|
||||
}?.toTypedArray()
|
||||
extras.support_entities = entities != null
|
||||
extras.statusnet_conversation_id = statusnetConversationId
|
||||
extras.conversation_id = conversationId
|
||||
|
@ -367,7 +375,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() {
|
||||
|
|
|
@ -25,6 +25,7 @@ import android.content.ContentValues
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Rect
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.CallSuper
|
||||
import androidx.fragment.app.Fragment
|
||||
|
@ -465,6 +466,12 @@ abstract class AbsStatusesFragment : AbsContentListRecyclerViewFragment<Parcelab
|
|||
startActivity(intent)
|
||||
}
|
||||
|
||||
override fun onLinkClick(holder: IStatusViewHolder, position: Int) {
|
||||
val status = adapter.getStatus(position)
|
||||
val url = status.extras?.entities_url?.firstOrNull()
|
||||
OnLinkClickHandler.openLink(requireContext(), preferences, Uri.parse(url))
|
||||
}
|
||||
|
||||
override fun scrollToStart(): Boolean {
|
||||
val result = super.scrollToStart()
|
||||
if (result) {
|
||||
|
|
|
@ -2,12 +2,11 @@ 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.jsoup.Jsoup
|
||||
import org.jsoup.nodes.Document
|
||||
import org.mariotaku.ktextension.toString
|
||||
import org.mariotaku.restfu.http.HttpRequest
|
||||
import org.mariotaku.twidere.extension.atto.firstElementOrNull
|
||||
import org.mariotaku.twidere.model.event.StatusListChangedEvent
|
||||
import org.mariotaku.twidere.view.LinkPreviewData
|
||||
|
||||
class LinkPreviewTask(
|
||||
|
@ -19,19 +18,19 @@ class LinkPreviewTask(
|
|||
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)
|
||||
val response = runCatching {
|
||||
restHttpClient.newCall(
|
||||
HttpRequest
|
||||
.Builder()
|
||||
.url(url.replace("http:", "https:"))
|
||||
.method("GET")
|
||||
.build()
|
||||
).execute()
|
||||
}.getOrNull()
|
||||
return response?.body?.stream()?.toString(charset = Charsets.UTF_8, close = true)?.let {
|
||||
Jsoup.parse(it)
|
||||
}?.let { doc ->
|
||||
val title = doc.getMeta("og:title")
|
||||
val title = doc.getMeta("og:title") ?: doc.title()
|
||||
val desc = doc.getMeta("og:description")
|
||||
val img = doc.getMeta("og:image")
|
||||
LinkPreviewData(
|
||||
|
@ -40,16 +39,15 @@ class LinkPreviewTask(
|
|||
}?.also {
|
||||
cacheData.put(url, it)
|
||||
loadingList.remove(url)
|
||||
//TODO: send the result back to bus
|
||||
}
|
||||
}
|
||||
|
||||
override fun afterExecute(callback: Any?, result: LinkPreviewData?) {
|
||||
bus.post(StatusListChangedEvent())
|
||||
}
|
||||
|
||||
private fun Document.getMeta(name: String): String? {
|
||||
return firstElementOrNull {
|
||||
it.elementNameMatches("meta") &&
|
||||
it.hasAttribute("property") &&
|
||||
it.getAttributeValue("property") == name
|
||||
}?.getAttributeValue("content")
|
||||
return this.head().getElementsByAttributeValue("property", name).firstOrNull { it.tagName() == "meta" }?.attributes()?.get("content")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -1,42 +1,49 @@
|
|||
package org.mariotaku.twidere.view
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.net.Uri
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.isVisible
|
||||
import com.bumptech.glide.RequestManager
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
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?
|
||||
val desc: String? = null,
|
||||
val img: String? = null,
|
||||
val imgRes: Int? = null
|
||||
)
|
||||
|
||||
|
||||
class LinkPreviewView : FrameLayout {
|
||||
class LinkPreviewView : MaterialCardView {
|
||||
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)
|
||||
LayoutInflater.from(context).inflate(R.layout.layout_link_preview, this, true)
|
||||
}
|
||||
|
||||
fun displayData(value: String, result: LinkPreviewData) {
|
||||
fun displayData(value: String, result: LinkPreviewData, requestManager: RequestManager) {
|
||||
link_preview_title.isVisible = true
|
||||
link_preview_link.isVisible = true
|
||||
link_preview_img.isVisible = result.img != null
|
||||
link_preview_loader.isVisible = false
|
||||
link_preview_title.text = result.title
|
||||
link_preview_link.text = value
|
||||
link_preview_link.text = Uri.parse(value).host
|
||||
if (result.img != null) {
|
||||
requestManager.load(result.img).into(link_preview_img)
|
||||
} else if (result.imgRes != null) {
|
||||
requestManager.load(result.imgRes).into(link_preview_img)
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
link_preview_img.isVisible = false
|
||||
link_preview_title.isVisible = false
|
||||
link_preview_link.isVisible = false
|
||||
link_preview_loader.isVisible = true
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.mariotaku.twidere.util.ThemeUtils
|
|||
import org.mariotaku.twidere.util.UnitConvertUtils
|
||||
import org.mariotaku.twidere.util.Utils
|
||||
import org.mariotaku.twidere.util.Utils.getUserTypeIconRes
|
||||
import org.mariotaku.twidere.view.LinkPreviewData
|
||||
import org.mariotaku.twidere.view.ShapedImageView
|
||||
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder
|
||||
import java.lang.ref.WeakReference
|
||||
|
@ -143,6 +144,8 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
|||
quotedMediaPreview.visibility = View.GONE
|
||||
quotedMediaLabel.visibility = View.GONE
|
||||
mediaPreview.displayMedia(R.drawable.featured_graphics)
|
||||
linkPreview.isVisible = isLinkPreviewShown
|
||||
linkPreview.displayData(TWIDERE_PREVIEW_LINK_URI, LinkPreviewData(title = TWIDERE_PREVIEW_NAME, imgRes = R.drawable.featured_graphics), adapter.requestManager)
|
||||
}
|
||||
|
||||
override fun display(status: ParcelableStatus, displayInReplyTo: Boolean,
|
||||
|
@ -349,13 +352,13 @@ 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) {
|
||||
val url = status.extras?.entities_url?.firstOrNull()
|
||||
linkPreview.isVisible = url != null && isLinkPreviewShown
|
||||
if (url != null && linkPreview.isVisible) {
|
||||
if (!LinkPreviewTask.isInLoading(url)) {
|
||||
val linkPreviewData = LinkPreviewTask.getCached(url)
|
||||
if (linkPreviewData != null) {
|
||||
linkPreview.displayData(url, linkPreviewData)
|
||||
linkPreview.displayData(url, linkPreviewData, requestManager)
|
||||
} else {
|
||||
LinkPreviewTask(context).let {
|
||||
it.params = url
|
||||
|
@ -510,6 +513,7 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
|||
itemContent.setOnClickListener(eventListener)
|
||||
itemContent.setOnLongClickListener(eventListener)
|
||||
|
||||
linkPreview.setOnClickListener(eventListener)
|
||||
itemMenu.setOnClickListener(eventListener)
|
||||
profileImageView.setOnClickListener(eventListener)
|
||||
replyButton.setOnClickListener(eventListener)
|
||||
|
@ -601,6 +605,8 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
|||
listener.onLiked()
|
||||
}
|
||||
}
|
||||
private val isLinkPreviewShown: Boolean
|
||||
get() = adapter.isLinkPreviewShown(layoutPosition)
|
||||
|
||||
private val isCardNumbersShown: Boolean
|
||||
get() = adapter.isCardNumbersShown(layoutPosition)
|
||||
|
@ -724,6 +730,9 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
|||
listener.onStatusClick(holder, position)
|
||||
}
|
||||
}
|
||||
holder.linkPreview -> {
|
||||
listener.onLinkClick(holder, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,8 @@ interface IStatusViewHolder : CardMediaContainer.OnMediaClickListener {
|
|||
fun onUserProfileClick(holder: IStatusViewHolder, position: Int) {}
|
||||
|
||||
fun onFilterClick(holder: TimelineFilterHeaderViewHolder) {}
|
||||
|
||||
fun onLinkClick(holder: IStatusViewHolder, position: Int) {}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ package org.mariotaku.twidere.view.holder.status
|
|||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.Rect
|
||||
import android.net.Uri
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
|
@ -36,10 +37,12 @@ import androidx.annotation.UiThread
|
|||
import androidx.appcompat.widget.ActionMenuView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.adapter_item_status_count_label.view.*
|
||||
import kotlinx.android.synthetic.main.header_status.view.*
|
||||
import org.mariotaku.abstask.library.TaskStarter
|
||||
import org.mariotaku.kpreferences.get
|
||||
import org.mariotaku.ktextension.applyFontFamily
|
||||
import org.mariotaku.ktextension.hideIfEmpty
|
||||
|
@ -54,6 +57,7 @@ import org.mariotaku.twidere.annotation.ProfileImageSize
|
|||
import org.mariotaku.twidere.constant.displaySensitiveContentsKey
|
||||
import org.mariotaku.twidere.constant.hideCardNumbersKey
|
||||
import org.mariotaku.twidere.constant.newDocumentApiKey
|
||||
import org.mariotaku.twidere.constant.showLinkPreviewKey
|
||||
import org.mariotaku.twidere.extension.loadProfileImage
|
||||
import org.mariotaku.twidere.extension.model.*
|
||||
import org.mariotaku.twidere.fragment.AbsStatusesFragment
|
||||
|
@ -63,6 +67,7 @@ import org.mariotaku.twidere.menu.RetweetItemProvider
|
|||
import org.mariotaku.twidere.model.*
|
||||
import org.mariotaku.twidere.model.util.ParcelableLocationUtils
|
||||
import org.mariotaku.twidere.model.util.ParcelableMediaUtils
|
||||
import org.mariotaku.twidere.task.LinkPreviewTask
|
||||
import org.mariotaku.twidere.util.*
|
||||
import org.mariotaku.twidere.util.twitter.card.TwitterCardViewFactory
|
||||
import org.mariotaku.twidere.view.ProfileImageView
|
||||
|
@ -366,6 +371,28 @@ class DetailStatusViewHolder(
|
|||
|
||||
textView.movementMethod = LinkMovementMethod.getInstance()
|
||||
itemView.quotedText.movementMethod = null
|
||||
|
||||
|
||||
val url = status.extras?.entities_url?.firstOrNull()
|
||||
itemView.linkPreview.isVisible = url != null && fragment.preferences[showLinkPreviewKey]
|
||||
if (url != null && itemView.linkPreview.isVisible) {
|
||||
if (!LinkPreviewTask.isInLoading(url)) {
|
||||
val linkPreviewData = LinkPreviewTask.getCached(url)
|
||||
if (linkPreviewData != null) {
|
||||
itemView.linkPreview.displayData(url, linkPreviewData, adapter.requestManager)
|
||||
} else {
|
||||
LinkPreviewTask(context).let {
|
||||
it.params = url
|
||||
TaskStarter.execute(it)
|
||||
}
|
||||
itemView.linkPreview.reset()
|
||||
}
|
||||
} else {
|
||||
itemView.linkPreview.reset()
|
||||
}
|
||||
} else {
|
||||
itemView.linkPreview.reset()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
|
@ -373,6 +400,10 @@ class DetailStatusViewHolder(
|
|||
val fragment = adapter.fragment
|
||||
val preferences = fragment.preferences
|
||||
when (v) {
|
||||
itemView.linkPreview -> {
|
||||
val url = status.extras?.entities_url?.firstOrNull()
|
||||
OnLinkClickHandler.openLink(fragment.requireContext(), preferences, Uri.parse(url))
|
||||
}
|
||||
itemView.mediaPreviewLoad -> {
|
||||
if (adapter.sensitiveContentEnabled || !status.is_possibly_sensitive) {
|
||||
adapter.isDetailMediaExpanded = true
|
||||
|
@ -472,6 +503,7 @@ class DetailStatusViewHolder(
|
|||
ThemeUtils.wrapMenuIcon(itemView.menuBar, excludeGroups = *intArrayOf(Constants.MENU_GROUP_STATUS_SHARE))
|
||||
itemView.mediaPreviewLoad.setOnClickListener(this)
|
||||
itemView.profileContainer.setOnClickListener(this)
|
||||
itemView.linkPreview.setOnClickListener(this)
|
||||
retweetedByView.setOnClickListener(this)
|
||||
locationView.setOnClickListener(this)
|
||||
itemView.quotedView.setOnClickListener(this)
|
||||
|
|
|
@ -290,11 +290,18 @@
|
|||
|
||||
</FrameLayout>
|
||||
|
||||
<org.mariotaku.twidere.view.LinkPreviewView
|
||||
android:id="@+id/linkPreview"
|
||||
android:layout_margin="@dimen/element_spacing_normal"
|
||||
android:layout_below="@+id/mediaPreviewContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<org.mariotaku.twidere.view.TwitterCardContainer
|
||||
android:id="@+id/twitterCard"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/mediaPreviewContainer"
|
||||
android:layout_below="@+id/linkPreview"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<org.mariotaku.twidere.view.ColorLabelLinearLayout
|
||||
|
|
|
@ -1,25 +1,44 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
<merge
|
||||
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:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<TextView
|
||||
style="@style/MaterialAlertDialog.MaterialComponents.Title.Text"
|
||||
tools:text="Google"
|
||||
android:id="@+id/link_preview_title"
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
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>
|
||||
android:layout_height="wrap_content">
|
||||
<ImageView
|
||||
tools:src="@mipmap/ic_launcher"
|
||||
android:id="@+id/link_preview_img"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<TextView
|
||||
android:paddingRight="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingTop="8dp"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
tools:text="Google"
|
||||
android:id="@+id/link_preview_title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
<TextView
|
||||
android:paddingRight="8dp"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingEnd="8dp"
|
||||
android:paddingStart="8dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:singleLine="true"
|
||||
android:maxLines="1"
|
||||
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
|
||||
android:visibility="gone"
|
||||
android:id="@+id/link_preview_loader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
</LinearLayout>
|
||||
</merge>
|
|
@ -212,6 +212,7 @@
|
|||
|
||||
<org.mariotaku.twidere.view.LinkPreviewView
|
||||
android:id="@+id/linkPreview"
|
||||
android:layout_marginTop="@dimen/element_spacing_normal"
|
||||
android:layout_below="@+id/mediaPreview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
@ -220,7 +221,7 @@
|
|||
android:id="@+id/quotedView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/mediaPreview"
|
||||
android:layout_below="@+id/linkPreview"
|
||||
android:layout_marginTop="@dimen/element_spacing_small"
|
||||
android:background="?selectableItemBackground"
|
||||
android:clickable="true"
|
||||
|
@ -314,6 +315,7 @@
|
|||
<include layout="@layout/layout_card_media_preview"/>
|
||||
|
||||
</org.mariotaku.twidere.view.CardMediaContainer>
|
||||
|
||||
</org.mariotaku.twidere.view.ColorLabelRelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
|
|
@ -446,6 +446,7 @@
|
|||
|
||||
<string name="hide_card_actions">Hide card actions</string>
|
||||
<string name="hide_card_numbers">Hide card numbers</string>
|
||||
<string name="show_link_preview">Show link preview</string>
|
||||
<string name="hide_quotes">Hide quotes</string>
|
||||
<string name="hide_replies">Hide replies</string>
|
||||
<string name="hide_retweets">Hide retweets</string>
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<domain includeSubdomains="true">fanfou.com</domain>
|
||||
</domain-config>
|
||||
<base-config cleartextTrafficPermitted="true">
|
||||
</base-config>
|
||||
</network-security-config>
|
||||
|
|
|
@ -111,8 +111,18 @@
|
|||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="i_want_my_stars_back"
|
||||
android:key="show_link_preview"
|
||||
android:order="35"
|
||||
android:title="@string/show_link_preview">
|
||||
<extra
|
||||
android:name="should_recreate"
|
||||
android:value="true"/>
|
||||
</SwitchPreferenceCompat>
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="i_want_my_stars_back"
|
||||
android:order="36"
|
||||
android:summary="@string/i_want_my_stars_back_summary"
|
||||
android:title="@string/i_want_my_stars_back">
|
||||
<extra
|
||||
|
@ -123,7 +133,7 @@
|
|||
<org.mariotaku.twidere.preference.FavoriteConfirmSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="favorite_confirmation"
|
||||
android:order="36"
|
||||
android:order="37"
|
||||
android:summary="@string/preference_summary_favorite_confirmation"
|
||||
android:title="@string/preference_title_favorite_confirmation"/>
|
||||
|
||||
|
|
Loading…
Reference in New Issue