Twidere-App-Android-Twitter.../twidere/src/main/kotlin/org/mariotaku/twidere/view/holder/status/DetailStatusViewHolder.kt

833 lines
35 KiB
Kotlin
Raw Normal View History

2017-08-28 11:56:09 +02:00
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.mariotaku.twidere.view.holder.status
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Rect
2020-06-02 11:15:34 +02:00
import android.net.Uri
2017-08-28 11:56:09 +02:00
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.TextUtils
import android.text.method.LinkMovementMethod
import android.text.style.ForegroundColorSpan
import android.view.LayoutInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
2020-05-13 10:36:15 +02:00
import androidx.annotation.UiThread
import androidx.appcompat.widget.ActionMenuView
import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
2020-06-02 11:15:34 +02:00
import androidx.core.view.isVisible
2020-05-13 10:36:15 +02:00
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
2017-08-28 11:56:09 +02:00
import kotlinx.android.synthetic.main.adapter_item_status_count_label.view.*
import kotlinx.android.synthetic.main.header_status.view.*
2020-06-02 11:15:34 +02:00
import org.mariotaku.abstask.library.TaskStarter
2017-08-28 11:56:09 +02:00
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.applyFontFamily
import org.mariotaku.ktextension.hideIfEmpty
import org.mariotaku.ktextension.spannable
2017-09-15 11:40:35 +02:00
import org.mariotaku.ktextension.supportActionProvider
2017-08-28 11:56:09 +02:00
import org.mariotaku.microblog.library.twitter.model.TranslationResult
import org.mariotaku.twidere.Constants
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.BaseRecyclerViewAdapter
import org.mariotaku.twidere.adapter.StatusDetailsAdapter
import org.mariotaku.twidere.annotation.ProfileImageSize
import org.mariotaku.twidere.constant.displaySensitiveContentsKey
2020-05-14 09:47:36 +02:00
import org.mariotaku.twidere.constant.hideCardNumbersKey
2017-08-28 11:56:09 +02:00
import org.mariotaku.twidere.constant.newDocumentApiKey
2020-06-02 11:15:34 +02:00
import org.mariotaku.twidere.constant.showLinkPreviewKey
2017-08-28 11:56:09 +02:00
import org.mariotaku.twidere.extension.loadProfileImage
import org.mariotaku.twidere.extension.model.*
2017-09-15 11:40:35 +02:00
import org.mariotaku.twidere.fragment.AbsStatusesFragment
2017-08-28 11:56:09 +02:00
import org.mariotaku.twidere.fragment.status.StatusFragment
import org.mariotaku.twidere.menu.FavoriteItemProvider
2017-09-15 11:40:35 +02:00
import org.mariotaku.twidere.menu.RetweetItemProvider
2017-08-28 11:56:09 +02:00
import org.mariotaku.twidere.model.*
import org.mariotaku.twidere.model.util.ParcelableLocationUtils
import org.mariotaku.twidere.model.util.ParcelableMediaUtils
2020-06-02 11:15:34 +02:00
import org.mariotaku.twidere.task.LinkPreviewTask
2017-08-28 11:56:09 +02:00
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.util.twitter.card.TwitterCardViewFactory
import org.mariotaku.twidere.view.ProfileImageView
import java.util.*
class DetailStatusViewHolder(
private val adapter: StatusDetailsAdapter,
itemView: View
) : RecyclerView.ViewHolder(itemView), View.OnClickListener, ActionMenuView.OnMenuItemClickListener {
private val linkClickHandler: StatusLinkClickHandler
private val linkify: TwidereLinkify
private val profileTypeView = itemView.profileType
private val nameView = itemView.name
private val summaryView = itemView.summary
private val textView = itemView.text
private val locationView = itemView.locationView
private val retweetedByView = itemView.retweetedBy
private val translateResultView = itemView.translateResult
private val translateChangeLanguageView = itemView.translateChangeLanguage
private val translateContainer = itemView.translateContainer
private val translateLabelView = itemView.translateLabel
init {
this.linkClickHandler = DetailStatusLinkClickHandler(adapter.context,
adapter.multiSelectManager, adapter, adapter.preferences)
this.linkify = TwidereLinkify(linkClickHandler)
initViews()
}
@UiThread
fun displayStatus(account: AccountDetails?, status: ParcelableStatus?,
statusActivity: StatusFragment.StatusActivity?, translation: TranslationResult?) {
if (account == null || status == null) return
val fragment = adapter.fragment
val context = adapter.context
val formatter = adapter.bidiFormatter
val twitter = adapter.twitterWrapper
val nameFirst = adapter.nameFirst
val colorNameManager = adapter.userColorNameManager
linkClickHandler.status = status
if (status.retweet_id != null) {
val retweetedBy = colorNameManager.getDisplayName(status.retweeted_by_user_key!!,
status.retweeted_by_user_name!!, status.retweeted_by_user_acct!!, nameFirst)
retweetedByView.spannable = context.getString(R.string.name_retweeted, retweetedBy)
retweetedByView.visibility = View.VISIBLE
} else {
retweetedByView.spannable = null
retweetedByView.visibility = View.GONE
}
itemView.profileContainer.drawEnd(status.account_color)
val layoutPosition = layoutPosition
val skipLinksInText = status.extras?.support_entities == true
if (status.is_quote) {
itemView.quotedView.visibility = View.VISIBLE
val quoteContentAvailable = status.quoted_text_plain != null && status.quoted_text_unescaped != null
if (quoteContentAvailable) {
itemView.quotedName.visibility = View.VISIBLE
itemView.quotedText.visibility = View.VISIBLE
itemView.quotedName.name = colorNameManager.getUserNickname(status.quoted_user_key!!,
status.quoted_user_name)
itemView.quotedName.screenName = "@${status.quoted_user_acct}"
itemView.quotedName.updateText(formatter)
val quotedDisplayEnd = status.extras?.quoted_display_text_range?.getOrNull(1) ?: -1
val quotedText = SpannableStringBuilder.valueOf(status.quoted_text_unescaped)
status.quoted_spans?.applyTo(quotedText)
linkify.applyAllLinks(quotedText, status.account_key, layoutPosition.toLong(),
status.is_possibly_sensitive, skipLinksInText)
if (quotedDisplayEnd != -1 && quotedDisplayEnd <= quotedText.length) {
itemView.quotedText.spannable = quotedText.subSequence(0, quotedDisplayEnd)
} else {
itemView.quotedText.spannable = quotedText
}
itemView.quotedText.hideIfEmpty()
val quotedUserColor = colorNameManager.getUserColor(status.quoted_user_key!!)
if (quotedUserColor != 0) {
itemView.quotedView.drawStart(quotedUserColor)
} else {
itemView.quotedView.drawStart(ThemeUtils.getColorFromAttribute(context,
R.attr.quoteIndicatorBackgroundColor))
}
val quotedMedia = status.quoted_media
2020-06-09 02:21:48 +02:00
when {
quotedMedia?.isEmpty() != false -> {
itemView.quotedMediaLabel.visibility = View.GONE
itemView.quotedMediaPreview.visibility = View.GONE
}
adapter.isDetailMediaExpanded -> {
itemView.quotedMediaLabel.visibility = View.GONE
itemView.quotedMediaPreview.visibility = View.VISIBLE
itemView.quotedMediaPreview.displayMedia(adapter.requestManager,
2017-08-28 11:56:09 +02:00
media = quotedMedia, accountKey = status.account_key,
mediaClickListener = adapter.fragment)
2020-06-09 02:21:48 +02:00
}
else -> {
itemView.quotedMediaLabel.visibility = View.VISIBLE
itemView.quotedMediaPreview.visibility = View.GONE
}
2017-08-28 11:56:09 +02:00
}
} else {
itemView.quotedName.visibility = View.GONE
itemView.quotedText.visibility = View.VISIBLE
itemView.quotedMediaLabel.visibility = View.GONE
itemView.quotedMediaPreview.visibility = View.GONE
// Not available
val string = SpannableString.valueOf(context.getString(R.string.label_status_not_available))
string.setSpan(ForegroundColorSpan(ThemeUtils.getColorFromAttribute(context,
android.R.attr.textColorTertiary, textView.currentTextColor)), 0,
string.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
itemView.quotedText.spannable = string
itemView.quotedView.drawStart(ThemeUtils.getColorFromAttribute(context,
R.attr.quoteIndicatorBackgroundColor))
}
} else {
itemView.quotedView.visibility = View.GONE
}
itemView.profileContainer.drawStart(colorNameManager.getUserColor(status.user_key))
2020-06-09 01:09:31 +02:00
val timestamp: Long = if (status.is_retweet) {
2020-06-08 23:19:10 +02:00
status.retweet_timestamp
2017-08-28 11:56:09 +02:00
} else {
2020-06-08 23:19:10 +02:00
status.timestamp
2017-08-28 11:56:09 +02:00
}
nameView.name = colorNameManager.getUserNickname(status.user_key, status.user_name)
nameView.screenName = "@${status.user_acct}"
nameView.updateText(formatter)
adapter.requestManager.loadProfileImage(context, status, adapter.profileImageStyle,
itemView.profileImage.cornerRadius, itemView.profileImage.cornerRadiusRatio,
size = ProfileImageSize.ORIGINAL).into(itemView.profileImage)
val typeIconRes = Utils.getUserTypeIconRes(status.user_is_verified, status.user_is_protected)
val typeDescriptionRes = Utils.getUserTypeDescriptionRes(status.user_is_verified, status.user_is_protected)
if (typeIconRes != 0 && typeDescriptionRes != 0) {
profileTypeView.setImageResource(typeIconRes)
profileTypeView.contentDescription = context.getString(typeDescriptionRes)
profileTypeView.visibility = View.VISIBLE
} else {
profileTypeView.setImageDrawable(null)
profileTypeView.contentDescription = null
profileTypeView.visibility = View.GONE
}
val timeString = Utils.formatToLongTimeString(context, timestamp)?.takeIf(String::isNotEmpty)
val source = status.source?.takeIf(String::isNotEmpty)
itemView.timeSource.spannable = when {
timeString != null && source != null -> {
HtmlSpanBuilder.fromHtml(context.getString(R.string.status_format_time_source,
timeString, source))
}
source != null -> HtmlSpanBuilder.fromHtml(source)
timeString != null -> timeString
else -> null
}
itemView.timeSource.movementMethod = LinkMovementMethod.getInstance()
val displayEnd = status.extras?.display_text_range?.getOrNull(1) ?: -1
val text = SpannableStringBuilder.valueOf(status.text_unescaped).apply {
status.spans?.applyTo(this)
linkify.applyAllLinks(this, status.account_key, layoutPosition.toLong(),
status.is_possibly_sensitive, skipLinksInText)
}
summaryView.spannable = status.extras?.summary_text
summaryView.hideIfEmpty()
if (displayEnd != -1 && displayEnd <= text.length) {
2017-10-03 20:09:01 +02:00
val displayText = text.subSequence(0, displayEnd)
if (!TextUtils.equals(textView.text, displayText)) {
textView.spannable = displayText
}
2017-08-28 11:56:09 +02:00
} else {
2017-10-03 20:09:01 +02:00
if (!TextUtils.equals(textView.text, text)) {
textView.spannable = text
}
2017-08-28 11:56:09 +02:00
}
textView.hideIfEmpty()
val location: ParcelableLocation? = status.location
val placeFullName: String? = status.place_full_name
if (!TextUtils.isEmpty(placeFullName)) {
locationView.visibility = View.VISIBLE
locationView.spannable = placeFullName
locationView.isClickable = ParcelableLocationUtils.isValidLocation(location)
} else if (ParcelableLocationUtils.isValidLocation(location)) {
locationView.visibility = View.VISIBLE
locationView.setText(R.string.action_view_map)
locationView.isClickable = true
} else {
locationView.visibility = View.GONE
locationView.spannable = null
}
val interactUsersAdapter = itemView.countsUsers.adapter as CountsUsersAdapter
if (statusActivity != null) {
updateStatusActivity(statusActivity)
} else {
interactUsersAdapter.setUsers(null)
interactUsersAdapter.setCounts(status)
}
if (interactUsersAdapter.itemCount > 0) {
itemView.countsUsers.visibility = View.VISIBLE
itemView.countsUsersHeightHolder.visibility = View.INVISIBLE
} else {
itemView.countsUsers.visibility = View.GONE
itemView.countsUsersHeightHolder.visibility = View.GONE
}
val media = status.media
2020-06-09 02:21:48 +02:00
when {
media?.isEmpty() != false -> {
itemView.mediaPreviewContainer.visibility = View.GONE
itemView.mediaPreview.visibility = View.GONE
itemView.mediaPreviewLoad.visibility = View.GONE
itemView.mediaPreview.displayMedia()
}
adapter.isDetailMediaExpanded -> {
itemView.mediaPreviewContainer.visibility = View.VISIBLE
itemView.mediaPreview.visibility = View.VISIBLE
itemView.mediaPreviewLoad.visibility = View.GONE
itemView.mediaPreview.displayMedia(adapter.requestManager, media = media,
2017-08-28 11:56:09 +02:00
accountKey = status.account_key, mediaClickListener = adapter.fragment)
2020-06-09 02:21:48 +02:00
}
else -> {
itemView.mediaPreviewContainer.visibility = View.VISIBLE
itemView.mediaPreview.visibility = View.GONE
itemView.mediaPreviewLoad.visibility = View.VISIBLE
itemView.mediaPreview.displayMedia()
}
2017-08-28 11:56:09 +02:00
}
if (TwitterCardUtils.isCardSupported(status)) {
val size = TwitterCardUtils.getCardSize(status.card!!)
if (size != null) {
itemView.twitterCard.setCardSize(size.x, size.y)
} else {
itemView.twitterCard.setCardSize(0, 0)
}
val vc = TwitterCardViewFactory.from(status)
itemView.twitterCard.viewController = vc
if (vc != null) {
itemView.twitterCard.visibility = View.VISIBLE
} else {
itemView.twitterCard.visibility = View.GONE
}
} else {
itemView.twitterCard.viewController = null
itemView.twitterCard.visibility = View.GONE
}
MenuUtils.setupForStatus(context, itemView.menuBar.menu, fragment.preferences, twitter,
colorNameManager, status, adapter.statusAccount!!)
val lang = status.lang
2020-05-13 10:36:15 +02:00
if (CheckUtils.isValidLocale(lang) /* && account.isOfficial(context)*/) {
translateContainer.visibility = View.VISIBLE
if (translation != null) {
val locale = Locale(translation.translatedLang)
translateLabelView.text = context.getString(R.string.label_translated_to_language,
locale.displayLanguage)
translateResultView.visibility = View.VISIBLE
translateChangeLanguageView.visibility = View.VISIBLE
translateResultView.text = translation.text
} else {
val locale = Locale(lang)
translateLabelView.text = context.getString(R.string.label_translate_from_language,
locale.displayLanguage)
translateResultView.visibility = View.GONE
translateChangeLanguageView.visibility = View.GONE
}
} else {
translateLabelView.setText(R.string.unknown_language)
translateContainer.visibility = View.GONE
}
2017-08-28 11:56:09 +02:00
textView.setTextIsSelectable(true)
translateResultView.setTextIsSelectable(true)
textView.movementMethod = LinkMovementMethod.getInstance()
itemView.quotedText.movementMethod = null
2020-06-02 11:15:34 +02:00
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()
}
2017-08-28 11:56:09 +02:00
}
override fun onClick(v: View) {
val status = adapter.getStatus(layoutPosition)
val fragment = adapter.fragment
val preferences = fragment.preferences
when (v) {
2020-06-02 11:15:34 +02:00
itemView.linkPreview -> {
val url = status.extras?.entities_url?.firstOrNull()
OnLinkClickHandler.openLink(fragment.requireContext(), preferences, Uri.parse(url))
}
2017-08-28 11:56:09 +02:00
itemView.mediaPreviewLoad -> {
if (adapter.sensitiveContentEnabled || !status.is_possibly_sensitive) {
adapter.isDetailMediaExpanded = true
} else {
val f = StatusFragment.LoadSensitiveImageConfirmDialogFragment()
f.show(fragment.childFragmentManager, "load_sensitive_image_confirm")
}
}
itemView.profileContainer -> {
2020-01-26 08:35:15 +01:00
val activity = fragment.activity ?: return
2017-08-28 11:56:09 +02:00
IntentUtils.openUserProfile(activity, status.account_key, status.user_key,
status.user_screen_name, status.extras?.user_statusnet_profile_url,
preferences[newDocumentApiKey], null)
}
retweetedByView -> {
if (status.retweet_id != null) {
IntentUtils.openUserProfile(adapter.context, status.account_key,
status.retweeted_by_user_key, status.retweeted_by_user_screen_name,
null, preferences[newDocumentApiKey], null)
}
}
locationView -> {
val location = status.location
if (!ParcelableLocationUtils.isValidLocation(location)) return
IntentUtils.openMap(adapter.context, location.latitude, location.longitude)
}
itemView.quotedView -> {
val quotedId = status.quoted_id ?: return
IntentUtils.openStatus(adapter.context, status.account_key, quotedId)
}
translateLabelView -> {
fragment.loadTranslation(adapter.status)
}
translateChangeLanguageView -> {
fragment.openTranslationDestinationChooser()
}
}
}
override fun onMenuItemClick(item: MenuItem): Boolean {
val layoutPosition = layoutPosition
if (layoutPosition < 0) return false
val fragment = adapter.fragment
val status = adapter.getStatus(layoutPosition)
val preferences = fragment.preferences
val twitter = fragment.twitterWrapper
val manager = fragment.userColorNameManager
2020-01-26 08:35:15 +01:00
val activity = fragment.activity ?: return false
2017-08-28 11:56:09 +02:00
return MenuUtils.handleStatusClick(activity, fragment, fragment.childFragmentManager,
preferences, manager, twitter, status, item)
}
internal fun updateStatusActivity(activity: StatusFragment.StatusActivity) {
val adapter = itemView.countsUsers.adapter as CountsUsersAdapter
adapter.setUsers(activity.retweeters)
adapter.setCounts(activity)
}
private fun initViews() {
itemView.menuBar.setOnMenuItemClickListener(this)
val fragment = adapter.fragment
2020-01-26 08:35:15 +01:00
val activity = fragment.activity ?: return
2017-08-28 11:56:09 +02:00
val inflater = activity.menuInflater
val menu = itemView.menuBar.menu
inflater.inflate(R.menu.menu_detail_status, menu)
2017-09-15 11:40:35 +02:00
2017-08-28 11:56:09 +02:00
val favoriteItem = menu.findItem(R.id.favorite)
2017-09-15 11:40:35 +02:00
val favoriteProvider = favoriteItem?.supportActionProvider
if (favoriteProvider is FavoriteItemProvider) {
2017-08-28 11:56:09 +02:00
val defaultColor = ThemeUtils.getActionIconColor(activity)
2017-09-15 11:40:35 +02:00
favoriteProvider.defaultColor = defaultColor
2017-08-28 11:56:09 +02:00
val favoriteHighlight = ContextCompat.getColor(activity, R.color.highlight_favorite)
val likeHighlight = ContextCompat.getColor(activity, R.color.highlight_like)
val useStar = adapter.useStarsForLikes
2017-09-15 11:40:35 +02:00
favoriteProvider.activatedColor = if (useStar) favoriteHighlight else likeHighlight
favoriteProvider.icon = if (useStar) R.drawable.ic_action_star else R.drawable.ic_action_heart
favoriteProvider.useStar = useStar
favoriteProvider.longClickListener = {
val status = adapter.getStatus(layoutPosition)
val itemId = adapter.getItemId(layoutPosition)
AbsStatusesFragment.handleActionLongClick(fragment, status, itemId, R.id.favorite)
}
favoriteProvider.init(itemView.menuBar, favoriteItem)
}
val retweetItem = menu.findItem(R.id.retweet)
val retweetProvider = retweetItem?.supportActionProvider
if (retweetProvider is RetweetItemProvider) {
retweetProvider.longClickListener = {
val status = adapter.getStatus(layoutPosition)
val itemId = adapter.getItemId(layoutPosition)
AbsStatusesFragment.handleActionLongClick(fragment, status, itemId, R.id.retweet)
}
retweetProvider.init(itemView.menuBar, retweetItem)
2017-08-28 11:56:09 +02:00
}
2017-09-15 11:40:35 +02:00
2019-06-20 14:04:52 +02:00
ThemeUtils.wrapMenuIcon(itemView.menuBar, excludeGroups = *intArrayOf(Constants.MENU_GROUP_STATUS_SHARE))
2017-08-28 11:56:09 +02:00
itemView.mediaPreviewLoad.setOnClickListener(this)
itemView.profileContainer.setOnClickListener(this)
2020-06-02 11:15:34 +02:00
itemView.linkPreview.setOnClickListener(this)
2017-08-28 11:56:09 +02:00
retweetedByView.setOnClickListener(this)
locationView.setOnClickListener(this)
itemView.quotedView.setOnClickListener(this)
translateLabelView.setOnClickListener(this)
translateChangeLanguageView.setOnClickListener(this)
val textSize = adapter.textSize
nameView.setPrimaryTextSize(textSize * 1.25f)
nameView.setSecondaryTextSize(textSize * 0.85f)
summaryView.textSize = textSize * 1.25f
textView.textSize = textSize * 1.25f
itemView.quotedName.setPrimaryTextSize(textSize * 1.25f)
itemView.quotedName.setSecondaryTextSize(textSize * 0.85f)
itemView.quotedText.textSize = textSize * 1.25f
locationView.textSize = textSize * 0.85f
itemView.timeSource.textSize = textSize * 0.85f
translateLabelView.textSize = textSize * 0.85f
2017-10-01 12:51:51 +02:00
translateChangeLanguageView.textSize = textSize * 0.85f
2017-08-28 11:56:09 +02:00
translateResultView.textSize = textSize * 1.05f
itemView.countsUsersHeightHolder.count.textSize = textSize * 1.25f
itemView.countsUsersHeightHolder.label.textSize = textSize * 0.85f
nameView.nameFirst = adapter.nameFirst
itemView.quotedName.nameFirst = adapter.nameFirst
itemView.mediaPreview.style = adapter.mediaPreviewStyle
itemView.quotedMediaPreview.style = adapter.mediaPreviewStyle
itemView.text.customSelectionActionModeCallback = StatusActionModeCallback(itemView.text, activity)
itemView.profileImage.style = adapter.profileImageStyle
val layoutManager = LinearLayoutManager(adapter.context)
layoutManager.orientation = LinearLayoutManager.HORIZONTAL
itemView.countsUsers.layoutManager = layoutManager
val countsUsersAdapter = CountsUsersAdapter(fragment, adapter)
itemView.countsUsers.adapter = countsUsersAdapter
val resources = activity.resources
itemView.countsUsers.addItemDecoration(SpacingItemDecoration(resources.getDimensionPixelOffset(R.dimen.element_spacing_normal)))
// Apply font families
nameView.applyFontFamily(adapter.lightFont)
summaryView.applyFontFamily(adapter.lightFont)
textView.applyFontFamily(adapter.lightFont)
itemView.quotedName.applyFontFamily(adapter.lightFont)
itemView.quotedText.applyFontFamily(adapter.lightFont)
itemView.locationView.applyFontFamily(adapter.lightFont)
translateLabelView.applyFontFamily(adapter.lightFont)
translateResultView.applyFontFamily(adapter.lightFont)
}
private class CountsUsersAdapter(
private val fragment: StatusFragment,
private val statusAdapter: StatusDetailsAdapter
) : BaseRecyclerViewAdapter<RecyclerView.ViewHolder>(statusAdapter.context, fragment.requestManager) {
2017-08-28 11:56:09 +02:00
private val inflater = LayoutInflater.from(statusAdapter.context)
private var counts: List<LabeledCount>? = null
private var users: List<ParcelableUser>? = null
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder.itemViewType) {
ITEM_VIEW_TYPE_USER -> {
(holder as ProfileImageViewHolder).displayUser(getUser(position)!!)
}
ITEM_VIEW_TYPE_COUNT -> {
2020-05-14 09:47:36 +02:00
(holder as CountViewHolder).displayCount(getCount(position)!!, preferences[hideCardNumbersKey])
2017-08-28 11:56:09 +02:00
}
}
}
private fun getCount(position: Int): LabeledCount? {
if (counts == null) return null
if (position < countItemsCount) {
return counts!![position]
}
return null
}
override fun getItemCount(): Int {
return countItemsCount + usersCount
}
override fun getItemViewType(position: Int): Int {
val countItemsCount = countItemsCount
if (position < countItemsCount) {
return ITEM_VIEW_TYPE_COUNT
}
return ITEM_VIEW_TYPE_USER
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when (viewType) {
ITEM_VIEW_TYPE_USER -> return ProfileImageViewHolder(this, inflater.inflate(R.layout.adapter_item_status_interact_user, parent, false))
ITEM_VIEW_TYPE_COUNT -> return CountViewHolder(this, inflater.inflate(R.layout.adapter_item_status_count_label, parent, false))
}
2020-06-08 23:07:20 +02:00
throw UnsupportedOperationException("Unsupported viewType $viewType")
2017-08-28 11:56:09 +02:00
}
fun setUsers(users: List<ParcelableUser>?) {
this.users = users
notifyDataSetChanged()
}
fun setCounts(activity: StatusFragment.StatusActivity?) {
if (activity != null) {
val counts = ArrayList<LabeledCount>()
val replyCount = activity.replyCount
if (replyCount > 0) {
counts.add(LabeledCount(KEY_REPLY_COUNT, replyCount))
}
val retweetCount = activity.retweetCount
if (retweetCount > 0) {
counts.add(LabeledCount(KEY_RETWEET_COUNT, retweetCount))
}
val favoriteCount = activity.favoriteCount
if (favoriteCount > 0) {
counts.add(LabeledCount(KEY_FAVORITE_COUNT, favoriteCount))
}
this.counts = counts
} else {
counts = null
}
notifyDataSetChanged()
}
fun setCounts(status: ParcelableStatus?) {
if (status != null) {
val counts = ArrayList<LabeledCount>()
if (status.reply_count > 0) {
counts.add(LabeledCount(KEY_REPLY_COUNT, status.reply_count))
}
if (status.retweet_count > 0) {
counts.add(LabeledCount(KEY_RETWEET_COUNT, status.retweet_count))
}
if (status.favorite_count > 0) {
counts.add(LabeledCount(KEY_FAVORITE_COUNT, status.favorite_count))
}
this.counts = counts
} else {
counts = null
}
notifyDataSetChanged()
}
val countItemsCount: Int
get() {
if (counts == null) return 0
return counts!!.size
}
private val usersCount: Int
get() {
if (users == null) return 0
return users!!.size
}
private fun notifyItemClick(position: Int) {
when (getItemViewType(position)) {
ITEM_VIEW_TYPE_COUNT -> {
val count = getCount(position)
val status = statusAdapter.status
if (count == null || status == null) return
when (count.type) {
KEY_RETWEET_COUNT -> {
IntentUtils.openStatusRetweeters(context, status.account_key,
status.originalId)
}
KEY_FAVORITE_COUNT -> {
IntentUtils.openStatusFavoriters(context, status.account_key,
status.originalId)
}
}
}
ITEM_VIEW_TYPE_USER -> {
fragment.onUserClick(getUser(position)!!)
}
}
}
private fun getUser(position: Int): ParcelableUser? {
val countItemsCount = countItemsCount
if (users == null || position < countItemsCount) return null
return users!![position - countItemsCount]
}
internal class ProfileImageViewHolder(private val adapter: CountsUsersAdapter, itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
private val profileImageView = itemView.findViewById<ProfileImageView>(R.id.profileImage)
init {
itemView.setOnClickListener(this)
}
fun displayUser(item: ParcelableUser) {
val context = adapter.context
val requestManager = adapter.requestManager
requestManager.loadProfileImage(context, item, adapter.profileImageStyle,
profileImageView.cornerRadius, profileImageView.cornerRadiusRatio,
adapter.profileImageSize).into(profileImageView)
}
override fun onClick(v: View) {
adapter.notifyItemClick(layoutPosition)
}
}
internal class CountViewHolder(
private val adapter: CountsUsersAdapter,
itemView: View
) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
init {
itemView.setOnClickListener(this)
val textSize = adapter.textSize
itemView.count.textSize = textSize * 1.25f
itemView.label.textSize = textSize * 0.85f
}
override fun onClick(v: View) {
adapter.notifyItemClick(layoutPosition)
}
2020-05-14 09:47:36 +02:00
fun displayCount(count: LabeledCount, hideNumbers: Boolean) {
2020-06-09 01:09:31 +02:00
val label: String = when (count.type) {
2017-08-28 11:56:09 +02:00
KEY_REPLY_COUNT -> {
2020-06-08 23:19:10 +02:00
adapter.context.getString(R.string.replies)
2017-08-28 11:56:09 +02:00
}
KEY_RETWEET_COUNT -> {
2020-06-08 23:19:10 +02:00
adapter.context.getString(R.string.count_label_retweets)
2017-08-28 11:56:09 +02:00
}
KEY_FAVORITE_COUNT -> {
2020-06-08 23:19:10 +02:00
adapter.context.getString(R.string.title_favorites)
2017-08-28 11:56:09 +02:00
}
else -> {
throw UnsupportedOperationException("Unsupported type " + count.type)
}
}
2020-05-14 09:47:36 +02:00
if (!hideNumbers) {
itemView.count.text = Utils.getLocalizedNumber(Locale.getDefault(), count.count)
}
2017-08-28 11:56:09 +02:00
itemView.label.text = label
}
}
internal class LabeledCount(var type: Int, var count: Long)
companion object {
2020-06-08 23:11:06 +02:00
private const val ITEM_VIEW_TYPE_USER = 1
private const val ITEM_VIEW_TYPE_COUNT = 2
2017-08-28 11:56:09 +02:00
2020-06-08 23:11:06 +02:00
private const val KEY_REPLY_COUNT = 1
private const val KEY_RETWEET_COUNT = 2
private const val KEY_FAVORITE_COUNT = 3
2017-08-28 11:56:09 +02:00
}
}
private class DetailStatusLinkClickHandler(
context: Context,
manager: MultiSelectManager,
private val adapter: StatusDetailsAdapter,
preferences: SharedPreferences
) : StatusLinkClickHandler(context, manager, preferences) {
override fun onLinkClick(link: String, orig: String?, accountKey: UserKey?,
extraId: Long, type: Int, sensitive: Boolean, start: Int, end: Int): Boolean {
val position = extraId.toInt()
val current = getCurrentMedia(link, position)
if (current != null && !current.open_browser) {
expandOrOpenMedia(current)
return true
}
return super.onLinkClick(link, orig, accountKey, extraId, type, sensitive, start, end)
}
private fun expandOrOpenMedia(current: ParcelableMedia) {
if (adapter.isDetailMediaExpanded) {
IntentUtils.openMedia(adapter.context, adapter.status!!, current,
preferences[newDocumentApiKey], preferences[displaySensitiveContentsKey])
return
}
adapter.isDetailMediaExpanded = true
}
override fun isMedia(link: String, extraId: Long): Boolean {
val current = getCurrentMedia(link, extraId.toInt())
return current != null && !current.open_browser
}
private fun getCurrentMedia(link: String, extraId: Int): ParcelableMedia? {
val status = adapter.getStatus(extraId)
val media = ParcelableMediaUtils.getAllMedia(status)
return findByLink(media, link)
}
}
private class SpacingItemDecoration(private val spacing: Int) : RecyclerView.ItemDecoration() {
2020-01-26 08:35:15 +01:00
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
2017-08-28 11:56:09 +02:00
if (ViewCompat.getLayoutDirection(parent) == ViewCompat.LAYOUT_DIRECTION_RTL) {
outRect.set(spacing, 0, 0, 0)
} else {
outRect.set(0, 0, spacing, 0)
}
}
}
2017-09-15 11:40:35 +02:00
companion object {
const val REQUEST_FAVORITE_SELECT_ACCOUNT = 101
const val REQUEST_RETWEET_SELECT_ACCOUNT = 102
}
2017-08-28 11:56:09 +02:00
}