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