SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/itemviewholder/ItemViewHolderShow.kt

868 lines
28 KiB
Kotlin

package jp.juggler.subwaytooter.itemviewholder
import android.annotation.SuppressLint
import android.content.res.ColorStateList
import android.graphics.Typeface
import android.os.SystemClock
import android.text.Spannable
import android.text.SpannableString
import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import android.view.View
import android.widget.Button
import android.widget.TextView
import androidx.annotation.StringRes
import jp.juggler.subwaytooter.*
import jp.juggler.subwaytooter.actmain.closePopup
import jp.juggler.subwaytooter.api.TootParser
import jp.juggler.subwaytooter.api.entity.*
import jp.juggler.subwaytooter.column.Column
import jp.juggler.subwaytooter.column.ColumnType
import jp.juggler.subwaytooter.column.getAcctColor
import jp.juggler.subwaytooter.column.getContentColor
import jp.juggler.subwaytooter.columnviewholder.ItemListAdapter
import jp.juggler.subwaytooter.drawable.PreviewCardBorder
import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.subwaytooter.pref.PrefI
import jp.juggler.subwaytooter.span.MyClickableSpan
import jp.juggler.subwaytooter.util.emojiSizeMode
import jp.juggler.subwaytooter.table.*
import jp.juggler.subwaytooter.util.DecodeOptions
import jp.juggler.subwaytooter.view.MyNetworkImageView
import jp.juggler.util.*
import jp.juggler.util.data.*
import jp.juggler.util.log.*
import jp.juggler.util.ui.*
import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.textColor
import kotlin.math.max
private val log = LogCategory("ItemViewHolderShow")
@SuppressLint("ClickableViewAccessibility")
fun ItemViewHolder.bind(
listAdapter: ItemListAdapter,
column: Column,
bSimpleList: Boolean,
item: TimelineItem,
) {
val b = Benchmark(ItemViewHolder.log, "Item-bind", 40L)
this.listAdapter = listAdapter
this.column = column
this.bSimpleList = bSimpleList
this.accessInfo = column.accessInfo
val fontBold = ActMain.timelineFontBold
val fontNormal = ActMain.timelineFont
viewRoot.scan { v ->
try {
when (v) {
// ボタンは太字なので触らない
is Button, is CountImageButton -> Unit
is TextView -> v.typeface = when {
v === tvName ||
v === tvFollowerName ||
v === tvBoosted ||
v === tvReply ||
v === tvTrendTagCount ||
v === tvTrendTagName ||
v === tvConversationIconsMore ||
v === tvConversationParticipants ||
v === tvFilterPhrase -> fontBold
else -> fontNormal
}
}
} catch (ex: Throwable) {
log.e(ex, "can't change font.")
}
}
if (bSimpleList) {
viewRoot.setOnTouchListener { _, ev ->
// ポップアップを閉じた時にクリックでリストを触ったことになってしまう不具合の回避
val now = SystemClock.elapsedRealtime()
// ポップアップを閉じた直後はタッチダウンを無視する
if (now - StatusButtonsPopup.lastPopupClose >= 30L) {
false
} else {
val action = ev.action
ItemViewHolder.log.d("onTouchEvent action=$action")
true
}
}
viewRoot.setOnClickListener { viewClicked ->
activity.closePopup()
statusShowing?.let { status ->
val popup =
StatusButtonsPopup(activity, column, true, this@bind)
activity.popupStatusButtons = popup
popup.show(
listAdapter.columnVh.listView,
viewClicked,
status,
item as? TootNotification
)
}
}
llButtonBar.visibility = View.GONE
this.buttonsForStatus = null
} else {
viewRoot.isClickable = false
llButtonBar.visibility = View.VISIBLE
this.buttonsForStatus = StatusButtons(
activity,
column,
false,
statusButtonsViewHolder,
this
)
}
this.statusShowing = null
this.statusReply = null
this.statusAccount = null
this.boostAccount = null
this.followAccount = null
this.boostTime = 0L
this.viewRoot.setBackgroundColor(0)
this.boostedAction = defaultBoostedAction
llOpenSticker.visibility = View.GONE
llBoosted.visibility = View.GONE
llReply.visibility = View.GONE
llFollow.visibility = View.GONE
llStatus.visibility = View.GONE
llSearchTag.visibility = View.GONE
btnGapHead.visibility = View.GONE
btnGapTail.visibility = View.GONE
llList.visibility = View.GONE
llFollowRequest.visibility = View.GONE
tvMessageHolder.visibility = View.GONE
llTrendTag.visibility = View.GONE
llFilter.visibility = View.GONE
tvMediaDescriptions.forEach { it.visibility = View.GONE }
llCardOuter.visibility = View.GONE
tvCardText.visibility = View.GONE
flCardImage.visibility = View.GONE
llConversationIcons.visibility = View.GONE
removeExtraView()
var c: Int
c = column.getContentColor()
this.colorTextContent = c
this.contentColorCsl = ColorStateList.valueOf(c)
tvBoosted.setTextColor(c)
tvReply.setTextColor(c)
tvFollowerName.setTextColor(c)
tvName.setTextColor(c)
tvMentions.setTextColor(c)
tvContentWarning.setTextColor(c)
tvContent.setTextColor(c)
//NSFWは文字色固定 btnShowMedia.setTextColor( c );
tvApplication.setTextColor(c)
tvMessageHolder.setTextColor(c)
tvTrendTagName.setTextColor(c)
tvTrendTagCount.setTextColor(c)
cvTagHistory.setColor(c)
tvFilterPhrase.setTextColor(c)
tvMediaDescriptions.forEach { it.setTextColor(c) }
tvCardText.setTextColor(c)
tvConversationIconsMore.setTextColor(c)
tvConversationParticipants.setTextColor(c)
(llCardOuter.background as? PreviewCardBorder)?.let {
val rgb = c and 0xffffff
val alpha = max(1, c ushr (24 + 1)) // 本来の値の半分にする
it.color = rgb or (alpha shl 24)
}
c = column.getAcctColor()
this.acctColor = c
tvBoostedTime.setTextColor(c)
tvTime.setTextColor(c)
tvTrendTagDesc.setTextColor(c)
tvFilterDetail.setTextColor(c)
tvFilterPhrase.setTextColor(c)
// 以下のビューの文字色はsetAcct() で設定される
// tvBoostedAcct.setTextColor(c)
// tvFollowerAcct.setTextColor(c)
// tvAcct.setTextColor(c)
this.item = item
when (item) {
is TootStatus -> {
val reblog = item.reblog
when {
reblog == null -> showStatusOrReply(item)
item.isQuoteToot -> {
// 引用Renote
val colorBg = PrefI.ipEventBgColorBoost.value
showReply(item.account, reblog, R.drawable.ic_quote, R.string.quote_to)
showStatus(item, colorBg)
}
else -> {
// 引用なしブースト
val colorBg = PrefI.ipEventBgColorBoost.value
showBoost(
item.accountRef,
item.time_created_at,
R.drawable.ic_repeat,
R.string.display_name_boosted_by,
boostStatus = item
)
showStatusOrReply(reblog, colorBg)
}
}
}
is TootAccountRef -> showAccount(item)
is TootNotification -> showNotification(item)
is TootGap -> showGap()
is TootSearchGap -> showSearchGap(item)
is TootDomainBlock -> showDomainBlock(item)
is TootList -> showList(item)
is MisskeyAntenna -> showAntenna(item)
is TootMessageHolder -> showMessageHolder(item)
is TootTag -> showSearchTag(item)
is TootFilter -> showFilter(item)
is TootConversationSummary -> {
showStatusOrReply(item.last_status)
showConversationIcons(item)
}
is TootScheduled -> showScheduled(item)
}
b.report()
}
fun ItemViewHolder.removeExtraView() {
llExtra.scan { v ->
if (v is MyNetworkImageView) {
v.cancelLoading()
}
}
llExtra.removeAllViews()
for (invalidator in extraInvalidatorList) {
invalidator.register(null)
}
extraInvalidatorList.clear()
}
fun ItemViewHolder.showAccount(whoRef: TootAccountRef) {
followAccount = whoRef
val who = whoRef.get()
llFollow.visibility = View.VISIBLE
ivFollow.setImageUrl(
calcIconRound(ivFollow.layoutParams),
accessInfo.supplyBaseUrl(who.avatar_static),
accessInfo.supplyBaseUrl(who.avatar)
)
followInvalidator.text = whoRef.decoded_display_name
setAcct(tvFollowerAcct, accessInfo, who)
who.setAccountExtra(
accessInfo,
lastActiveInvalidator,
suggestionSource = if (column.type == ColumnType.FOLLOW_SUGGESTION) {
SuggestionSource.get(accessInfo.db_id, who.acct)
} else {
null
}
)
val relation = daoUserRelation.load(accessInfo.db_id, who.id)
setFollowIcon(
activity,
btnFollow,
ivFollowedBy,
relation,
who,
colorTextContent,
alphaMultiplier = stylerBoostAlpha
)
if (column.type == ColumnType.FOLLOW_REQUESTS) {
llFollowRequest.visibility = View.VISIBLE
btnFollowRequestAccept.imageTintList = contentColorCsl
btnFollowRequestDeny.imageTintList = contentColorCsl
}
}
fun ItemViewHolder.showAntenna(a: MisskeyAntenna) {
llList.visibility = View.VISIBLE
btnListTL.text = a.name
btnListTL.textColor = colorTextContent
btnListMore.imageTintList = contentColorCsl
}
fun ItemViewHolder.showBoost(
whoRef: TootAccountRef,
time: Long,
iconId: Int,
@StringRes stringId: Int,
reaction: TootReaction? = null,
boostStatus: TootStatus? = null,
reblogVisibility: TootVisibility? = null,
) {
boostAccount = whoRef
val who = whoRef.get()
setIconDrawableId(
activity,
ivBoosted,
iconId,
color = colorTextContent,
alphaMultiplier = stylerBoostAlpha
)
ivBoostAvatar.let { v ->
v.setImageUrl(
calcIconRound(v.layoutParams),
accessInfo.supplyBaseUrl(who.avatar_static),
accessInfo.supplyBaseUrl(who.avatar)
)
}
boostTime = time
llBoosted.visibility = View.VISIBLE
showStatusTime(
activity,
tvBoostedTime,
who,
time = time,
status = boostStatus,
reblogVisibility = reblogVisibility
)
setAcct(tvBoostedAcct, accessInfo, who)
// フォローの場合 decoded_display_name が2箇所で表示に使われるのを避ける必要がある
if (reaction != null) {
val options = DecodeOptions(
activity,
accessInfo,
decodeEmoji = true,
enlargeEmoji = DecodeOptions.emojiScaleReaction,
enlargeCustomEmoji = DecodeOptions.emojiScaleReaction,
emojiSizeMode = accessInfo.emojiSizeMode(),
)
val ssb = reaction.toSpannableStringBuilder(options, boostStatus)
ssb.append(" ")
ssb.append(
who.decodeDisplayNameCached(activity)
.intoStringResource(activity, stringId)
)
boostInvalidator.text = ssb
} else {
boostInvalidator.text =
who.decodeDisplayNameCached(activity)
.intoStringResource(activity, stringId)
}
}
fun ItemViewHolder.showMessageHolder(item: TootMessageHolder) {
tvMessageHolder.visibility = View.VISIBLE
tvMessageHolder.text = item.text
tvMessageHolder.gravity = item.gravity
}
fun ItemViewHolder.showList(list: TootList) {
llList.visibility = View.VISIBLE
btnListTL.text = list.title
btnListTL.textColor = colorTextContent
btnListMore.imageTintList = contentColorCsl
}
fun ItemViewHolder.showDomainBlock(domainBlock: TootDomainBlock) {
llSearchTag.visibility = View.VISIBLE
btnSearchTag.text = domainBlock.domain.pretty
}
fun ItemViewHolder.showFilter(filter: TootFilter) {
llFilter.visibility = View.VISIBLE
tvFilterPhrase.text = filter.displayString
tvFilterDetail.text = StringBuffer().apply {
val contextNames = filter.contextNames.joinToString("/") { activity.getString(it) }
append(activity.getString(R.string.filter_context))
append(": ")
append(contextNames)
val action = when (filter.hide) {
true -> activity.getString(R.string.filter_action_hide)
else -> activity.getString(R.string.filter_action_warn)
}
append('\n')
append(activity.getString(R.string.filter_action))
append(": ")
append(action)
if (filter.time_expires_at > 0L) {
append('\n')
append(activity.getString(R.string.filter_expires_at))
append(": ")
append(TootStatus.formatTime(activity, filter.time_expires_at, false))
}
}.toString()
}
fun ItemViewHolder.showSearchTag(tag: TootTag) {
if (tag.history?.isNotEmpty() == true) {
llTrendTag.visibility = View.VISIBLE
tvTrendTagCount.text = "${tag.countDaily}(${tag.countWeekly})"
cvTagHistory.setHistory(tag.history)
when (tag.type) {
TootTag.TagType.Link -> {
tvTrendTagName.text = tag.url?.ellipsizeDot3(256)
tvTrendTagDesc.text = tag.name + "\n" + tag.description
}
TootTag.TagType.Tag -> {
tvTrendTagName.text = "#${tag.name.ellipsizeDot3(256)}"
tvTrendTagDesc.text = listOf(
when (tag.following) {
true -> activity.getString(R.string.following)
else -> ""
},
activity.getString(R.string.people_talking, tag.accountDaily, tag.accountWeekly)
).filter { it.isNotEmpty() }.joinToString(" ")
}
}
} else {
llSearchTag.visibility = View.VISIBLE
btnSearchTag.text = "#" + tag.name
}
}
fun ItemViewHolder.showGap() {
llSearchTag.visibility = View.VISIBLE
btnSearchTag.text = activity.getString(R.string.read_gap)
btnGapHead.vg(column.type.gapDirection(column, true))
?.imageTintList = contentColorCsl
btnGapTail.vg(column.type.gapDirection(column, false))
?.imageTintList = contentColorCsl
val c = PrefI.ipEventBgColorGap.value
if (c != 0) this.viewRoot.backgroundColor = c
}
fun ItemViewHolder.showSearchGap(item: TootSearchGap) {
llSearchTag.visibility = View.VISIBLE
btnSearchTag.text = activity.getString(
when (item.type) {
TootSearchGap.SearchType.Hashtag -> R.string.read_more_hashtag
TootSearchGap.SearchType.Account -> R.string.read_more_account
TootSearchGap.SearchType.Status -> R.string.read_more_status
}
)
}
fun ItemViewHolder.showReply(
// 返信した人
replyer: TootAccount?,
// 返信された人
target: TootAccount?,
iconId: Int,
text: Spannable,
) {
llReply.visibility = View.VISIBLE
setIconDrawableId(
activity,
ivReply,
iconId,
color = colorTextContent,
alphaMultiplier = stylerBoostAlpha
)
ivReplyAvatar.vg(target != null && target.avatar != replyer?.avatar)?.let { v ->
v.setImageUrl(
calcIconRound(v.layoutParams),
accessInfo.supplyBaseUrl(target!!.avatar_static),
accessInfo.supplyBaseUrl(target.avatar)
)
}
replyInvalidator.text = text
}
fun ItemViewHolder.showReply(replyer: TootAccount?, reply: TootStatus, iconId: Int, stringId: Int) {
statusReply = reply
showReply(
replyer = replyer,
target = reply.accountRef.get(),
iconId,
reply.accountRef.decoded_display_name.intoStringResource(activity, stringId)
)
}
fun ItemViewHolder.showReply(replyer: TootAccount?, reply: TootStatus, accountId: EntityId) {
val name = if (accountId == reply.account.id) {
// 自己レスなら
daoAcctColor.getNicknameWithColor(accessInfo, reply.account)
} else {
val m = reply.mentions?.find { it.id == accountId }
if (m != null) {
daoAcctColor.getNicknameWithColor(accessInfo.getFullAcct(m.acct))
} else {
SpannableString("ID($accountId)")
}
}
val text = name.intoStringResource(activity, R.string.reply_to)
showReply(replyer = replyer, target = null, R.drawable.ic_reply, text)
// tootsearchはreplyオブジェクトがなくin_reply_toだけが提供される場合があるが
// tootsearchではどのタンスから読んだか分からないのでin_reply_toのIDも信用できない
}
fun ItemViewHolder.showStatusTime(
activity: ActMain,
tv: TextView,
@Suppress("UNUSED_PARAMETER") who: TootAccount,
status: TootStatus? = null,
time: Long? = null,
reblogVisibility: TootVisibility? = null,
) {
val sb = SpannableStringBuilder()
if (status != null) {
if (status.account.isAdmin) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(activity, R.drawable.ic_shield, "admin")
}
if (status.account.isPro) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(activity, R.drawable.ic_authorized, "pro")
}
if (status.account.isCat) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(activity, R.drawable.ic_cat, "cat")
}
// botマーク
if (status.account.bot) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(activity, R.drawable.ic_bot, "bot")
}
if (status.account.suspended) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(activity, R.drawable.ic_delete, "suspended")
}
// mobileマーク
if (status.viaMobile) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(activity, R.drawable.ic_mobile, "mobile")
}
// ブックマーク済み
if (status.bookmarked) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(activity, R.drawable.ic_bookmark_added, "bookmarked")
}
// NSFWマーク
if (status.hasMedia() && status.sensitive) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(activity, R.drawable.ic_eye_off, "NSFW")
}
// visibility
val visIconId = status.visibility.getVisibilityIconId(accessInfo.isMisskey)
if (R.drawable.ic_public != visIconId) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(
activity,
visIconId,
status.visibility.getVisibilityString(accessInfo.isMisskey)
)
}
// pinned
if (status.pinned) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(activity, R.drawable.ic_pin, "pinned")
// val start = sb.length
// sb.append("pinned")
// val end = sb.length
// val icon_id = Styler.getAttributeResourceId(activity, R.attr.ic_pin)
// sb.setSpan(
// EmojiImageSpan(activity, icon_id),
// start,
// end,
// Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
// )
}
// unread
if (status.conversationSummary?.unread == true) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(
activity,
R.drawable.ic_unread,
"unread",
color = MyClickableSpan.defaultLinkColor
)
}
if (status.isPromoted) {
if (sb.isNotEmpty()) sb.append(' ')
sb.append(activity.getString(R.string.promoted))
}
if (status.isFeatured) {
if (sb.isNotEmpty()) sb.append(' ')
sb.append(activity.getString(R.string.featured))
}
if (status.time_edited_at > 0L) {
if (sb.isNotEmpty()) sb.append(' ')
val start = sb.length
sb.append(activity.getString(R.string.edited))
val end = sb.length
sb.setSpan(StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
} else {
reblogVisibility?.takeIf { it != TootVisibility.Unknown }?.let { visibility ->
val visIconId = visibility.getVisibilityIconId(accessInfo.isMisskey)
if (R.drawable.ic_public != visIconId) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(
activity,
visIconId,
visibility.getVisibilityString(accessInfo.isMisskey)
)
}
}
}
if (sb.isNotEmpty()) sb.append(' ')
sb.append(
(time ?: status?.time_created_at)?.let {
TootStatus.formatTime(activity, it, column.canRelativeTime)
} ?: "?"
)
tv.text = sb
}
fun ItemViewHolder.showStatusTimeScheduled(
activity: ActMain,
tv: TextView,
item: TootScheduled,
) {
val sb = SpannableStringBuilder()
// NSFWマーク
if (item.hasMedia() && item.sensitive) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(activity, R.drawable.ic_eye_off, "NSFW")
}
// visibility
val visIconId = item.visibility.getVisibilityIconId(accessInfo.isMisskey)
if (R.drawable.ic_public != visIconId) {
if (sb.isNotEmpty()) sb.append('\u200B')
sb.appendColorShadeIcon(
activity,
visIconId,
item.visibility.getVisibilityString(accessInfo.isMisskey)
)
}
if (sb.isNotEmpty()) sb.append(' ')
sb.append(TootStatus.formatTime(activity, item.timeScheduledAt, column.canRelativeTime))
tv.text = sb
}
val Column.canRelativeTime
get() = when (type) {
ColumnType.CONVERSATION,
ColumnType.CONVERSATION_WITH_REFERENCE,
ColumnType.STATUS_HISTORY,
-> false
else -> true
}
// fun updateRelativeTime() {
// val boost_time = this.boost_time
// if(boost_time != 0L) {
// tvBoostedTime.text = TootStatus.formatTime(tvBoostedTime.context, boost_time, true)
// }
// val status_showing = this.status_showing
// if(status_showing != null) {
// showStatusTime(activity, status_showing)
// }
// }
fun ItemViewHolder.showScheduled(item: TootScheduled) {
try {
llStatus.visibility = View.VISIBLE
this.viewRoot.setBackgroundColor(0)
showStatusTimeScheduled(activity, tvTime, item)
val who = column.whoAccount!!.get()
val whoRef = TootAccountRef.tootAccountRef(TootParser(activity, accessInfo), who)
this.statusAccount = whoRef
setAcct(tvAcct, accessInfo, who)
nameInvalidator.text = whoRef.decoded_display_name
ivAvatar.setImageUrl(
calcIconRound(ivAvatar.layoutParams),
accessInfo.supplyBaseUrl(who.avatar_static),
accessInfo.supplyBaseUrl(who.avatar)
)
val content = SpannableString(item.text ?: "")
tvMentions.visibility = View.GONE
contentInvalidator.text = content
tvContent.minLines = -1
val decodedSpoilerText = SpannableString(item.spoilerText ?: "")
when {
decodedSpoilerText.isNotEmpty() -> {
// 元データに含まれるContent Warning を使う
llContentWarning.visibility = View.VISIBLE
spoilerInvalidator.text = decodedSpoilerText
val cwShown = daoContentWarning.isShown(item.uri, accessInfo.expandCw)
setContentVisibility(cwShown)
}
else -> {
// CWしない
llContentWarning.visibility = View.GONE
llContents.visibility = View.VISIBLE
}
}
val mediaAttachments = item.mediaAttachments
if (mediaAttachments?.isEmpty() != false) {
flMedia.visibility = View.GONE
llMedia.visibility = View.GONE
btnShowMedia.visibility = View.GONE
} else {
flMedia.visibility = View.VISIBLE
// hide sensitive media
val defaultShown = when {
column.hideMediaDefault -> false
accessInfo.dontHideNsfw -> true
else -> !item.sensitive
}
val isShown = daoMediaShown.isShown(item.uri, defaultShown)
btnShowMedia.visibility = if (!isShown) View.VISIBLE else View.GONE
llMedia.visibility = if (!isShown) View.GONE else View.VISIBLE
repeat(ItemViewHolder.MEDIA_VIEW_COUNT) { idx ->
setMedia(mediaAttachments, idx)
}
setIconDrawableId(
activity,
btnHideMedia,
R.drawable.ic_close,
color = colorTextContent,
alphaMultiplier = stylerBoostAlpha
)
}
buttonsForStatus?.hide()
tvApplication.visibility = View.GONE
} catch (ex: Throwable) {
ItemViewHolder.log.w(ex, "showScheduled failed")
}
llSearchTag.visibility = View.VISIBLE
btnSearchTag.text = activity.getString(R.string.scheduled_status) + " " +
TootStatus.formatTime(activity, item.timeScheduledAt, true)
}
fun ItemViewHolder.showConversationIcons(cs: TootConversationSummary) {
val lastAccountId = cs.last_status.account.id
val accountsOther = cs.accounts.filter { it.get().id != lastAccountId }
if (accountsOther.isNotEmpty()) {
llConversationIcons.visibility = View.VISIBLE
val size = accountsOther.size
tvConversationParticipants.text = if (size <= 1) {
activity.getString(R.string.conversation_to)
} else {
activity.getString(R.string.participants)
}
fun showIcon(iv: MyNetworkImageView, idx: Int) {
val bShown = idx < size
iv.visibility = if (bShown) View.VISIBLE else View.GONE
if (!bShown) return
val who = accountsOther[idx].get()
iv.setImageUrl(
calcIconRound(iv.layoutParams),
accessInfo.supplyBaseUrl(who.avatar_static),
accessInfo.supplyBaseUrl(who.avatar)
)
}
showIcon(ivConversationIcon1, 0)
showIcon(ivConversationIcon2, 1)
showIcon(ivConversationIcon3, 2)
showIcon(ivConversationIcon4, 3)
tvConversationIconsMore.text = when {
size <= 4 -> ""
else -> activity.getString(R.string.participants_and_more)
}
}
if (cs.last_status.in_reply_to_id != null) {
llSearchTag.visibility = View.VISIBLE
btnSearchTag.text = activity.getString(R.string.show_conversation)
}
}
fun ItemViewHolder.setAcct(tv: TextView, accessInfo: SavedAccount, who: TootAccount) {
val ac = daoAcctColor.load(accessInfo, who)
tv.text = when {
daoAcctColor.hasNickname(ac) -> ac.nickname
PrefB.bpShortAcctLocalUser.value -> "@${who.acct.pretty}"
else -> "@${ac.nickname}"
}
tv.textColor = ac.colorFg.notZero() ?: this.acctColor
tv.setBackgroundColor(ac.colorBg) // may 0
tv.setPaddingRelative(activity.acctPadLr, 0, activity.acctPadLr, 0)
}