basic full text expansion #843
This commit is contained in:
parent
21f5c98642
commit
b89d3c198e
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.support.v4.text.BidiFormatter
|
import android.support.v4.text.BidiFormatter
|
||||||
import android.support.v7.widget.RecyclerView
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import android.util.SparseBooleanArray
|
||||||
import com.bumptech.glide.RequestManager
|
import com.bumptech.glide.RequestManager
|
||||||
import org.mariotaku.kpreferences.get
|
import org.mariotaku.kpreferences.get
|
||||||
import org.mariotaku.twidere.R
|
import org.mariotaku.twidere.R
|
||||||
|
@ -63,6 +64,7 @@ class DummyItemAdapter(
|
||||||
var showCardActions: Boolean = false
|
var showCardActions: Boolean = false
|
||||||
|
|
||||||
private var showingActionCardPosition = RecyclerView.NO_POSITION
|
private var showingActionCardPosition = RecyclerView.NO_POSITION
|
||||||
|
private val showingFullTextStates = SparseBooleanArray()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
GeneralComponent.get(context).inject(this)
|
GeneralComponent.get(context).inject(this)
|
||||||
|
@ -111,6 +113,17 @@ class DummyItemAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isFullTextVisible(position: Int): Boolean {
|
||||||
|
return showingFullTextStates.get(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setFullTextVisible(position: Int, visible: Boolean) {
|
||||||
|
showingFullTextStates.put(position, visible)
|
||||||
|
if (position != RecyclerView.NO_POSITION && adapter != null) {
|
||||||
|
adapter.notifyItemChanged(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun getUser(position: Int): ParcelableUser? {
|
override fun getUser(position: Int): ParcelableUser? {
|
||||||
if (adapter is ParcelableUsersAdapter) {
|
if (adapter is ParcelableUsersAdapter) {
|
||||||
return adapter.getUser(position)
|
return adapter.getUser(position)
|
||||||
|
|
|
@ -23,9 +23,11 @@ import android.content.Context
|
||||||
import android.database.CursorIndexOutOfBoundsException
|
import android.database.CursorIndexOutOfBoundsException
|
||||||
import android.support.v4.widget.Space
|
import android.support.v4.widget.Space
|
||||||
import android.support.v7.widget.RecyclerView
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import android.util.SparseBooleanArray
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import com.bumptech.glide.RequestManager
|
import com.bumptech.glide.RequestManager
|
||||||
|
import org.apache.commons.collections.primitives.ArrayLongList
|
||||||
import org.mariotaku.kpreferences.get
|
import org.mariotaku.kpreferences.get
|
||||||
import org.mariotaku.ktextension.*
|
import org.mariotaku.ktextension.*
|
||||||
import org.mariotaku.library.objectcursor.ObjectCursor
|
import org.mariotaku.library.objectcursor.ObjectCursor
|
||||||
|
@ -118,6 +120,7 @@ abstract class ParcelableStatusesAdapter(
|
||||||
private var displayPositions: IntArray? = null
|
private var displayPositions: IntArray? = null
|
||||||
private var displayDataCount: Int = 0
|
private var displayDataCount: Int = 0
|
||||||
private var showingActionCardId = RecyclerView.NO_ID
|
private var showingActionCardId = RecyclerView.NO_ID
|
||||||
|
private val showingFullTextStates = SparseBooleanArray()
|
||||||
private val reuseStatus = ParcelableStatus()
|
private val reuseStatus = ParcelableStatus()
|
||||||
private var infoCache: Array<StatusInfo?>? = null
|
private var infoCache: Array<StatusInfo?>? = null
|
||||||
|
|
||||||
|
@ -284,6 +287,17 @@ abstract class ParcelableStatusesAdapter(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isFullTextVisible(position: Int): Boolean {
|
||||||
|
return showingFullTextStates.get(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setFullTextVisible(position: Int, visible: Boolean) {
|
||||||
|
showingFullTextStates.put(position, visible)
|
||||||
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
|
notifyItemChanged(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
when (viewType) {
|
when (viewType) {
|
||||||
ITEM_VIEW_TYPE_GAP -> {
|
ITEM_VIEW_TYPE_GAP -> {
|
||||||
|
|
|
@ -37,6 +37,10 @@ interface IStatusesAdapter<in Data> : IContentAdapter, IGapSupportedAdapter {
|
||||||
|
|
||||||
fun showCardActions(position: Int)
|
fun showCardActions(position: Int)
|
||||||
|
|
||||||
|
fun isFullTextVisible(position: Int): Boolean
|
||||||
|
|
||||||
|
fun setFullTextVisible(position: Int, visible: Boolean)
|
||||||
|
|
||||||
fun setData(data: Data?): Boolean
|
fun setData(data: Data?): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -49,6 +49,7 @@ import android.text.Spanned
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.text.method.LinkMovementMethod
|
import android.text.method.LinkMovementMethod
|
||||||
import android.text.style.ForegroundColorSpan
|
import android.text.style.ForegroundColorSpan
|
||||||
|
import android.util.SparseBooleanArray
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.view.View.OnClickListener
|
import android.view.View.OnClickListener
|
||||||
import android.widget.Space
|
import android.widget.Space
|
||||||
|
@ -1447,7 +1448,8 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
||||||
private var replyError: CharSequence? = null
|
private var replyError: CharSequence? = null
|
||||||
private var conversationError: CharSequence? = null
|
private var conversationError: CharSequence? = null
|
||||||
private var replyStart: Int = 0
|
private var replyStart: Int = 0
|
||||||
private var showingActionCardPosition: Int = 0
|
private var showingActionCardPosition = RecyclerView.NO_POSITION
|
||||||
|
private val showingFullTextStates = SparseBooleanArray()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setHasStableIds(true)
|
setHasStableIds(true)
|
||||||
|
@ -1532,6 +1534,16 @@ class StatusFragment : BaseFragment(), LoaderCallbacks<SingleResponse<Parcelable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun isFullTextVisible(position: Int): Boolean {
|
||||||
|
return showingFullTextStates.get(position)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setFullTextVisible(position: Int, visible: Boolean) {
|
||||||
|
showingFullTextStates.put(position, visible)
|
||||||
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
|
notifyItemChanged(position)
|
||||||
|
}
|
||||||
|
}
|
||||||
override fun setData(data: List<ParcelableStatus>?): Boolean {
|
override fun setData(data: List<ParcelableStatus>?): Boolean {
|
||||||
val status = this.status ?: return false
|
val status = this.status ?: return false
|
||||||
val changed = this.data != data
|
val changed = this.data != data
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.mariotaku.twidere.text
|
||||||
|
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.text.TextPaint
|
||||||
|
import android.text.style.ReplacementSpan
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Mariotaku on 2017/5/21.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ShowMoreToggleSpan: ReplacementSpan() {
|
||||||
|
|
||||||
|
override fun getSize(paint: Paint?, text: CharSequence?, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun draw(canvas: Canvas?, text: CharSequence?, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint?) {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package org.mariotaku.twidere.text
|
||||||
|
|
||||||
|
import android.text.TextPaint
|
||||||
|
import android.text.style.ClickableSpan
|
||||||
|
import org.mariotaku.ktextension.contains
|
||||||
|
import org.mariotaku.twidere.constant.SharedPreferenceConstants
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Mariotaku on 2017/5/21.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abstract class TwidereClickableSpan(val highlightStyle: Int): ClickableSpan() {
|
||||||
|
|
||||||
|
override fun updateDrawState(ds: TextPaint) {
|
||||||
|
if (SharedPreferenceConstants.VALUE_LINK_HIGHLIGHT_OPTION_CODE_UNDERLINE in highlightStyle) {
|
||||||
|
ds.isUnderlineText = true
|
||||||
|
}
|
||||||
|
if (SharedPreferenceConstants.VALUE_LINK_HIGHLIGHT_OPTION_CODE_HIGHLIGHT in highlightStyle) {
|
||||||
|
ds.color = ds.linkColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import android.support.v7.widget.RecyclerView.ViewHolder
|
||||||
import android.text.SpannableString
|
import android.text.SpannableString
|
||||||
import android.text.SpannableStringBuilder
|
import android.text.SpannableStringBuilder
|
||||||
import android.text.Spanned
|
import android.text.Spanned
|
||||||
|
import android.text.style.ClickableSpan
|
||||||
import android.text.style.ForegroundColorSpan
|
import android.text.style.ForegroundColorSpan
|
||||||
import android.text.style.StyleSpan
|
import android.text.style.StyleSpan
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
@ -37,6 +38,7 @@ import org.mariotaku.twidere.model.UserKey
|
||||||
import org.mariotaku.twidere.task.CreateFavoriteTask
|
import org.mariotaku.twidere.task.CreateFavoriteTask
|
||||||
import org.mariotaku.twidere.task.DestroyFavoriteTask
|
import org.mariotaku.twidere.task.DestroyFavoriteTask
|
||||||
import org.mariotaku.twidere.task.RetweetStatusTask
|
import org.mariotaku.twidere.task.RetweetStatusTask
|
||||||
|
import org.mariotaku.twidere.text.TwidereClickableSpan
|
||||||
import org.mariotaku.twidere.util.*
|
import org.mariotaku.twidere.util.*
|
||||||
import org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText
|
import org.mariotaku.twidere.util.HtmlEscapeHelper.toPlainText
|
||||||
import org.mariotaku.twidere.util.Utils.getUserTypeIconRes
|
import org.mariotaku.twidere.util.Utils.getUserTypeIconRes
|
||||||
|
@ -109,7 +111,7 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
||||||
adapter.profileImageStyle, profileImageView.cornerRadius,
|
adapter.profileImageStyle, profileImageView.cornerRadius,
|
||||||
profileImageView.cornerRadiusRatio).into(profileImageView)
|
profileImageView.cornerRadiusRatio).into(profileImageView)
|
||||||
nameView.name = TWIDERE_PREVIEW_NAME
|
nameView.name = TWIDERE_PREVIEW_NAME
|
||||||
nameView.screenName = "@" + TWIDERE_PREVIEW_SCREEN_NAME
|
nameView.screenName = "@$TWIDERE_PREVIEW_SCREEN_NAME"
|
||||||
nameView.updateText(adapter.bidiFormatter)
|
nameView.updateText(adapter.bidiFormatter)
|
||||||
summaryView.hideIfEmpty()
|
summaryView.hideIfEmpty()
|
||||||
if (adapter.linkHighlightingStyle == VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) {
|
if (adapter.linkHighlightingStyle == VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) {
|
||||||
|
@ -154,8 +156,8 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
||||||
statusContentLowerSpace.visibility = if (showCardActions) View.GONE else View.VISIBLE
|
statusContentLowerSpace.visibility = if (showCardActions) View.GONE else View.VISIBLE
|
||||||
|
|
||||||
val replyCount = status.reply_count
|
val replyCount = status.reply_count
|
||||||
val retweetCount: Long
|
val retweetCount = status.retweet_count
|
||||||
val favoriteCount: Long
|
val favoriteCount = status.favorite_count
|
||||||
|
|
||||||
if (displayPinned && status.is_pinned_status) {
|
if (displayPinned && status.is_pinned_status) {
|
||||||
statusInfoLabel.setText(R.string.pinned_status)
|
statusInfoLabel.setText(R.string.pinned_status)
|
||||||
|
@ -232,13 +234,7 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
||||||
} else {
|
} else {
|
||||||
quotedTextView.spannable = quotedText
|
quotedTextView.spannable = quotedText
|
||||||
}
|
}
|
||||||
|
quotedTextView.hideIfEmpty()
|
||||||
if (quotedTextView.length() == 0) {
|
|
||||||
// No text
|
|
||||||
quotedTextView.visibility = View.GONE
|
|
||||||
} else {
|
|
||||||
quotedTextView.visibility = View.VISIBLE
|
|
||||||
}
|
|
||||||
|
|
||||||
val quoted_user_color = colorNameManager.getUserColor(quoted_user_key)
|
val quoted_user_color = colorNameManager.getUserColor(quoted_user_key)
|
||||||
if (quoted_user_color != 0) {
|
if (quoted_user_color != 0) {
|
||||||
|
@ -361,11 +357,13 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
||||||
|
|
||||||
val text: CharSequence
|
val text: CharSequence
|
||||||
val displayEnd: Int
|
val displayEnd: Int
|
||||||
if (!summaryView.empty) {
|
if (!summaryView.empty && !isFullTextVisible) {
|
||||||
text = SpannableStringBuilder.valueOf(context.getString(R.string.label_status_show_more)).apply {
|
text = SpannableStringBuilder.valueOf(context.getString(R.string.label_status_show_more)).apply {
|
||||||
val colorSecondary = ThemeUtils.getTextColorSecondary(context)
|
setSpan(object : TwidereClickableSpan(adapter.linkHighlightingStyle) {
|
||||||
setSpan(ForegroundColorSpan(colorSecondary), 0, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
override fun onClick(widget: View?) {
|
||||||
setSpan(StyleSpan(Typeface.ITALIC), 0, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
showFullText()
|
||||||
|
}
|
||||||
|
}, 0, length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
}
|
}
|
||||||
displayEnd = -1
|
displayEnd = -1
|
||||||
} else if (adapter.linkHighlightingStyle != VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) {
|
} else if (adapter.linkHighlightingStyle != VALUE_LINK_HIGHLIGHT_OPTION_CODE_NONE) {
|
||||||
|
@ -415,7 +413,6 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
||||||
Utils.isMyRetweet(status.account_key, status.retweeted_by_user_key,
|
Utils.isMyRetweet(status.account_key, status.retweeted_by_user_key,
|
||||||
status.my_retweet_id)
|
status.my_retweet_id)
|
||||||
}
|
}
|
||||||
retweetCount = status.retweet_count
|
|
||||||
|
|
||||||
if (retweetCount > 0) {
|
if (retweetCount > 0) {
|
||||||
retweetCountView.spannable = UnitConvertUtils.calculateProperCount(retweetCount)
|
retweetCountView.spannable = UnitConvertUtils.calculateProperCount(retweetCount)
|
||||||
|
@ -430,7 +427,6 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
||||||
val creatingFavorite = CreateFavoriteTask.isCreatingFavorite(status.account_key, status.id)
|
val creatingFavorite = CreateFavoriteTask.isCreatingFavorite(status.account_key, status.id)
|
||||||
favoriteIcon.isActivated = creatingFavorite || status.is_favorite
|
favoriteIcon.isActivated = creatingFavorite || status.is_favorite
|
||||||
}
|
}
|
||||||
favoriteCount = status.favorite_count
|
|
||||||
|
|
||||||
if (favoriteCount > 0) {
|
if (favoriteCount > 0) {
|
||||||
favoriteCountView.spannable = UnitConvertUtils.calculateProperCount(favoriteCount)
|
favoriteCountView.spannable = UnitConvertUtils.calculateProperCount(favoriteCount)
|
||||||
|
@ -590,6 +586,9 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
||||||
private val isCardActionsShown: Boolean
|
private val isCardActionsShown: Boolean
|
||||||
get() = adapter.isCardActionsShown(layoutPosition)
|
get() = adapter.isCardActionsShown(layoutPosition)
|
||||||
|
|
||||||
|
private val isFullTextVisible: Boolean
|
||||||
|
get() = adapter.isFullTextVisible(layoutPosition)
|
||||||
|
|
||||||
private fun showCardActions() {
|
private fun showCardActions() {
|
||||||
adapter.showCardActions(layoutPosition)
|
adapter.showCardActions(layoutPosition)
|
||||||
}
|
}
|
||||||
|
@ -599,6 +598,15 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
|
||||||
return !adapter.isCardActionsShown(RecyclerView.NO_POSITION)
|
return !adapter.isCardActionsShown(RecyclerView.NO_POSITION)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun showFullText() {
|
||||||
|
adapter.setFullTextVisible(layoutPosition, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideFullText(): Boolean {
|
||||||
|
adapter.setFullTextVisible(layoutPosition, false)
|
||||||
|
return !adapter.isFullTextVisible(RecyclerView.NO_POSITION)
|
||||||
|
}
|
||||||
|
|
||||||
private fun TextView.displayMediaLabel(cardName: String?, media: Array<ParcelableMedia?>?,
|
private fun TextView.displayMediaLabel(cardName: String?, media: Array<ParcelableMedia?>?,
|
||||||
location: ParcelableLocation?, placeFullName: String?,
|
location: ParcelableLocation?, placeFullName: String?,
|
||||||
sensitive: Boolean) {
|
sensitive: Boolean) {
|
||||||
|
|
Loading…
Reference in New Issue