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

474 lines
16 KiB
Kotlin
Raw Normal View History

2021-06-28 09:09:00 +02:00
package jp.juggler.subwaytooter.itemviewholder
2021-06-22 10:31:51 +02:00
2021-06-24 04:31:34 +02:00
import android.text.Spannable
2021-06-22 10:31:51 +02:00
import android.text.SpannableStringBuilder
import android.view.View
import android.widget.ImageView
import jp.juggler.subwaytooter.ActMain
import jp.juggler.subwaytooter.R
2021-06-28 09:09:00 +02:00
import jp.juggler.subwaytooter.actmain.checkAutoCW
import jp.juggler.subwaytooter.api.entity.TootAccount
import jp.juggler.subwaytooter.api.entity.TootAttachment
import jp.juggler.subwaytooter.api.entity.TootAttachmentLike
import jp.juggler.subwaytooter.api.entity.TootAttachmentType
import jp.juggler.subwaytooter.api.entity.TootMessageHolder
import jp.juggler.subwaytooter.api.entity.TootNotification
import jp.juggler.subwaytooter.api.entity.TootPolls
import jp.juggler.subwaytooter.api.entity.TootPollsType
import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.api.entity.TootVisibility
import jp.juggler.subwaytooter.calcIconRound
2021-06-28 09:09:00 +02:00
import jp.juggler.subwaytooter.column.Column
import jp.juggler.subwaytooter.column.ColumnType
import jp.juggler.subwaytooter.column.isConversation
import jp.juggler.subwaytooter.defaultColorIcon
import jp.juggler.subwaytooter.pref.PrefB
import jp.juggler.subwaytooter.pref.PrefI
import jp.juggler.subwaytooter.stylerBoostAlpha
import jp.juggler.subwaytooter.table.daoContentWarning
import jp.juggler.subwaytooter.table.daoMediaShown
2021-06-22 10:31:51 +02:00
import jp.juggler.subwaytooter.util.OpenSticker
import jp.juggler.util.data.cast
import jp.juggler.util.data.ellipsizeDot3
import jp.juggler.util.data.notBlank
import jp.juggler.util.data.notEmpty
import jp.juggler.util.data.notZero
import jp.juggler.util.log.LogCategory
import jp.juggler.util.ui.attrColor
import jp.juggler.util.ui.setIconDrawableId
import jp.juggler.util.ui.textOrGone
import jp.juggler.util.ui.vg
2021-06-22 10:31:51 +02:00
import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.imageResource
2021-06-22 10:31:51 +02:00
import org.jetbrains.anko.textColor
private val log = LogCategory("ItemViewHolderShowStatus")
fun ItemViewHolder.showStatusOrReply(
item: TootStatus,
colorBgArg: Int = 0,
fadeText: Boolean = false,
) {
2021-06-22 10:31:51 +02:00
var colorBg = colorBgArg
val reply = item.reply
val inReplyToId = item.in_reply_to_id
val inReplyToAccountId = item.in_reply_to_account_id
when {
reply != null -> {
showReply(item.account, reply, R.drawable.ic_reply, R.string.reply_to)
if (colorBgArg == 0) colorBg = PrefI.ipEventBgColorMention.value
2021-06-22 10:31:51 +02:00
}
inReplyToId != null && inReplyToAccountId != null -> {
showReply(null, item, inReplyToAccountId)
if (colorBgArg == 0) colorBg = PrefI.ipEventBgColorMention.value
2021-06-22 10:31:51 +02:00
}
}
showStatus(item, colorBg, fadeText = fadeText)
2021-06-22 10:31:51 +02:00
}
fun ItemViewHolder.showStatus(
status: TootStatus,
colorBg: Int = 0,
fadeText: Boolean = false,
) {
2021-06-22 10:31:51 +02:00
val filteredWord = status.filteredWord
if (filteredWord != null) {
PrefI.ipEventBgColorFiltered.value.notZero()?.let {
viewRoot.backgroundColor = it
}
val text = StringBuilder().apply {
append(activity.getString(R.string.filtered))
if (PrefB.bpShowFilteredWord.value) {
append(" / $filteredWord")
}
if (PrefB.bpShowUsernameFilteredPost.value) {
val s = status.reblog ?: status
append(" / ${s.account.display_name} @${s.account.acct}")
}
}.toString()
showMessageHolder(TootMessageHolder(text))
2021-06-22 10:31:51 +02:00
return
}
this.statusShowing = status
llStatus.visibility = View.VISIBLE
if (status.conversation_main) {
PrefI.ipConversationMainTootBgColor.value.notZero()
2023-01-13 07:33:28 +01:00
?: activity.attrColor(R.attr.colorConversationMainTootBg)
2021-06-22 10:31:51 +02:00
} else {
2021-06-24 04:31:34 +02:00
colorBg.notZero() ?: when (status.bookmarked) {
true -> PrefI.ipEventBgColorBookmark.value
2021-06-24 04:31:34 +02:00
false -> 0
}.notZero() ?: when (status.getBackgroundColorType(accessInfo)) {
TootVisibility.UnlistedHome -> ItemViewHolder.toot_color_unlisted
TootVisibility.PrivateFollowers -> ItemViewHolder.toot_color_follower
TootVisibility.DirectSpecified -> ItemViewHolder.toot_color_direct_user
TootVisibility.DirectPrivate -> ItemViewHolder.toot_color_direct_me
// TODO add color setting for limited?
TootVisibility.Limited -> ItemViewHolder.toot_color_follower
else -> 0
}.notZero()
}?.let { viewRoot.backgroundColor = it }
2021-06-22 10:31:51 +02:00
showStatusTime(activity, tvTime, who = status.account, status = status)
val whoRef = status.accountRef
val who = whoRef.get()
this.statusAccount = whoRef
setAcct(tvAcct, accessInfo, who)
nameInvalidator.text = whoRef.decoded_display_name
2021-06-22 10:31:51 +02:00
ivAvatar.setImageUrl(
calcIconRound(ivAvatar.layoutParams),
2021-06-22 10:31:51 +02:00
accessInfo.supplyBaseUrl(who.avatar_static),
accessInfo.supplyBaseUrl(who.avatar)
)
showOpenSticker(who)
2021-06-24 04:31:34 +02:00
val modifiedContent = if (status.time_deleted_at > 0L) {
SpannableStringBuilder()
.append('(')
.append(
activity.getString(
R.string.deleted_at,
TootStatus.formatTime(activity, status.time_deleted_at, true)
)
)
.append(')')
} else {
showPoll(status) ?: status.decoded_content
2021-06-22 10:31:51 +02:00
}
// if( status.decoded_tags == null ){
// tvTags.setVisibility( View.GONE );
// }else{
// tvTags.setVisibility( View.VISIBLE );
// tvTags.setText( status.decoded_tags );
// }
val fadeAlpha = ActMain.eventFadeAlpha
if (fadeAlpha < 1f) {
val a = if (fadeText) fadeAlpha else 1f
tvMentions.alpha = a
tvContentWarning.alpha = a
tvContent.alpha = a
tvApplication.alpha = a
2021-12-04 09:55:28 +01:00
tvCardText.alpha = a
}
tvMentions.textOrGone = status.decoded_mentions
2021-06-22 10:31:51 +02:00
contentInvalidator.text = modifiedContent
2021-06-22 10:31:51 +02:00
2021-06-24 04:31:34 +02:00
activity.checkAutoCW(status, modifiedContent)
2021-06-22 10:31:51 +02:00
val r = status.auto_cw
tvContent.minLines = r?.originalLineCount ?: -1
showPreviewCard(status)
2021-06-24 04:31:34 +02:00
showSpoilerTextAndContent(status)
showAttachments(status)
makeReactionsView(status)
buttonsForStatus?.bind(status, (item as? TootNotification))
showApplicationAndLanguage(status)
}
// 投票の表示
// returns modified decoded_content or null
private fun ItemViewHolder.showPoll(status: TootStatus): Spannable? {
val enquete = status.enquete
return when {
enquete == null -> null
// フレニコの投票の結果表示は普通にテキストを表示するだけでよい
enquete.pollType == TootPollsType.FriendsNico && enquete.type != TootPolls.TYPE_ENQUETE -> null
else -> {
showEnqueteItems(status, enquete)
// アンケートの本文を使ってcontentを上書きする
enquete.decoded_question.notBlank()
}
}
}
private fun ItemViewHolder.showSpoilerTextAndContent(status: TootStatus) {
val r = status.auto_cw
2021-06-22 10:31:51 +02:00
val decodedSpoilerText = status.decoded_spoiler_text
val autoCwText = r?.decodedSpoilerText
2021-06-22 10:31:51 +02:00
when {
decodedSpoilerText.isNotEmpty() -> {
// 元データに含まれるContent Warning を使う
llContentWarning.visibility = View.VISIBLE
spoilerInvalidator.text = status.decoded_spoiler_text
val cwShown = daoContentWarning.isShown(status, accessInfo.expandCw)
2021-06-24 04:31:34 +02:00
setContentVisibility(cwShown)
2021-06-22 10:31:51 +02:00
}
autoCwText != null -> {
2021-06-22 10:31:51 +02:00
// 自動CW
llContentWarning.visibility = View.VISIBLE
spoilerInvalidator.text = autoCwText
val cwShown = daoContentWarning.isShown(status, accessInfo.expandCw)
2021-06-24 04:31:34 +02:00
setContentVisibility(cwShown)
2021-06-22 10:31:51 +02:00
}
else -> {
// CWしない
llContentWarning.visibility = View.GONE
llContents.visibility = View.VISIBLE
}
}
2021-06-24 04:31:34 +02:00
}
2021-06-22 10:31:51 +02:00
2021-06-24 06:49:27 +02:00
// 予約投稿でも使う
fun ItemViewHolder.setContentVisibility(shown: Boolean) {
2021-06-24 04:31:34 +02:00
llContents.visibility = if (shown) View.VISIBLE else View.GONE
btnContentWarning.contentDescription =
activity.getString(if (shown) R.string.hide else R.string.show)
btnContentWarning.imageResource =
if (shown) R.drawable.outline_compress_24 else R.drawable.outline_expand_24
2021-06-24 04:31:34 +02:00
statusShowing?.let { status ->
val r = status.auto_cw
tvContent.minLines = r?.originalLineCount ?: -1
val autoCwText = r?.decodedSpoilerText
if (autoCwText != null) {
2021-06-24 04:31:34 +02:00
// 自動CWの場合はContentWarningのテキストを切り替える
spoilerInvalidator.text =
if (shown) activity.getString(R.string.auto_cw_prefix) else autoCwText
2021-06-22 10:31:51 +02:00
}
}
2021-06-24 04:31:34 +02:00
}
2021-06-22 10:31:51 +02:00
2021-06-24 04:31:34 +02:00
private fun ItemViewHolder.showApplicationAndLanguage(status: TootStatus) {
2021-06-22 10:31:51 +02:00
var sb: StringBuilder? = null
fun prepareSb(): StringBuilder =
sb?.append(", ") ?: StringBuilder().also { sb = it }
val application = status.application
if (application != null &&
(column.isConversation || PrefB.bpShowAppName.value)
2021-06-22 10:31:51 +02:00
) {
prepareSb().append(activity.getString(R.string.application_is, application.name ?: ""))
}
val language = status.language
if (language != null &&
(column.isConversation || PrefB.bpShowLanguage.value)
2021-06-22 10:31:51 +02:00
) {
prepareSb().append(activity.getString(R.string.language_is, language))
}
tvApplication.vg(sb != null)?.text = sb
}
2021-06-24 04:31:34 +02:00
private fun ItemViewHolder.showOpenSticker(who: TootAccount) {
2021-06-22 10:31:51 +02:00
try {
if (!Column.showOpenSticker) return
val host = who.apDomain
// LTLでホスト名が同じならTickerを表示しない
when (column.type) {
ColumnType.LOCAL, ColumnType.LOCAL_AROUND -> {
if (host == accessInfo.apDomain) return
}
else -> Unit
2021-06-22 10:31:51 +02:00
}
val item = OpenSticker.lastList[host.ascii] ?: return
tvOpenSticker.text = item.name
tvOpenSticker.textColor = item.fontColor
val density = activity.density
val lp = ivOpenSticker.layoutParams
lp.height = (density * 16f + 0.5f).toInt()
lp.width = (density * item.imageWidth + 0.5f).toInt()
ivOpenSticker.layoutParams = lp
ivOpenSticker.setImageUrl(0f, item.favicon)
2021-06-22 10:31:51 +02:00
val colorBg = item.bgColor
when (colorBg.size) {
1 -> {
val c = colorBg.first()
tvOpenSticker.setBackgroundColor(c)
ivOpenSticker.setBackgroundColor(c)
}
else -> {
ivOpenSticker.setBackgroundColor(colorBg.last())
tvOpenSticker.background = colorBg.getGradation()
}
}
llOpenSticker.visibility = View.VISIBLE
llOpenSticker.requestLayout()
} catch (ex: Throwable) {
log.e(ex, "showOpenSticker failed.")
2021-06-22 10:31:51 +02:00
}
}
2021-06-24 04:31:34 +02:00
private fun ItemViewHolder.showAttachments(status: TootStatus) {
val mediaAttachments = status.media_attachments
if (mediaAttachments.isNullOrEmpty()) {
2021-06-24 04:31:34 +02:00
flMedia.visibility = View.GONE
llMedia.visibility = View.GONE
btnShowMedia.visibility = View.GONE
} else {
flMedia.visibility = View.VISIBLE
tvMediaCount.vg(mediaAttachments.size > 4)?.let {
it.text = activity.getString(R.string.media_count, mediaAttachments.size)
}
2021-06-24 04:31:34 +02:00
// hide sensitive media
val defaultShown = when {
column.hideMediaDefault -> false
accessInfo.dontHideNsfw -> true
2021-06-24 04:31:34 +02:00
else -> !status.sensitive
2021-06-22 10:31:51 +02:00
}
val isShown = daoMediaShown.isShown(status, defaultShown)
2021-06-24 04:31:34 +02:00
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)
2021-06-24 04:31:34 +02:00
}
btnShowMedia.blurhash = mediaAttachments.firstOrNull()
?.cast<TootAttachment>()
?.blurhash
2021-06-24 04:31:34 +02:00
setIconDrawableId(
activity,
btnHideMedia,
R.drawable.ic_close,
color = colorTextContent,
alphaMultiplier = stylerBoostAlpha
2021-06-24 04:31:34 +02:00
)
2021-06-22 10:31:51 +02:00
}
}
2023-07-20 04:40:12 +02:00
val reUrlGif by lazy {
2023-07-18 19:38:18 +02:00
"""\.gif(?:\z|\?|#)"""
.toRegex(RegexOption.IGNORE_CASE)
}
2023-07-17 02:37:48 +02:00
2021-06-22 10:31:51 +02:00
fun ItemViewHolder.setMedia(
mediaAttachments: ArrayList<TootAttachmentLike>,
idx: Int,
) {
val iv = ivMediaThumbnails[idx]
2021-06-22 10:31:51 +02:00
val ta = if (idx < mediaAttachments.size) mediaAttachments[idx] else null
if (ta == null) {
iv.visibility = View.GONE
return
}
iv.visibility = View.VISIBLE
iv.setFocusPoint(ta.focusX, ta.focusY)
if (PrefB.bpDontCropMediaThumb.value) {
2021-06-22 10:31:51 +02:00
iv.scaleType = ImageView.ScaleType.FIT_CENTER
} else {
iv.setScaleTypeForMedia()
}
val showUrl: Boolean
when (ta.type) {
TootAttachmentType.Audio -> {
iv.setMediaType(0)
iv.setDefaultImage(defaultColorIcon(activity, R.drawable.wide_music))
iv.setImageUrl(0f, ta.urlForThumbnail())
2021-06-22 10:31:51 +02:00
showUrl = true
}
TootAttachmentType.Unknown -> {
iv.setMediaType(0)
iv.setDefaultImage(defaultColorIcon(activity, R.drawable.wide_question))
2023-07-17 02:37:48 +02:00
if (ta is TootAttachment &&
reUrlGif.containsMatchIn(ta.remote_url ?: "") &&
PrefB.bpImageAnimationEnable.value
) {
val url = ta.remote_url!!
iv.setImageUrl(0f, url, url)
} else {
iv.setImageUrl(0f, null)
}
2021-06-22 10:31:51 +02:00
showUrl = true
}
2023-07-17 02:37:48 +02:00
else -> if (ta is TootAttachment &&
reUrlGif.containsMatchIn(ta.remote_url ?: "") &&
PrefB.bpImageAnimationEnable.value
) {
iv.setMediaType(0)
iv.setDefaultImage(null)
val url = ta.remote_url!!
iv.setImageUrl(0f, url, url)
showUrl = false
} else when (val urlThumbnail = ta.urlForThumbnail()) {
2021-06-22 10:31:51 +02:00
null, "" -> {
iv.setMediaType(0)
iv.setDefaultImage(defaultColorIcon(activity, R.drawable.wide_question))
iv.setImageUrl(0f, null)
2021-06-22 10:31:51 +02:00
showUrl = true
}
else -> {
iv.setMediaType(
when (ta.type) {
TootAttachmentType.Video -> R.drawable.media_type_video
TootAttachmentType.GIFV -> R.drawable.media_type_gifv
else -> 0
}
)
iv.setDefaultImage(null)
iv.setImageUrl(
0f,
accessInfo.supplyBaseUrl(urlThumbnail),
accessInfo.supplyBaseUrl(urlThumbnail)
)
showUrl = false
}
}
}
val desc = ta.description.notEmpty()
?: if (!showUrl) null else ta.urlForDescription.notEmpty()
tvMediaDescriptions[idx].vg(
!desc.isNullOrBlank() && column.showMediaDescription
)?.let {
2021-06-22 10:31:51 +02:00
// val lp = LinearLayout.LayoutParams(
// LinearLayout.LayoutParams.MATCH_PARENT,
// LinearLayout.LayoutParams.WRAP_CONTENT
// )
// lp.topMargin = (0.5f + activity.density * 3f).toInt()
//
// val tv = MyTextView(activity)
// tv.layoutParams = lp
// //
// tv.movementMethod = MyLinkMovementMethod
// if(! activity.timeline_font_size_sp.isNaN()) {
// tv.textSize = activity.timeline_font_size_sp
// }
// tv.setTextColor(content_color)
it.setTag(R.id.text, desc)
it.text = activity.getString(
R.string.media_description,
idx + 1,
desc?.ellipsizeDot3(140)
)
2021-06-22 10:31:51 +02:00
}
}