Fedibirdの横長絵文字を表示するが、表示形式の判別が仮組み状態
This commit is contained in:
parent
8a307bcfed
commit
577bc3fae5
|
@ -31,6 +31,7 @@ import jp.juggler.subwaytooter.dialog.actionsDialog
|
|||
import jp.juggler.subwaytooter.notification.*
|
||||
import jp.juggler.subwaytooter.push.PushBase
|
||||
import jp.juggler.subwaytooter.push.pushRepo
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.daoAcctColor
|
||||
import jp.juggler.subwaytooter.table.daoSavedAccount
|
||||
|
@ -145,8 +146,8 @@ class ActAccountSetting : AppCompatActivity(),
|
|||
private var loadingBusy = false
|
||||
private var profileBusy = false
|
||||
|
||||
private lateinit var listEtFieldName: List<EditText>
|
||||
private lateinit var listEtFieldValue: List<EditText>
|
||||
// private lateinit var listEtFieldName: List<EditText>
|
||||
// private lateinit var listEtFieldValue: List<EditText>
|
||||
private lateinit var listFieldNameInvalidator: List<NetworkEmojiInvalidator>
|
||||
private lateinit var listFieldValueInvalidator: List<NetworkEmojiInvalidator>
|
||||
private lateinit var btnFields: View
|
||||
|
@ -334,34 +335,31 @@ class ActAccountSetting : AppCompatActivity(),
|
|||
setDropDownViewResource(R.layout.lv_spinner_dropdown)
|
||||
}
|
||||
|
||||
listEtFieldName = intArrayOf(
|
||||
listFieldNameInvalidator = intArrayOf(
|
||||
R.id.etFieldName1,
|
||||
R.id.etFieldName2,
|
||||
R.id.etFieldName3,
|
||||
R.id.etFieldName4
|
||||
).map { findViewById(it) }
|
||||
).map {
|
||||
NetworkEmojiInvalidator(handler, findViewById<EditText>(it))
|
||||
|
||||
listEtFieldValue = intArrayOf(
|
||||
}
|
||||
listFieldValueInvalidator = intArrayOf(
|
||||
R.id.etFieldValue1,
|
||||
R.id.etFieldValue2,
|
||||
R.id.etFieldValue3,
|
||||
R.id.etFieldValue4
|
||||
).map { findViewById(it) }
|
||||
).map {
|
||||
NetworkEmojiInvalidator(handler, findViewById<EditText>(it))
|
||||
}
|
||||
|
||||
// btnNotificationStyleEditReply.vg(PrefB.bpSeparateReplyNotificationGroup.value)
|
||||
|
||||
// invalidaterがないと描画できないので
|
||||
nameInvalidator = NetworkEmojiInvalidator(handler, etDisplayName)
|
||||
noteInvalidator = NetworkEmojiInvalidator(handler, etNote)
|
||||
defaultTextInvalidator = NetworkEmojiInvalidator(handler, etDefaultText)
|
||||
|
||||
listFieldNameInvalidator = listEtFieldName.map {
|
||||
NetworkEmojiInvalidator(handler, it)
|
||||
}
|
||||
|
||||
listFieldValueInvalidator = listEtFieldValue.map {
|
||||
NetworkEmojiInvalidator(handler, it)
|
||||
}
|
||||
|
||||
val watcher1 = simpleTextWatcher {
|
||||
saveUIToData()
|
||||
}
|
||||
|
@ -433,7 +431,7 @@ class ActAccountSetting : AppCompatActivity(),
|
|||
swNotificationPullEnabled.isChecked = a.notificationPullEnable
|
||||
swNotificationPushEnabled.isChecked = a.notificationPushEnable
|
||||
|
||||
etDefaultText.setText(a.defaultText)
|
||||
defaultTextInvalidator.text = a.defaultText
|
||||
etMaxTootChars.setText(a.maxTootChars.toString())
|
||||
|
||||
val ti = TootInstance.getCached(a)
|
||||
|
@ -884,8 +882,8 @@ class ActAccountSetting : AppCompatActivity(),
|
|||
ivProfileAvatar.setErrorImage(defaultColorIcon(this@ActAccountSetting, questionId))
|
||||
ivProfileAvatar.setDefaultImage(defaultColorIcon(this@ActAccountSetting, questionId))
|
||||
|
||||
etDisplayName.setText(loadingText)
|
||||
etNote.setText(loadingText)
|
||||
nameInvalidator.text = loadingText
|
||||
noteInvalidator.text = loadingText
|
||||
|
||||
// 初期状態では編集不可能
|
||||
arrayOf(
|
||||
|
@ -898,13 +896,15 @@ class ActAccountSetting : AppCompatActivity(),
|
|||
cbLocked,
|
||||
).forEach { it.isEnabledAlpha = false }
|
||||
|
||||
for (et in listEtFieldName) {
|
||||
et.setText(loadingText)
|
||||
et.isEnabledAlpha = false
|
||||
|
||||
for (i in listFieldNameInvalidator) {
|
||||
i.text = loadingText
|
||||
i.view.isEnabledAlpha = false
|
||||
}
|
||||
for (et in listEtFieldValue) {
|
||||
et.setText(loadingText)
|
||||
et.isEnabledAlpha = false
|
||||
|
||||
for (i in listFieldValueInvalidator) {
|
||||
i.text = loadingText
|
||||
i.view.isEnabledAlpha = false
|
||||
}
|
||||
|
||||
// 疑似アカウントなら編集不可のまま
|
||||
|
@ -968,12 +968,12 @@ class ActAccountSetting : AppCompatActivity(),
|
|||
emojiMapProfile = src.profile_emojis,
|
||||
emojiMapCustom = src.custom_emojis,
|
||||
authorDomain = account,
|
||||
emojiSizeMode = account.emojiSizeMode(),
|
||||
)
|
||||
|
||||
val displayName = src.display_name
|
||||
val name = decodeOptions.decodeEmoji(displayName)
|
||||
views.etDisplayName.setText(name)
|
||||
nameInvalidator.register(name)
|
||||
nameInvalidator.text = name
|
||||
|
||||
val noteString = src.source?.note ?: src.note
|
||||
val noteSpannable = when {
|
||||
|
@ -986,8 +986,7 @@ class ActAccountSetting : AppCompatActivity(),
|
|||
}
|
||||
}
|
||||
|
||||
views.etNote.setText(noteSpannable)
|
||||
noteInvalidator.register(noteSpannable)
|
||||
noteInvalidator.text = noteSpannable
|
||||
|
||||
views.cbLocked.isChecked = src.locked
|
||||
|
||||
|
@ -1006,75 +1005,55 @@ class ActAccountSetting : AppCompatActivity(),
|
|||
|
||||
if (src.source?.fields != null) {
|
||||
val fields = src.source.fields
|
||||
listEtFieldName.forEachIndexed { i, et ->
|
||||
val handler = et.handler // may null
|
||||
if (handler != null) {
|
||||
// いつからかfields name にもカスタム絵文字が使えるようになった
|
||||
// https://github.com/tootsuite/mastodon/pull/11350
|
||||
// しかし
|
||||
val text = decodeOptions.decodeEmoji(
|
||||
when {
|
||||
i >= fields.size -> ""
|
||||
else -> fields[i].name
|
||||
}
|
||||
)
|
||||
et.setText(text)
|
||||
et.isEnabledAlpha = true
|
||||
val invalidator = NetworkEmojiInvalidator(handler, et)
|
||||
invalidator.register(text)
|
||||
}
|
||||
listFieldNameInvalidator.forEachIndexed { i, et ->
|
||||
// いつからかfields name にもカスタム絵文字が使えるようになった
|
||||
// https://github.com/tootsuite/mastodon/pull/11350
|
||||
// しかし
|
||||
val text = decodeOptions.decodeEmoji(
|
||||
when {
|
||||
i >= fields.size -> ""
|
||||
else -> fields[i].name
|
||||
}
|
||||
)
|
||||
et.text = text
|
||||
et.view.isEnabledAlpha = true
|
||||
}
|
||||
|
||||
listEtFieldValue.forEachIndexed { i, et ->
|
||||
val handler = et.handler // may null
|
||||
if (handler != null) {
|
||||
val text = decodeOptions.decodeEmoji(
|
||||
when {
|
||||
i >= fields.size -> ""
|
||||
else -> fields[i].value
|
||||
}
|
||||
)
|
||||
et.setText(text)
|
||||
et.isEnabledAlpha = true
|
||||
val invalidator = NetworkEmojiInvalidator(handler, et)
|
||||
invalidator.register(text)
|
||||
}
|
||||
listFieldValueInvalidator.forEachIndexed { i, et ->
|
||||
val text = decodeOptions.decodeEmoji(
|
||||
when {
|
||||
i >= fields.size -> ""
|
||||
else -> fields[i].value
|
||||
}
|
||||
)
|
||||
et.text = text
|
||||
et.view.isEnabledAlpha = true
|
||||
}
|
||||
} else {
|
||||
val fields = src.fields
|
||||
|
||||
listEtFieldName.forEachIndexed { i, et ->
|
||||
val handler = et.handler // may null
|
||||
if (handler != null) {
|
||||
// いつからかfields name にもカスタム絵文字が使えるようになった
|
||||
// https://github.com/tootsuite/mastodon/pull/11350
|
||||
val text = decodeOptions.decodeEmoji(
|
||||
when {
|
||||
fields == null || i >= fields.size -> ""
|
||||
else -> fields[i].name
|
||||
}
|
||||
)
|
||||
et.setText(text)
|
||||
et.isEnabledAlpha = true
|
||||
val invalidator = NetworkEmojiInvalidator(handler, et)
|
||||
invalidator.register(text)
|
||||
}
|
||||
listFieldNameInvalidator.forEachIndexed { i, et ->
|
||||
// いつからかfields name にもカスタム絵文字が使えるようになった
|
||||
// https://github.com/tootsuite/mastodon/pull/11350
|
||||
val text = decodeOptions.decodeEmoji(
|
||||
when {
|
||||
fields == null || i >= fields.size -> ""
|
||||
else -> fields[i].name
|
||||
}
|
||||
)
|
||||
et.text = text
|
||||
et.view.isEnabledAlpha = true
|
||||
}
|
||||
|
||||
listEtFieldValue.forEachIndexed { i, et ->
|
||||
val handler = et.handler // may null
|
||||
if (handler != null) {
|
||||
val text = decodeOptions.decodeHTML(
|
||||
when {
|
||||
fields == null || i >= fields.size -> ""
|
||||
else -> fields[i].value
|
||||
}
|
||||
)
|
||||
et.text = text
|
||||
et.isEnabledAlpha = true
|
||||
val invalidator = NetworkEmojiInvalidator(handler, et)
|
||||
invalidator.register(text)
|
||||
}
|
||||
listFieldValueInvalidator.forEachIndexed { i, et ->
|
||||
val text = decodeOptions.decodeHTML(
|
||||
when {
|
||||
fields == null || i >= fields.size -> ""
|
||||
else -> fields[i].value
|
||||
}
|
||||
)
|
||||
et.text = text
|
||||
et.view.isEnabledAlpha = true
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
|
@ -1314,9 +1293,9 @@ class ActAccountSetting : AppCompatActivity(),
|
|||
private fun sendFields(bConfirmed: Boolean = false) {
|
||||
val args = ArrayList<Pair<String, String>>()
|
||||
var lengthLongest = -1
|
||||
for (i in listEtFieldName.indices) {
|
||||
val k = listEtFieldName[i].text.toString().trim()
|
||||
val v = listEtFieldValue[i].text.toString().trim()
|
||||
for (i in listFieldNameInvalidator.indices) {
|
||||
val k = listFieldNameInvalidator[i].text.toString().trim()
|
||||
val v = listFieldValueInvalidator[i].text.toString().trim()
|
||||
args.add(Pair("fields_attributes[$i][name]", k))
|
||||
args.add(Pair("fields_attributes[$i][value]", v))
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import jp.juggler.subwaytooter.databinding.ActGlideTestBinding
|
||||
import jp.juggler.subwaytooter.databinding.LvGlideTestBinding
|
||||
import jp.juggler.subwaytooter.span.EmojiSizeMode
|
||||
import jp.juggler.subwaytooter.span.NetworkEmojiSpan
|
||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||
import jp.juggler.util.coroutine.AppDispatchers
|
||||
|
@ -98,13 +99,13 @@ class ActGlideTest : AppCompatActivity() {
|
|||
val span = NetworkEmojiSpan(
|
||||
url = item.url,
|
||||
scale = 2f,
|
||||
sizeMode = EmojiSizeMode.Square,
|
||||
)
|
||||
setSpan(span, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
append(" ")
|
||||
append(item.name)
|
||||
}
|
||||
nameInvalidator.register(text)
|
||||
views.tvName.text = text
|
||||
nameInvalidator.text = text
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import jp.juggler.subwaytooter.dialog.launchEmojiPicker
|
|||
import jp.juggler.subwaytooter.dialog.pickAccount
|
||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.accountListCanReaction
|
||||
import jp.juggler.subwaytooter.table.daoAcctColor
|
||||
|
@ -120,7 +121,8 @@ fun ActMain.reactionAdd(
|
|||
accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
enlargeCustomEmoji = 1.5f
|
||||
enlargeCustomEmoji = 1.5f,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
)
|
||||
val emojiSpan = TootReaction.toSpannableStringBuilder(options, code, urlArg)
|
||||
confirm(
|
||||
|
@ -218,7 +220,8 @@ fun ActMain.reactionRemove(
|
|||
accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
enlargeCustomEmoji = 1.5f
|
||||
enlargeCustomEmoji = 1.5f,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
)
|
||||
val emojiSpan = reaction.toSpannableStringBuilder(options, status)
|
||||
confirm(R.string.reaction_remove_confirm, emojiSpan)
|
||||
|
@ -328,7 +331,8 @@ private fun ActMain.reactionWithoutUi(
|
|||
accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
enlargeCustomEmoji = 1.5f
|
||||
enlargeCustomEmoji = 1.5f,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
)
|
||||
val emojiSpan = TootReaction.toSpannableStringBuilder(options, reactionCode, reactionImage)
|
||||
val isCustomEmoji = TootReaction.isCustomEmoji(reactionCode)
|
||||
|
|
|
@ -7,18 +7,31 @@ import jp.juggler.subwaytooter.ActMain
|
|||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.subwaytooter.pref.PrefS
|
||||
import jp.juggler.subwaytooter.span.NetworkEmojiSpan
|
||||
import jp.juggler.subwaytooter.view.MyTextView
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
// AutoCWの基準幅を計算する
|
||||
fun ActMain.resizeAutoCW(columnW: Int) {
|
||||
/**
|
||||
* 各カラムの各投稿のコンテンツ部分の幅を覚える
|
||||
*/
|
||||
fun ActMain.saveContentTextWidth(columnW: Int) {
|
||||
val lvPad = (0.5f + 12 * density).toInt()
|
||||
val iconWidth = avatarIconSize
|
||||
val iconEnd = (0.5f + 4 * density).toInt()
|
||||
|
||||
val contentTextWidth = columnW - lvPad * 2 - iconWidth - iconEnd
|
||||
|
||||
// CW部分の絵文字とか、リアクションなら数字と枠を避けたサイズとか
|
||||
val maxEmojiWidth = contentTextWidth - (64f * density+ 0.5f )
|
||||
|
||||
// NetworkEmojiSpanに覚える
|
||||
NetworkEmojiSpan.maxEmojiWidth = maxEmojiWidth
|
||||
|
||||
// 自動CW用の値に覚える
|
||||
val sv = PrefS.spAutoCWLines.value
|
||||
nAutoCwLines = sv.toIntOrNull() ?: -1
|
||||
if (nAutoCwLines > 0) {
|
||||
val lvPad = (0.5f + 12 * density).toInt()
|
||||
val iconWidth = avatarIconSize
|
||||
val iconEnd = (0.5f + 4 * density).toInt()
|
||||
nAutoCwCellWidth = columnW - lvPad * 2 - iconWidth - iconEnd
|
||||
nAutoCwCellWidth = contentTextWidth
|
||||
}
|
||||
// この後各カラムは再描画される
|
||||
}
|
||||
|
|
|
@ -445,7 +445,7 @@ fun ActMain.resizeColumnWidth(views: ActMainTabletViews) {
|
|||
views.tabletPagerAdapter.columnWidth = columnW // dividerの幅を含まない
|
||||
// env.tablet_snap_helper.columnWidth = column_w //使われていない
|
||||
|
||||
resizeAutoCW(columnW) // dividerの幅を含まない
|
||||
saveContentTextWidth(columnW) // dividerの幅を含まない
|
||||
|
||||
// 並べ直す
|
||||
views.tabletPagerAdapter.notifyDataSetChanged()
|
||||
|
|
|
@ -37,7 +37,7 @@ fun ActMain.initPhoneTablet() {
|
|||
phoneViews = ActMainPhoneViews(this).apply {
|
||||
initUI(tmpPhonePager)
|
||||
}
|
||||
resizeAutoCW(sw)
|
||||
saveContentTextWidth(sw)
|
||||
} else {
|
||||
tmpPhonePager.visibility = View.GONE
|
||||
tabletViews = ActMainTabletViews(this).apply {
|
||||
|
|
|
@ -10,6 +10,7 @@ import jp.juggler.subwaytooter.api.TootParser
|
|||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.api.entity.TootAttachment.Companion.tootAttachmentJson
|
||||
import jp.juggler.subwaytooter.dialog.DlgDraftPicker
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.daoPostDraft
|
||||
import jp.juggler.subwaytooter.table.daoSavedAccount
|
||||
|
|
|
@ -13,6 +13,7 @@ import jp.juggler.subwaytooter.api.entity.unknownHostAndDomain
|
|||
import jp.juggler.subwaytooter.dialog.DlgConfirm.confirm
|
||||
import jp.juggler.subwaytooter.dialog.actionsDialog
|
||||
import jp.juggler.subwaytooter.pref.PrefB
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.daoPostDraft
|
||||
import jp.juggler.subwaytooter.table.daoSavedAccount
|
||||
|
@ -39,6 +40,7 @@ suspend fun ActPost.appendContentText(
|
|||
context = this,
|
||||
decodeEmoji = true,
|
||||
authorDomain = account ?: unknownHostAndDomain,
|
||||
emojiSizeMode = account.emojiSizeMode(),
|
||||
).decodeEmoji(src)
|
||||
if (svEmoji.isEmpty()) return
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import jp.juggler.subwaytooter.api.entity.TootVisibility
|
|||
import jp.juggler.subwaytooter.api.runApiTask
|
||||
import jp.juggler.subwaytooter.api.syncStatus
|
||||
import jp.juggler.subwaytooter.calcIconRound
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.util.coroutine.launchMain
|
||||
|
@ -38,6 +39,7 @@ suspend fun ActPost.showReplyTo() {
|
|||
linkHelper = account,
|
||||
short = true,
|
||||
decodeEmoji = true,
|
||||
emojiSizeMode = account.emojiSizeMode(),
|
||||
).decodeHTML(states.inReplyToText)
|
||||
views.ivReply.setImageUrl(
|
||||
calcIconRound(views.ivReply.layoutParams),
|
||||
|
|
|
@ -15,6 +15,7 @@ import jp.juggler.subwaytooter.emoji.EmojiBase
|
|||
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
||||
import jp.juggler.subwaytooter.pref.PrefB
|
||||
import jp.juggler.subwaytooter.span.NetworkEmojiSpan
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.*
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.EmojiDecoder
|
||||
|
@ -274,7 +275,7 @@ class CompletionHelper(
|
|||
val sb = SpannableStringBuilder()
|
||||
sb.append(' ')
|
||||
sb.setSpan(
|
||||
NetworkEmojiSpan(item.url),
|
||||
NetworkEmojiSpan(item.url, sizeMode = accessInfo.emojiSizeMode()),
|
||||
0,
|
||||
sb.length,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
|
@ -407,12 +408,12 @@ class CompletionHelper(
|
|||
// et.setCustomSelectionActionModeCallback( action_mode_callback );
|
||||
}
|
||||
|
||||
private suspend fun SpannableStringBuilder.appendEmoji(
|
||||
private fun SpannableStringBuilder.appendEmoji(
|
||||
emoji: EmojiBase,
|
||||
bInstanceHasCustomEmoji: Boolean,
|
||||
) = appendEmoji(bInstanceHasCustomEmoji, emoji)
|
||||
|
||||
private suspend fun SpannableStringBuilder.appendEmoji(
|
||||
private fun SpannableStringBuilder.appendEmoji(
|
||||
bInstanceHasCustomEmoji: Boolean,
|
||||
emoji: EmojiBase,
|
||||
): SpannableStringBuilder {
|
||||
|
|
|
@ -4,6 +4,9 @@ import android.content.Context
|
|||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.api.entity.TootAccount.Companion.tootAccount
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus.Companion.tootStatus
|
||||
import jp.juggler.subwaytooter.span.EmojiSizeMode
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
import jp.juggler.subwaytooter.util.LinkHelper
|
||||
import jp.juggler.util.data.JsonArray
|
||||
|
@ -67,4 +70,7 @@ class TootParser(
|
|||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
val emojiSizeMode: EmojiSizeMode
|
||||
get()= (linkHelper as? SavedAccount).emojiSizeMode()
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package jp.juggler.subwaytooter.api.entity
|
|||
import android.content.Context
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.widget.TextView
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.MisskeyAccountDetailMap
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
|
@ -11,6 +10,7 @@ import jp.juggler.subwaytooter.api.entity.TootAccountRef.Companion.tootAccountRe
|
|||
import jp.juggler.subwaytooter.api.entity.TootStatus.Companion.tootStatus
|
||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||
import jp.juggler.subwaytooter.pref.PrefB
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.daoUserRelation
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
|
@ -167,7 +167,7 @@ open class TootAccount(
|
|||
context,
|
||||
emojiMapProfile = profile_emojis,
|
||||
emojiMapCustom = custom_emojis,
|
||||
authorDomain = this
|
||||
authorDomain = this,
|
||||
).decodeEmoji(sv)
|
||||
}
|
||||
|
||||
|
@ -182,7 +182,7 @@ open class TootAccount(
|
|||
context,
|
||||
emojiMapProfile = profile_emojis,
|
||||
emojiMapCustom = custom_emojis,
|
||||
authorDomain = this
|
||||
authorDomain = this,
|
||||
).decodeEmoji(sv)
|
||||
}
|
||||
|
||||
|
@ -219,12 +219,11 @@ open class TootAccount(
|
|||
|
||||
fun setAccountExtra(
|
||||
accessInfo: SavedAccount,
|
||||
tv: TextView,
|
||||
invalidator: NetworkEmojiInvalidator?,
|
||||
invalidator: NetworkEmojiInvalidator,
|
||||
fromProfileHeader: Boolean = false,
|
||||
suggestionSource: String? = null,
|
||||
): SpannableStringBuilder? {
|
||||
val context = tv.context
|
||||
val context = invalidator.view.context
|
||||
|
||||
var sb: SpannableStringBuilder? = null
|
||||
fun prepareSb() = sb?.apply { append('\n') } ?: SpannableStringBuilder().also { sb = it }
|
||||
|
@ -281,6 +280,7 @@ open class TootAccount(
|
|||
emojiMapCustom = custom_emojis,
|
||||
unwrapEmojiImageTag = true,
|
||||
authorDomain = this,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
).decodeHTML(note)
|
||||
.replaceAllEx(reNoteLineFeed, " ")
|
||||
.trimEx()
|
||||
|
@ -296,13 +296,10 @@ open class TootAccount(
|
|||
}
|
||||
}
|
||||
|
||||
tv.vg(sb != null)
|
||||
?.apply {
|
||||
text = sb
|
||||
movementMethod = MyLinkMovementMethod
|
||||
invalidator?.register(sb)
|
||||
}
|
||||
?: invalidator?.clear()
|
||||
invalidator.view.vg(sb != null)?.apply {
|
||||
invalidator.text = sb!!
|
||||
movementMethod = MyLinkMovementMethod
|
||||
} ?: invalidator.clear()
|
||||
|
||||
return sb
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ class TootAccountRef private constructor(
|
|||
emojiMapCustom = account.custom_emojis,
|
||||
unwrapEmojiImageTag = true,
|
||||
authorDomain = account,
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
).decodeHTML(account.note),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package jp.juggler.subwaytooter.api.entity
|
|||
import android.text.Spannable
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.util.data.JsonObject
|
||||
import jp.juggler.util.data.notEmpty
|
||||
|
@ -55,6 +56,7 @@ class TootAnnouncement(
|
|||
// attachmentList = media_attachments,
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = mentions,
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
)
|
||||
val content = src.string("content") ?: ""
|
||||
return TootAnnouncement(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package jp.juggler.subwaytooter.api.entity
|
||||
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.util.data.JsonObject
|
||||
import jp.juggler.util.data.filterNotEmpty
|
||||
|
@ -58,6 +59,7 @@ class TootCard(
|
|||
context = parser.context,
|
||||
decodeEmoji = true,
|
||||
authorDomain = src.account,
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
).decodeHTML(src.content ?: "").toString()
|
||||
},
|
||||
image = src.media_attachments
|
||||
|
|
|
@ -126,7 +126,8 @@ class TootPolls(
|
|||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
mentions = status.mentions,
|
||||
authorDomain = status.account
|
||||
authorDomain = status.account,
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
).decodeHTML(question ?: "?"),
|
||||
)
|
||||
}
|
||||
|
@ -134,7 +135,7 @@ class TootPolls(
|
|||
TootPollsType.Mastodon -> {
|
||||
val question = status.content
|
||||
val items = parseChoiceListMastodon(
|
||||
parser.context,
|
||||
parser,
|
||||
status,
|
||||
src.jsonArray("options")?.objectList()
|
||||
)
|
||||
|
@ -186,7 +187,8 @@ class TootPolls(
|
|||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
mentions = status.mentions,
|
||||
authorDomain = status.account
|
||||
authorDomain = status.account,
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
).decodeHTML(question ?: "?"),
|
||||
pollId = EntityId.mayNull(src.string("id")),
|
||||
expired_at =
|
||||
|
@ -214,10 +216,11 @@ class TootPolls(
|
|||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
mentions = status.mentions,
|
||||
authorDomain = status.account
|
||||
authorDomain = status.account,
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
).decodeHTML(question ?: "?"),
|
||||
items = parseChoiceListFriendsNico(
|
||||
parser.context,
|
||||
parser,
|
||||
status,
|
||||
src.stringArrayList("items")
|
||||
),
|
||||
|
@ -232,7 +235,7 @@ class TootPolls(
|
|||
val expired_at =
|
||||
TootStatus.parseTime(src.string("endTime")).notZero() ?: Long.MAX_VALUE
|
||||
val items = parseChoiceListNotestock(
|
||||
parser.context,
|
||||
parser,
|
||||
status,
|
||||
srcArray?.objectList()
|
||||
)
|
||||
|
@ -281,6 +284,7 @@ class TootPolls(
|
|||
mentions = status.mentions,
|
||||
authorDomain = status.account,
|
||||
unwrapEmojiImageTag = true, // notestockはカスタム絵文字がimageタグになってる
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
).decodeHTML(question ?: "?"),
|
||||
pollId = EntityId.DEFAULT,
|
||||
expired = expired_at >= System.currentTimeMillis(),
|
||||
|
@ -313,7 +317,7 @@ class TootPolls(
|
|||
}
|
||||
|
||||
private fun parseChoiceListMastodon(
|
||||
context: Context,
|
||||
parser: TootParser,
|
||||
status: TootStatus,
|
||||
objectArray: List<JsonObject>?,
|
||||
): ArrayList<TootPollsChoice>? {
|
||||
|
@ -321,11 +325,13 @@ class TootPolls(
|
|||
val size = objectArray.size
|
||||
val items = ArrayList<TootPollsChoice>(size)
|
||||
val options = DecodeOptions(
|
||||
context,
|
||||
context = parser.context,
|
||||
linkHelper = parser.linkHelper,
|
||||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
decodeEmoji = true,
|
||||
authorDomain = status.account
|
||||
authorDomain = status.account,
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
)
|
||||
for (o in objectArray) {
|
||||
val text = reWhitespace
|
||||
|
@ -347,7 +353,7 @@ class TootPolls(
|
|||
}
|
||||
|
||||
private fun parseChoiceListNotestock(
|
||||
context: Context,
|
||||
parser: TootParser,
|
||||
status: TootStatus,
|
||||
objectArray: List<JsonObject>?,
|
||||
): ArrayList<TootPollsChoice>? {
|
||||
|
@ -355,11 +361,13 @@ class TootPolls(
|
|||
val size = objectArray.size
|
||||
val items = ArrayList<TootPollsChoice>(size)
|
||||
val options = DecodeOptions(
|
||||
context,
|
||||
context = parser.context,
|
||||
linkHelper = parser.linkHelper,
|
||||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
decodeEmoji = true,
|
||||
authorDomain = status.account
|
||||
authorDomain = status.account,
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
)
|
||||
for (o in objectArray) {
|
||||
val text = reWhitespace
|
||||
|
@ -381,7 +389,7 @@ class TootPolls(
|
|||
}
|
||||
|
||||
private fun parseChoiceListFriendsNico(
|
||||
context: Context,
|
||||
parser: TootParser,
|
||||
status: TootStatus,
|
||||
stringArray: ArrayList<String>?,
|
||||
): ArrayList<TootPollsChoice>? {
|
||||
|
@ -389,11 +397,13 @@ class TootPolls(
|
|||
val size = stringArray.size
|
||||
val items = ArrayList<TootPollsChoice>(size)
|
||||
val options = DecodeOptions(
|
||||
context,
|
||||
context = parser.context,
|
||||
linkHelper = parser.linkHelper,
|
||||
emojiMapCustom = status.custom_emojis,
|
||||
emojiMapProfile = status.profile_emojis,
|
||||
decodeEmoji = true,
|
||||
authorDomain = status.account
|
||||
authorDomain = status.account,
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
)
|
||||
for (i in 0 until size) {
|
||||
val text = reWhitespace
|
||||
|
|
|
@ -126,15 +126,22 @@ class TootReaction(
|
|||
fun encodeEmojiQuery(src: List<TootReaction>): String =
|
||||
JsonArray(src.map { it.jsonFedibird() } as Collection<JsonObject>).toString()
|
||||
|
||||
fun urlToSpan(options: DecodeOptions, code: String, url: String) =
|
||||
SpannableStringBuilder(code).apply {
|
||||
setSpan(
|
||||
NetworkEmojiSpan(url, scale = options.enlargeCustomEmoji),
|
||||
0,
|
||||
length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
fun urlToSpan(
|
||||
options: DecodeOptions,
|
||||
code: String,
|
||||
url: String,
|
||||
) = SpannableStringBuilder(code).apply {
|
||||
setSpan(
|
||||
NetworkEmojiSpan(
|
||||
url = url,
|
||||
scale = options.enlargeCustomEmoji,
|
||||
sizeMode = options.emojiSizeMode
|
||||
),
|
||||
0,
|
||||
length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
fun toSpannableStringBuilder(
|
||||
options: DecodeOptions,
|
||||
|
|
|
@ -618,7 +618,8 @@ class TootStatus(
|
|||
attachmentList = media_attachments,
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = null, // MisskeyはMFMをパースし終わるまでメンションが分からない
|
||||
authorDomain = accountRef.get()
|
||||
authorDomain = accountRef.get(),
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
)
|
||||
// ハイライト検出のためにDecodeOptionsを作り直す?
|
||||
val options2 = DecodeOptions(
|
||||
|
@ -631,7 +632,8 @@ class TootStatus(
|
|||
attachmentList = media_attachments,
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = null, // MisskeyはMFMをパースし終わるまでメンションが分からない
|
||||
authorDomain = accountRef.get()
|
||||
authorDomain = accountRef.get(),
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
)
|
||||
|
||||
val content = src.string("text")
|
||||
|
@ -834,6 +836,7 @@ class TootStatus(
|
|||
mentions = mentions,
|
||||
authorDomain = account,
|
||||
unwrapEmojiImageTag = true, // notestockはカスタム絵文字がimageタグになってる
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
)
|
||||
|
||||
// ハイライト検出のためにDecodeOptionsを作り直す?
|
||||
|
@ -845,6 +848,7 @@ class TootStatus(
|
|||
mentions = mentions,
|
||||
authorDomain = account,
|
||||
unwrapEmojiImageTag = true, // notestockはカスタム絵文字がimageタグになってる
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
)
|
||||
|
||||
val content = src.string("content")
|
||||
|
@ -971,7 +975,7 @@ class TootStatus(
|
|||
|
||||
val who = parser.account(src.jsonObject("account"))
|
||||
?: error("missing account")
|
||||
val accountRef = TootAccountRef.tootAccountRef(parser, who)
|
||||
val accountRef = tootAccountRef(parser, who)
|
||||
val account = accountRef.get()
|
||||
|
||||
val readerApDomain: Host?
|
||||
|
@ -1093,7 +1097,8 @@ class TootStatus(
|
|||
attachmentList = media_attachments,
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = mentions,
|
||||
authorDomain = account
|
||||
authorDomain = account,
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
)
|
||||
|
||||
val decoded_content = options1.decodeHTML(content)
|
||||
|
@ -1115,7 +1120,8 @@ class TootStatus(
|
|||
emojiMapProfile = profile_emojis,
|
||||
highlightTrie = parser.highlightTrie,
|
||||
mentions = mentions,
|
||||
authorDomain = account
|
||||
authorDomain = account,
|
||||
emojiSizeMode = parser.emojiSizeMode,
|
||||
)
|
||||
val decoded_spoiler_text = options2.decodeEmoji(spoiler_text)
|
||||
if (highlightSound == null) highlightSound = options2.highlightSound
|
||||
|
|
|
@ -26,6 +26,7 @@ import jp.juggler.subwaytooter.emoji.CustomEmoji
|
|||
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
||||
import jp.juggler.subwaytooter.pref.PrefB
|
||||
import jp.juggler.subwaytooter.span.NetworkEmojiSpan
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
import jp.juggler.util.coroutine.launchAndShowError
|
||||
import jp.juggler.util.coroutine.launchMain
|
||||
|
@ -225,9 +226,8 @@ private fun ColumnViewHolder.showAnnouncementContent(item: TootAnnouncement, con
|
|||
val sb = periods
|
||||
tvAnnouncementPeriod.vg(sb != null)?.text = sb
|
||||
tvAnnouncementContent.textColor = contentColor
|
||||
tvAnnouncementContent.text = item.decoded_content
|
||||
tvAnnouncementContent.tag = this
|
||||
announcementContentInvalidator.register(item.decoded_content)
|
||||
announcementContentInvalidator.text = item.decoded_content
|
||||
}
|
||||
|
||||
private fun ColumnViewHolder.showReactionBox(
|
||||
|
@ -333,7 +333,8 @@ private fun ColumnViewHolder.showReactions(
|
|||
column.accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
authorDomain = column.accessInfo
|
||||
authorDomain = column.accessInfo,
|
||||
emojiSizeMode = column.accessInfo.emojiSizeMode(),
|
||||
)
|
||||
|
||||
val actMain = activity
|
||||
|
@ -377,18 +378,16 @@ private fun ColumnViewHolder.showReactions(
|
|||
if (url == null) {
|
||||
btn.text = EmojiDecoder.decodeEmoji(options, "${reaction.name} ${reaction.count}")
|
||||
} else {
|
||||
btn.text = SpannableStringBuilder("${reaction.name} ${reaction.count}").also { sb ->
|
||||
val invalidator = NetworkEmojiInvalidator(actMain.handler, btn)
|
||||
invalidator.text = SpannableStringBuilder("${reaction.name} ${reaction.count}").also { sb ->
|
||||
sb.setSpan(
|
||||
NetworkEmojiSpan(url, scale = 1.5f),
|
||||
NetworkEmojiSpan(url, scale = 1.5f, sizeMode = options.emojiSizeMode),
|
||||
0,
|
||||
reaction.name.length,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
val invalidator =
|
||||
NetworkEmojiInvalidator(actMain.handler, btn)
|
||||
invalidator.register(sb)
|
||||
extraInvalidatorList.add(invalidator)
|
||||
}
|
||||
extraInvalidatorList.add(invalidator)
|
||||
}
|
||||
|
||||
btn.setOnClickListener {
|
||||
|
|
|
@ -10,6 +10,7 @@ import jp.juggler.subwaytooter.column.getContentColor
|
|||
import jp.juggler.subwaytooter.dialog.launchEmojiPicker
|
||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||
import jp.juggler.subwaytooter.emoji.UnicodeEmoji
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||
import jp.juggler.subwaytooter.util.minWidthCompat
|
||||
|
@ -65,7 +66,8 @@ fun ColumnViewHolder.updateReactionQueryView() {
|
|||
column.accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
enlargeCustomEmoji = 1.5f
|
||||
enlargeCustomEmoji = 1.5f,
|
||||
emojiSizeMode = column.accessInfo.emojiSizeMode(),
|
||||
)
|
||||
|
||||
val buttonHeight = ActMain.boostButtonSize
|
||||
|
@ -93,8 +95,6 @@ fun ColumnViewHolder.updateReactionQueryView() {
|
|||
setTextColor(contentColor)
|
||||
setPadding(paddingH, paddingV, paddingH, paddingV)
|
||||
|
||||
text = ssb
|
||||
|
||||
allCaps = false
|
||||
tag = reaction
|
||||
|
||||
|
@ -104,7 +104,7 @@ fun ColumnViewHolder.updateReactionQueryView() {
|
|||
}
|
||||
// カスタム絵文字の場合、アニメーション等のコールバックを処理する必要がある
|
||||
val invalidator = NetworkEmojiInvalidator(act.handler, this)
|
||||
invalidator.register(ssb)
|
||||
invalidator.text = ssb
|
||||
emojiQueryInvalidatorList.add(invalidator)
|
||||
}
|
||||
flEmoji.addView(b)
|
||||
|
|
|
@ -14,6 +14,7 @@ import jp.juggler.subwaytooter.api.entity.TootInstance
|
|||
import jp.juggler.subwaytooter.column.Column
|
||||
import jp.juggler.subwaytooter.column.ColumnType
|
||||
import jp.juggler.subwaytooter.databinding.LvHeaderInstanceBinding
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.openBrowser
|
||||
import jp.juggler.subwaytooter.util.openCustomTab
|
||||
|
@ -128,7 +129,8 @@ internal class ViewHolderHeaderInstance(
|
|||
activity,
|
||||
accessInfo,
|
||||
decodeEmoji = true,
|
||||
authorDomain = accessInfo
|
||||
authorDomain = accessInfo,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
)
|
||||
|
||||
tvShortDescription.text = options
|
||||
|
|
|
@ -26,10 +26,7 @@ import jp.juggler.subwaytooter.emoji.EmojiMap
|
|||
import jp.juggler.subwaytooter.itemviewholder.DlgContextMenu
|
||||
import jp.juggler.subwaytooter.pref.PrefB
|
||||
import jp.juggler.subwaytooter.pref.PrefI
|
||||
import jp.juggler.subwaytooter.span.EmojiImageSpan
|
||||
import jp.juggler.subwaytooter.span.LinkInfo
|
||||
import jp.juggler.subwaytooter.span.MyClickableSpan
|
||||
import jp.juggler.subwaytooter.span.createSpan
|
||||
import jp.juggler.subwaytooter.span.*
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
import jp.juggler.subwaytooter.table.daoAcctColor
|
||||
|
@ -251,9 +248,9 @@ internal class ViewHolderHeaderProfile(
|
|||
|
||||
tvAcct.text = "@"
|
||||
|
||||
tvDisplayName.text = ""
|
||||
nameInvalidator1.text = ""
|
||||
|
||||
tvNote.text = ""
|
||||
noteInvalidator.text = ""
|
||||
tvMisskeyExtra.text = ""
|
||||
|
||||
btnStatusCount.text = activity.getString(R.string.statuses) + "\n" + "?"
|
||||
|
@ -276,8 +273,7 @@ internal class ViewHolderHeaderProfile(
|
|||
|
||||
who.setAccountExtra(
|
||||
accessInfo,
|
||||
tvLastStatusAt,
|
||||
invalidator = null,
|
||||
NetworkEmojiInvalidator(activity.handler ,tvLastStatusAt),
|
||||
fromProfileHeader = true
|
||||
)
|
||||
|
||||
|
@ -296,16 +292,14 @@ internal class ViewHolderHeaderProfile(
|
|||
)
|
||||
|
||||
val name = whoDetail?.decodeDisplayName(activity) ?: whoRef.decoded_display_name
|
||||
tvDisplayName.text = name
|
||||
nameInvalidator1.register(name)
|
||||
nameInvalidator1.text = name
|
||||
|
||||
tvRemoteProfileWarning.vg(column.accessInfo.isRemoteUser(who))
|
||||
|
||||
tvAcct.text = encodeAcctText(who, whoDetail)
|
||||
|
||||
val note = whoRef.decoded_note
|
||||
tvNote.text = note
|
||||
noteInvalidator.register(note)
|
||||
noteInvalidator.text = note
|
||||
|
||||
tvMisskeyExtra.text = encodeMisskeyExtra(whoDetail)
|
||||
tvMisskeyExtra.vg(tvMisskeyExtra.text.isNotEmpty())
|
||||
|
@ -361,8 +355,7 @@ internal class ViewHolderHeaderProfile(
|
|||
val caption = who.decodeDisplayName(activity)
|
||||
.intoStringResource(activity, R.string.account_moved_to)
|
||||
|
||||
tvMoved.text = caption
|
||||
movedCaptionInvalidator.register(caption)
|
||||
movedCaptionInvalidator.text = caption
|
||||
|
||||
ivMoved.layoutParams.width = activity.avatarIconSize
|
||||
ivMoved.setImageUrl(
|
||||
|
@ -370,8 +363,7 @@ internal class ViewHolderHeaderProfile(
|
|||
accessInfo.supplyBaseUrl(moved.avatar_static)
|
||||
)
|
||||
|
||||
tvMovedName.text = movedRef.decoded_display_name
|
||||
movedNameInvalidator.register(movedRef.decoded_display_name)
|
||||
movedNameInvalidator.text = movedRef.decoded_display_name
|
||||
|
||||
setAcct(tvMovedAcct, accessInfo, moved)
|
||||
|
||||
|
@ -633,7 +625,8 @@ internal class ViewHolderHeaderProfile(
|
|||
short = true,
|
||||
emojiMapCustom = who.custom_emojis,
|
||||
emojiMapProfile = who.profile_emojis,
|
||||
authorDomain = who
|
||||
authorDomain = who,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
)
|
||||
|
||||
val nameTypeface = ActMain.timelineFontBold
|
||||
|
@ -648,17 +641,17 @@ internal class ViewHolderHeaderProfile(
|
|||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
val nameText = fieldDecodeOptions.decodeEmoji(item.name)
|
||||
val nameInvalidator = NetworkEmojiInvalidator(activity.handler, nameView)
|
||||
nameInvalidator.register(nameText)
|
||||
|
||||
nameLp.topMargin = (density * 6f).toInt()
|
||||
nameView.layoutParams = nameLp
|
||||
nameView.text = nameText
|
||||
nameView.setTextColor(colorTextContent)
|
||||
nameView.typeface = nameTypeface
|
||||
nameView.movementMethod = MyLinkMovementMethod
|
||||
views.llFields.addView(nameView)
|
||||
|
||||
val nameInvalidator = NetworkEmojiInvalidator(activity.handler, nameView)
|
||||
nameInvalidator.text = nameText
|
||||
|
||||
// 値の方はHTMLエンコードされている
|
||||
val valueView = MyTextView(activity)
|
||||
val valueLp = LinearLayout.LayoutParams(
|
||||
|
@ -687,16 +680,16 @@ internal class ViewHolderHeaderProfile(
|
|||
)
|
||||
}
|
||||
|
||||
val valueInvalidator = NetworkEmojiInvalidator(activity.handler, valueView)
|
||||
valueInvalidator.register(valueText)
|
||||
|
||||
valueLp.startMargin = (density * 32f).toInt()
|
||||
valueView.layoutParams = valueLp
|
||||
valueView.text = valueText
|
||||
valueView.setTextColor(colorTextContent)
|
||||
valueView.typeface = valueTypeface
|
||||
valueView.movementMethod = MyLinkMovementMethod
|
||||
|
||||
val valueInvalidator = NetworkEmojiInvalidator(activity.handler, valueView)
|
||||
valueInvalidator.text = valueText
|
||||
|
||||
if (item.verified_at > 0L) {
|
||||
val linkBgColor = PrefI.ipVerifiedLinkBgColor.value.notZero()
|
||||
?: (0x337fbc99)
|
||||
|
|
|
@ -7,6 +7,7 @@ import jp.juggler.subwaytooter.column.Column
|
|||
import jp.juggler.subwaytooter.column.getContentColor
|
||||
import jp.juggler.subwaytooter.column.getHeaderDesc
|
||||
import jp.juggler.subwaytooter.databinding.LvHeaderSearchDescBinding
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.view.MyLinkMovementMethod
|
||||
import org.jetbrains.anko.textColor
|
||||
|
@ -35,7 +36,8 @@ internal class ViewHolderHeaderSearch(
|
|||
tvSearchDesc.textColor = column.getContentColor()
|
||||
tvSearchDesc.text = DecodeOptions(
|
||||
activity, accessInfo, decodeEmoji = true,
|
||||
authorDomain = accessInfo
|
||||
authorDomain = accessInfo,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
).decodeHTML(column.getHeaderDesc())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import jp.juggler.subwaytooter.R
|
|||
import jp.juggler.subwaytooter.api.auth.CreateUserParams
|
||||
import jp.juggler.subwaytooter.api.entity.Host
|
||||
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.LinkHelper
|
||||
import jp.juggler.subwaytooter.util.openCustomTab
|
||||
|
@ -69,7 +70,7 @@ class DlgCreateAccount(
|
|||
linkHelper = LinkHelper.create(
|
||||
apiHost,
|
||||
misskeyVersion = instanceInfo?.misskeyVersionMajor ?: 0
|
||||
)
|
||||
),
|
||||
).decodeHTML(
|
||||
instanceInfo?.short_description?.notBlank()
|
||||
?: instanceInfo?.description?.notBlank()
|
||||
|
|
|
@ -80,12 +80,12 @@ class DlgListMember(
|
|||
who.avatar_static,
|
||||
who.avatar
|
||||
)
|
||||
val userNameInvalidator = NetworkEmojiInvalidator(activity.handler, tvUserName)
|
||||
val name = who.decodeDisplayName(activity)
|
||||
tvUserName.text = name
|
||||
userNameInvalidator.register(name)
|
||||
tvUserAcct.text = targetUserFullAcct.pretty
|
||||
|
||||
val name = who.decodeDisplayName(activity)
|
||||
val userNameInvalidator = NetworkEmojiInvalidator(activity.handler, tvUserName)
|
||||
userNameInvalidator.text = name
|
||||
|
||||
setListOwner(listOwner)
|
||||
|
||||
dialog = Dialog(activity).apply {
|
||||
|
|
|
@ -129,13 +129,11 @@ fun ItemViewHolder.makeEnqueteChoiceView(
|
|||
|
||||
val b = AppCompatTextView(activity)
|
||||
b.layoutParams = lp
|
||||
b.padding = (activity.density * 3f + 0.5f).toInt()
|
||||
|
||||
b.text = text
|
||||
val invalidator = NetworkEmojiInvalidator(activity.handler, b)
|
||||
extraInvalidatorList.add(invalidator)
|
||||
invalidator.register(text)
|
||||
|
||||
b.padding = (activity.density * 3f + 0.5f).toInt()
|
||||
invalidator.text = text
|
||||
|
||||
val ratio = when (enquete.pollType) {
|
||||
TootPollsType.Mastodon -> {
|
||||
|
@ -173,10 +171,11 @@ fun ItemViewHolder.makeEnqueteChoiceView(
|
|||
val b = CheckBox(activity)
|
||||
b.layoutParams = lp
|
||||
b.isAllCaps = false
|
||||
b.text = text
|
||||
|
||||
val invalidator = NetworkEmojiInvalidator(activity.handler, b)
|
||||
extraInvalidatorList.add(invalidator)
|
||||
invalidator.register(text)
|
||||
invalidator.text = text
|
||||
|
||||
if (!canVote) {
|
||||
b.isEnabledAlpha = false
|
||||
} else {
|
||||
|
@ -190,10 +189,11 @@ fun ItemViewHolder.makeEnqueteChoiceView(
|
|||
val b = AppCompatButton(activity)
|
||||
b.layoutParams = lp
|
||||
b.isAllCaps = false
|
||||
b.text = text
|
||||
|
||||
val invalidator = NetworkEmojiInvalidator(activity.handler, b)
|
||||
extraInvalidatorList.add(invalidator)
|
||||
invalidator.register(text)
|
||||
invalidator.text = text
|
||||
|
||||
if (!canVote) {
|
||||
b.isEnabled = false
|
||||
} else {
|
||||
|
|
|
@ -6,6 +6,7 @@ import jp.juggler.subwaytooter.api.entity.TootStatus
|
|||
import jp.juggler.subwaytooter.column.isConversation
|
||||
import jp.juggler.subwaytooter.pref.PrefB
|
||||
import jp.juggler.subwaytooter.pref.PrefS
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.daoMediaShown
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.HTMLDecoder
|
||||
|
@ -68,7 +69,8 @@ fun ItemViewHolder.showPreviewCard(status: TootStatus) {
|
|||
val text = DecodeOptions(
|
||||
activity, accessInfo,
|
||||
forceHtml = true,
|
||||
authorDomain = status.account
|
||||
authorDomain = status.account,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
).decodeHTML(sb.toString())
|
||||
|
||||
if (text.isNotEmpty()) {
|
||||
|
|
|
@ -18,6 +18,7 @@ import jp.juggler.subwaytooter.api.entity.TootReaction
|
|||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.subwaytooter.pref.PrefB
|
||||
import jp.juggler.subwaytooter.pref.PrefI
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.util.NetworkEmojiInvalidator
|
||||
import jp.juggler.subwaytooter.util.minWidthCompat
|
||||
|
@ -74,6 +75,7 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
|
|||
decodeEmoji = true,
|
||||
enlargeEmoji = imageScale,
|
||||
enlargeCustomEmoji = imageScale,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
)
|
||||
|
||||
reactionSet?.forEachIndexed { index, reaction ->
|
||||
|
@ -132,15 +134,12 @@ fun ItemViewHolder.makeReactionsView(status: TootStatus) {
|
|||
// カスタム絵文字の場合、アニメーション等のコールバックを処理する必要がある
|
||||
val invalidator = NetworkEmojiInvalidator(act.handler, this)
|
||||
extraInvalidatorList.add(invalidator)
|
||||
try {
|
||||
val ssb =
|
||||
reaction.toSpannableStringBuilder(options, status)
|
||||
.also { it.append(" ${reaction.count}") }
|
||||
text = ssb
|
||||
invalidator.register(ssb)
|
||||
invalidator.text = try {
|
||||
reaction.toSpannableStringBuilder(options, status)
|
||||
.also { it.append(" ${reaction.count}") }
|
||||
} catch (ex: Throwable) {
|
||||
log.e(ex, "can't decode reaction emoji.")
|
||||
text = "${reaction.name} ${reaction.count}"
|
||||
"${reaction.name} ${reaction.count}"
|
||||
}
|
||||
}
|
||||
box.addView(b)
|
||||
|
|
|
@ -25,6 +25,7 @@ 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.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.*
|
||||
import jp.juggler.subwaytooter.util.DecodeOptions
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||
|
@ -268,14 +269,12 @@ fun ItemViewHolder.showAccount(whoRef: TootAccountRef) {
|
|||
accessInfo.supplyBaseUrl(who.avatar)
|
||||
)
|
||||
|
||||
tvFollowerName.text = whoRef.decoded_display_name
|
||||
followInvalidator.register(whoRef.decoded_display_name)
|
||||
followInvalidator.text = whoRef.decoded_display_name
|
||||
|
||||
setAcct(tvFollowerAcct, accessInfo, who)
|
||||
|
||||
who.setAccountExtra(
|
||||
accessInfo,
|
||||
tvLastStatusAt,
|
||||
lastActiveInvalidator,
|
||||
suggestionSource = if (column.type == ColumnType.FOLLOW_SUGGESTION) {
|
||||
SuggestionSource.get(accessInfo.db_id, who.acct)
|
||||
|
@ -356,7 +355,8 @@ fun ItemViewHolder.showBoost(
|
|||
accessInfo,
|
||||
decodeEmoji = true,
|
||||
enlargeEmoji = 1.5f,
|
||||
enlargeCustomEmoji = 1.5f
|
||||
enlargeCustomEmoji = 1.5f,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
)
|
||||
val ssb = reaction.toSpannableStringBuilder(options, boostStatus)
|
||||
ssb.append(" ")
|
||||
|
@ -364,15 +364,11 @@ fun ItemViewHolder.showBoost(
|
|||
who.decodeDisplayNameCached(activity)
|
||||
.intoStringResource(activity, stringId)
|
||||
)
|
||||
val text: Spannable =ssb
|
||||
tvBoosted.text = text
|
||||
boostInvalidator.register(text)
|
||||
boostInvalidator.text = ssb
|
||||
} else {
|
||||
val text: Spannable =
|
||||
boostInvalidator.text =
|
||||
who.decodeDisplayNameCached(activity)
|
||||
.intoStringResource(activity, stringId)
|
||||
tvBoosted.text = text
|
||||
boostInvalidator.register(text)
|
||||
.intoStringResource(activity, stringId)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -501,8 +497,7 @@ fun ItemViewHolder.showReply(
|
|||
)
|
||||
}
|
||||
|
||||
tvReply.text = text
|
||||
replyInvalidator.register(text)
|
||||
replyInvalidator.text = text
|
||||
}
|
||||
|
||||
fun ItemViewHolder.showReply(replyer: TootAccount?, reply: TootStatus, iconId: Int, stringId: Int) {
|
||||
|
@ -738,8 +733,8 @@ fun ItemViewHolder.showScheduled(item: TootScheduled) {
|
|||
|
||||
setAcct(tvAcct, accessInfo, who)
|
||||
|
||||
tvName.text = whoRef.decoded_display_name
|
||||
nameInvalidator.register(whoRef.decoded_display_name)
|
||||
nameInvalidator.text = whoRef.decoded_display_name
|
||||
|
||||
ivAvatar.setImageUrl(
|
||||
calcIconRound(ivAvatar.layoutParams),
|
||||
accessInfo.supplyBaseUrl(who.avatar_static),
|
||||
|
@ -750,8 +745,7 @@ fun ItemViewHolder.showScheduled(item: TootScheduled) {
|
|||
|
||||
tvMentions.visibility = View.GONE
|
||||
|
||||
tvContent.text = content
|
||||
contentInvalidator.register(content)
|
||||
contentInvalidator.text = content
|
||||
|
||||
tvContent.minLines = -1
|
||||
|
||||
|
@ -760,8 +754,7 @@ fun ItemViewHolder.showScheduled(item: TootScheduled) {
|
|||
decodedSpoilerText.isNotEmpty() -> {
|
||||
// 元データに含まれるContent Warning を使う
|
||||
llContentWarning.visibility = View.VISIBLE
|
||||
tvContentWarning.text = decodedSpoilerText
|
||||
spoilerInvalidator.register(decodedSpoilerText)
|
||||
spoilerInvalidator.text = decodedSpoilerText
|
||||
val cwShown = daoContentWarning.isShown(item.uri, accessInfo.expandCw)
|
||||
setContentVisibility(cwShown)
|
||||
}
|
||||
|
|
|
@ -104,8 +104,8 @@ fun ItemViewHolder.showStatus(
|
|||
|
||||
setAcct(tvAcct, accessInfo, who)
|
||||
|
||||
tvName.text = whoRef.decoded_display_name
|
||||
nameInvalidator.register(whoRef.decoded_display_name)
|
||||
nameInvalidator.text = whoRef.decoded_display_name
|
||||
|
||||
ivAvatar.setImageUrl(
|
||||
calcIconRound(ivAvatar.layoutParams),
|
||||
accessInfo.supplyBaseUrl(who.avatar_static),
|
||||
|
@ -147,8 +147,7 @@ fun ItemViewHolder.showStatus(
|
|||
|
||||
tvMentions.textOrGone = status.decoded_mentions
|
||||
|
||||
tvContent.text = modifiedContent
|
||||
contentInvalidator.register(modifiedContent)
|
||||
contentInvalidator.text = modifiedContent
|
||||
|
||||
activity.checkAutoCW(status, modifiedContent)
|
||||
val r = status.auto_cw
|
||||
|
@ -183,21 +182,20 @@ private fun ItemViewHolder.showPoll(status: TootStatus): Spannable? {
|
|||
private fun ItemViewHolder.showSpoilerTextAndContent(status: TootStatus) {
|
||||
val r = status.auto_cw
|
||||
val decodedSpoilerText = status.decoded_spoiler_text
|
||||
val autoCwText = r?.decodedSpoilerText
|
||||
when {
|
||||
decodedSpoilerText.isNotEmpty() -> {
|
||||
// 元データに含まれるContent Warning を使う
|
||||
llContentWarning.visibility = View.VISIBLE
|
||||
tvContentWarning.text = status.decoded_spoiler_text
|
||||
spoilerInvalidator.register(status.decoded_spoiler_text)
|
||||
spoilerInvalidator.text = status.decoded_spoiler_text
|
||||
val cwShown = daoContentWarning.isShown(status, accessInfo.expandCw)
|
||||
setContentVisibility(cwShown)
|
||||
}
|
||||
|
||||
r?.decodedSpoilerText != null -> {
|
||||
autoCwText != null -> {
|
||||
// 自動CW
|
||||
llContentWarning.visibility = View.VISIBLE
|
||||
tvContentWarning.text = r.decodedSpoilerText
|
||||
spoilerInvalidator.register(r.decodedSpoilerText)
|
||||
spoilerInvalidator.text = autoCwText
|
||||
val cwShown = daoContentWarning.isShown(status, accessInfo.expandCw)
|
||||
setContentVisibility(cwShown)
|
||||
}
|
||||
|
@ -221,10 +219,11 @@ fun ItemViewHolder.setContentVisibility(shown: Boolean) {
|
|||
statusShowing?.let { status ->
|
||||
val r = status.auto_cw
|
||||
tvContent.minLines = r?.originalLineCount ?: -1
|
||||
if (r?.decodedSpoilerText != null) {
|
||||
val autoCwText = r?.decodedSpoilerText
|
||||
if (autoCwText != null) {
|
||||
// 自動CWの場合はContentWarningのテキストを切り替える
|
||||
tvContentWarning.text =
|
||||
if (shown) activity.getString(R.string.auto_cw_prefix) else r.decodedSpoilerText
|
||||
spoilerInvalidator.text =
|
||||
if (shown) activity.getString(R.string.auto_cw_prefix) else autoCwText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package jp.juggler.subwaytooter.span
|
|||
interface AnimatableSpanInvalidator {
|
||||
val timeFromStart: Long
|
||||
fun delayInvalidate(delay: Long)
|
||||
fun requestLayout()
|
||||
}
|
||||
|
||||
interface AnimatableSpan {
|
||||
|
|
|
@ -6,29 +6,51 @@ import android.graphics.Rect
|
|||
import android.graphics.RectF
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.style.ReplacementSpan
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.annotation.IntRange
|
||||
import androidx.collection.LruCache
|
||||
import androidx.core.content.ContextCompat
|
||||
import jp.juggler.apng.ApngFrames
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||
import jp.juggler.subwaytooter.pref.PrefB
|
||||
import jp.juggler.subwaytooter.pref.lazyContext
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.util.log.LogCategory
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlin.math.min
|
||||
|
||||
class NetworkEmojiSpan internal constructor(
|
||||
enum class EmojiSizeMode {
|
||||
Square,
|
||||
Wide,
|
||||
}
|
||||
|
||||
fun SavedAccount?.emojiSizeMode(): EmojiSizeMode {
|
||||
val ti = this?.let { TootInstance.getCached(it) }
|
||||
return when {
|
||||
ti == null -> EmojiSizeMode.Square
|
||||
ti.isMisskey || !ti.fedibirdCapabilities.isNullOrEmpty() -> EmojiSizeMode.Wide
|
||||
else -> EmojiSizeMode.Square
|
||||
}
|
||||
}
|
||||
|
||||
class NetworkEmojiSpan constructor(
|
||||
private val url: String,
|
||||
private val sizeMode: EmojiSizeMode,
|
||||
private val scale: Float = 1f,
|
||||
@DrawableRes private val errorDrawableId: Int = R.drawable.outline_broken_image_24,
|
||||
private val errorDrawableId: Int = R.drawable.outline_broken_image_24,
|
||||
) : ReplacementSpan(), AnimatableSpan {
|
||||
|
||||
companion object {
|
||||
|
||||
internal val log = LogCategory("NetworkEmojiSpan")
|
||||
|
||||
private const val scaleRatio = 1.14f
|
||||
private const val descentRatio = 0.211f
|
||||
|
||||
// 最大幅
|
||||
var maxEmojiWidth = Float.MAX_VALUE
|
||||
|
||||
// ImageWidthCache
|
||||
val imageAspectCache = LruCache<String, Float>(1024)
|
||||
}
|
||||
|
||||
private val mPaint = Paint().apply { isFilterBitmap = true }
|
||||
|
@ -44,6 +66,67 @@ class NetworkEmojiSpan internal constructor(
|
|||
|
||||
private var errorDrawableCache: Drawable? = null
|
||||
|
||||
private var lastWidth: Float? = null
|
||||
private var transY = 0f
|
||||
private var baseHeight = 0f
|
||||
|
||||
/**
|
||||
* lastAspect に基づいて rectDst と transY を更新する
|
||||
*/
|
||||
private fun updateRect(aspectArg: Float? = null, textSize: Float, baseline: Float) {
|
||||
// テキストサイズをスケーリングした基本高さ
|
||||
this.baseHeight = textSize * scaleRatio * scale
|
||||
|
||||
// ベースラインから上下方向にずらすオフセット
|
||||
val cDescent = baseHeight * descentRatio
|
||||
this.transY = baseline - baseHeight + cDescent
|
||||
|
||||
val aspect = when(aspectArg){
|
||||
null ->{
|
||||
imageAspectCache[url] ?: 1f
|
||||
}
|
||||
else->{
|
||||
imageAspectCache.put(url,aspectArg)
|
||||
aspectArg
|
||||
}
|
||||
}.takeIf { it>0f }?:1f
|
||||
|
||||
when {
|
||||
// 横長画像で、それを許可するモード
|
||||
aspect > 1f && sizeMode == EmojiSizeMode.Wide -> {
|
||||
// 絵文字のアスペクト比から描画範囲の幅と高さを決める
|
||||
val dstWidth = min(maxEmojiWidth, aspect * baseHeight)
|
||||
val dstHeight = dstWidth / aspect
|
||||
val dstX = 0f
|
||||
val dstY = (baseHeight - dstHeight) / 2f
|
||||
rectDst.set(dstX, dstY, dstX + dstWidth, dstY + dstHeight)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// 絵文字のアスペクト比から描画範囲の幅と高さを決める
|
||||
val dstWidth: Float
|
||||
val dstHeight: Float
|
||||
if (aspect >= 1f) {
|
||||
dstWidth = baseHeight
|
||||
dstHeight = baseHeight / aspect
|
||||
} else {
|
||||
dstHeight = baseHeight
|
||||
dstWidth = baseHeight * aspect
|
||||
}
|
||||
val dstX = (baseHeight - dstWidth) / 2f
|
||||
val dstY = (baseHeight - dstHeight) / 2f
|
||||
rectDst.set(dstX, dstY, dstX + dstWidth, dstY + dstHeight)
|
||||
}
|
||||
}
|
||||
// 出力サイズが変化したならrequestLayout
|
||||
val newWidth = rectDst.width()
|
||||
if (lastWidth != null && lastWidth != newWidth) {
|
||||
log.i("updateRect: width changed. $lastWidth → $newWidth")
|
||||
invalidateCallback?.requestLayout()
|
||||
}
|
||||
lastWidth = newWidth
|
||||
}
|
||||
|
||||
override fun setInvalidateCallback(
|
||||
drawTargetTag: Any,
|
||||
invalidateCallback: AnimatableSpanInvalidator,
|
||||
|
@ -59,16 +142,17 @@ class NetworkEmojiSpan internal constructor(
|
|||
@IntRange(from = 0) end: Int,
|
||||
fm: Paint.FontMetricsInt?,
|
||||
): Int {
|
||||
val size = (paint.textSize * scaleRatio * scale + 0.5f).toInt()
|
||||
updateRect(aspectArg = null, paint.textSize, baseline = 0f)
|
||||
val height = (baseHeight + 0.5f).toInt()
|
||||
if (fm != null) {
|
||||
val cDescent = (0.5f + size * descentRatio).toInt()
|
||||
val cAscent = cDescent - size
|
||||
val cDescent = (0.5f + height * descentRatio).toInt()
|
||||
val cAscent = cDescent - height
|
||||
if (fm.ascent > cAscent) fm.ascent = cAscent
|
||||
if (fm.top > cAscent) fm.top = cAscent
|
||||
if (fm.descent < cDescent) fm.descent = cDescent
|
||||
if (fm.bottom < cDescent) fm.bottom = cDescent
|
||||
}
|
||||
return size
|
||||
return (rectDst.width() + 0.5f).toInt()
|
||||
}
|
||||
|
||||
override fun draw(
|
||||
|
@ -123,28 +207,11 @@ class NetworkEmojiSpan internal constructor(
|
|||
return false
|
||||
}
|
||||
rectSrc.set(0, 0, srcWidth, srcHeight)
|
||||
|
||||
// 絵文字の正方形のサイズ
|
||||
val dstSize = textPaint.textSize * scaleRatio * scale
|
||||
|
||||
// ベースラインから上下方向にずらすオフセット
|
||||
val cDescent = dstSize * descentRatio
|
||||
val transY = baseline - dstSize + cDescent
|
||||
|
||||
// 絵文字のアスペクト比から描画範囲の幅と高さを決める
|
||||
val dstWidth: Float
|
||||
val dstHeight: Float
|
||||
val aspectSrc = srcWidth.toFloat() / srcHeight.toFloat()
|
||||
if (aspectSrc >= 1f) {
|
||||
dstWidth = dstSize
|
||||
dstHeight = dstSize / aspectSrc
|
||||
} else {
|
||||
dstHeight = dstSize
|
||||
dstWidth = dstSize * aspectSrc
|
||||
}
|
||||
val dstX = (dstSize - dstWidth) / 2f
|
||||
val dstY = (dstSize - dstHeight) / 2f
|
||||
rectDst.set(dstX, dstY, dstX + dstWidth, dstY + dstHeight)
|
||||
updateRect(
|
||||
aspectArg = srcWidth.toFloat() / srcHeight.toFloat(),
|
||||
textPaint.textSize,
|
||||
baseline.toFloat()
|
||||
)
|
||||
|
||||
canvas.save()
|
||||
try {
|
||||
|
@ -184,38 +251,23 @@ class NetworkEmojiSpan internal constructor(
|
|||
?.also { errorDrawableCache = it }
|
||||
|
||||
drawable ?: return
|
||||
val srcWidth = drawable.intrinsicWidth
|
||||
val srcHeight = drawable.intrinsicHeight
|
||||
val srcWidth = drawable.intrinsicWidth.toFloat()
|
||||
val srcHeight = drawable.intrinsicHeight.toFloat()
|
||||
|
||||
// 絵文字の正方形のサイズ
|
||||
val dstSize = textPaint.textSize * scaleRatio * scale
|
||||
updateRect(
|
||||
aspectArg = srcWidth / srcHeight,
|
||||
textSize = textPaint.textSize,
|
||||
baseline = baseline.toFloat()
|
||||
)
|
||||
|
||||
// ベースラインから上下方向にずらすオフセット
|
||||
val cDescent = dstSize * descentRatio
|
||||
val transY = baseline - dstSize + cDescent
|
||||
|
||||
// 絵文字のアスペクト比から描画範囲の幅と高さを決める
|
||||
val dstWidth: Float
|
||||
val dstHeight: Float
|
||||
val aspectSrc = srcWidth.toFloat() / srcHeight.toFloat()
|
||||
if (aspectSrc >= 1f) {
|
||||
dstWidth = dstSize
|
||||
dstHeight = dstSize / aspectSrc
|
||||
} else {
|
||||
dstHeight = dstSize
|
||||
dstWidth = dstSize * aspectSrc
|
||||
}
|
||||
val dstX = (dstSize - dstWidth) / 2f
|
||||
val dstY = (dstSize - dstHeight) / 2f
|
||||
// rectDst.set(dstX, dstY, dstX + dstWidth, dstY + dstHeight)
|
||||
canvas.save()
|
||||
try {
|
||||
canvas.translate(x, transY)
|
||||
drawable.setBounds(
|
||||
dstX.toInt(),
|
||||
dstY.toInt(),
|
||||
(dstX + dstWidth).toInt(),
|
||||
(dstY + dstHeight).toInt()
|
||||
rectDst.left.toInt(),
|
||||
rectDst.top.toInt(),
|
||||
rectDst.right.plus(0.5f).toInt(),
|
||||
rectDst.bottom.plus(0.5f).toInt(),
|
||||
)
|
||||
drawable.draw(canvas)
|
||||
} catch (ex: Throwable) {
|
||||
|
|
|
@ -319,9 +319,11 @@ class CustomEmojiCache(
|
|||
private fun decodeAPNG(data: ByteArray, url: String): ApngFrames? {
|
||||
val errors = ArrayList<Throwable>()
|
||||
|
||||
val maxSize = 256
|
||||
|
||||
try {
|
||||
// APNGをデコード AWebPも
|
||||
val x = ApngFrames.parse(64) { ByteArrayInputStream(data) }
|
||||
val x = ApngFrames.parse(maxSize) { ByteArrayInputStream(data) }
|
||||
if (x != null) return x
|
||||
error("ApngFrames.parse returns null.")
|
||||
} catch (ex: Throwable) {
|
||||
|
@ -331,7 +333,7 @@ class CustomEmojiCache(
|
|||
|
||||
// 通常のビットマップでのロードを試みる
|
||||
try {
|
||||
val b = decodeBitmap(data, 128)
|
||||
val b = decodeBitmap(data, maxSize)
|
||||
if (b != null) return ApngFrames(b)
|
||||
error("decodeBitmap returns null.")
|
||||
} catch (ex: Throwable) {
|
||||
|
@ -341,7 +343,7 @@ class CustomEmojiCache(
|
|||
|
||||
// SVGのロードを試みる
|
||||
try {
|
||||
val b = decodeSVG(url, data, 128.toFloat())
|
||||
val b = decodeSVG(url, data, maxSize.toFloat())
|
||||
if (b != null) return ApngFrames(b)
|
||||
error("decodeSVG returns null.")
|
||||
} catch (ex: Throwable) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import jp.juggler.subwaytooter.api.entity.NicoProfileEmoji
|
|||
import jp.juggler.subwaytooter.api.entity.TootAttachmentLike
|
||||
import jp.juggler.subwaytooter.api.entity.TootMention
|
||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||
import jp.juggler.subwaytooter.span.EmojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.HighlightWord
|
||||
import jp.juggler.util.data.WordTrieTree
|
||||
import org.jetbrains.anko.collections.forEachReversedByIndex
|
||||
|
@ -33,6 +34,7 @@ class DecodeOptions(
|
|||
// Account.note などmentionsがない状況でメンションリンクをfull acct化するにはアカウント等からapDomainを補う必要がある
|
||||
// MFMはメンションのホスト名を補うのに閲覧者ではなく投稿作者のホスト名を必要とする
|
||||
var authorDomain: HostAndDomain? = null,
|
||||
var emojiSizeMode: EmojiSizeMode = EmojiSizeMode.Square,
|
||||
) {
|
||||
|
||||
internal fun isMediaAttachment(url: String?): Boolean =
|
||||
|
|
|
@ -5,7 +5,6 @@ import android.text.SpannableStringBuilder
|
|||
import android.text.Spanned
|
||||
import android.util.SparseBooleanArray
|
||||
import androidx.annotation.DrawableRes
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.emoji.CustomEmoji
|
||||
import jp.juggler.subwaytooter.emoji.EmojiMap
|
||||
|
@ -143,7 +142,11 @@ object EmojiDecoder {
|
|||
sb.append(text)
|
||||
val end = sb.length
|
||||
sb.setSpan(
|
||||
NetworkEmojiSpan(url, scale = options.enlargeCustomEmoji),
|
||||
NetworkEmojiSpan(
|
||||
url,
|
||||
scale = options.enlargeCustomEmoji,
|
||||
sizeMode = options.emojiSizeMode
|
||||
),
|
||||
start,
|
||||
end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
|
@ -396,7 +399,7 @@ object EmojiDecoder {
|
|||
else -> {
|
||||
// 存在確認せずに絵文字プロキシのURLを返す
|
||||
// 閲覧先サーバの絵文字を探す
|
||||
return "https://${apiHostAscii}/emoji/${ cols.elementAtOrNull(0)}.webp"
|
||||
return "https://${apiHostAscii}/emoji/${cols.elementAtOrNull(0)}.webp"
|
||||
}
|
||||
}
|
||||
return null
|
||||
|
|
|
@ -528,7 +528,11 @@ object HTMLDecoder {
|
|||
append(alt)
|
||||
val end = length
|
||||
setSpan(
|
||||
NetworkEmojiSpan(url, scale = options.enlargeCustomEmoji),
|
||||
NetworkEmojiSpan(
|
||||
url,
|
||||
scale = options.enlargeCustomEmoji,
|
||||
sizeMode = options.emojiSizeMode
|
||||
),
|
||||
start,
|
||||
end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package jp.juggler.subwaytooter.util
|
||||
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.span.EmojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.util.data.findOrNull
|
||||
import jp.juggler.util.data.groupEx
|
||||
|
||||
|
@ -69,7 +71,7 @@ fun LinkHelper.matchHost(src: TootAccount) =
|
|||
apiHost == src.apiHost || apDomain == src.apDomain ||
|
||||
apDomain == src.apiHost || apiHost == src.apDomain
|
||||
|
||||
fun LinkHelper.matchHost(srcApiHost:Host,srcApDomain:Host) =
|
||||
fun LinkHelper.matchHost(srcApiHost: Host, srcApDomain: Host) =
|
||||
apiHost == srcApiHost ||
|
||||
apDomain == srcApDomain ||
|
||||
apDomain == srcApiHost ||
|
||||
|
|
|
@ -3,16 +3,24 @@ package jp.juggler.subwaytooter.util
|
|||
import android.os.Handler
|
||||
import android.os.SystemClock
|
||||
import android.text.Spannable
|
||||
import android.view.View
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
import android.widget.TextView
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.span.AnimatableSpan
|
||||
import jp.juggler.subwaytooter.span.AnimatableSpanInvalidator
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class NetworkEmojiInvalidator(internal val handler: Handler, internal val view: View) : Runnable, AnimatableSpanInvalidator {
|
||||
class NetworkEmojiInvalidator(
|
||||
internal val handler: Handler,
|
||||
internal val view: TextView,
|
||||
// val parent:View? = null,
|
||||
) : AnimatableSpanInvalidator {
|
||||
|
||||
var text: CharSequence
|
||||
get() = view.text
|
||||
set(value) {
|
||||
if (value is Spannable) register(value)
|
||||
view.text = value
|
||||
}
|
||||
|
||||
private val drawTargetList = ArrayList<WeakReference<Any>>()
|
||||
|
||||
|
@ -34,6 +42,43 @@ class NetworkEmojiInvalidator(internal val handler: Handler, internal val view:
|
|||
return now - tStart
|
||||
}
|
||||
|
||||
// Handler経由で遅延実行される
|
||||
private val runnableInvalidate = object : Runnable {
|
||||
override fun run() {
|
||||
// 後から来たのをこのタイミングで重複排除する
|
||||
handler.removeCallbacks(this)
|
||||
if (view.isAttachedToWindow) {
|
||||
view.postInvalidateOnAnimation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handler経由で遅延実行される
|
||||
private val runnableRequestLayout = object : Runnable {
|
||||
override fun run() {
|
||||
// 後から来たのをこのタイミングで重複排除する
|
||||
handler.removeCallbacks(this)
|
||||
if (view.isAttachedToWindow) {
|
||||
view.requestLayout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 絵文字スパンを描画した直後に呼ばれる
|
||||
// (絵文字が多いと描画の度に大量に呼び出される)
|
||||
override fun delayInvalidate(delay: Long) {
|
||||
// あとから来たのをconflateしたいので、このタイミングでは removeCallbacks しない
|
||||
handler.postDelayed(
|
||||
runnableInvalidate,
|
||||
if (delay < 10L) 10L else if (delay > 711L) 711L else delay
|
||||
)
|
||||
}
|
||||
|
||||
override fun requestLayout() {
|
||||
// あとから来たのをconflateしたいので、このタイミングでは removeCallbacks しない
|
||||
handler.postDelayed(runnableRequestLayout, 10L)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
for (o in drawTargetList) {
|
||||
App1.custom_emoji_cache.cancelRequest(o)
|
||||
|
@ -53,18 +98,4 @@ class NetworkEmojiInvalidator(internal val handler: Handler, internal val view:
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 絵文字スパンを描画した直後に呼ばれる
|
||||
// (絵文字が多いと描画の度に大量に呼び出される)
|
||||
override fun delayInvalidate(delay: Long) {
|
||||
handler.postDelayed(this, if (delay < 10L) 10L else if (delay > 711L) 711L else delay)
|
||||
}
|
||||
|
||||
// Handler経由で遅延実行される
|
||||
override fun run() {
|
||||
handler.removeCallbacks(this)
|
||||
if (view.isAttachedToWindow) {
|
||||
view.postInvalidateOnAnimation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ internal class PopupAutoCompleteAcct(
|
|||
v.setTextColor(activity.attrColor(android.R.attr.textColorPrimary))
|
||||
v.text = acct
|
||||
if (acct is Spannable) {
|
||||
NetworkEmojiInvalidator(handler, v).register(acct)
|
||||
NetworkEmojiInvalidator(handler, v).text = acct
|
||||
}
|
||||
v.setOnClickListener { handleItemClick(et, selStart, selEnd, acct) }
|
||||
llItems.addView(v)
|
||||
|
|
|
@ -7,6 +7,7 @@ import jp.juggler.subwaytooter.ActText
|
|||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.pref.PrefB
|
||||
import jp.juggler.subwaytooter.span.emojiSizeMode
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.util.data.notEmpty
|
||||
import java.util.*
|
||||
|
@ -72,6 +73,7 @@ object TootTextEncoder {
|
|||
accessInfo,
|
||||
mentions = status.mentions,
|
||||
authorDomain = status.account,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
).decodeHTML(status.content)
|
||||
)
|
||||
|
||||
|
@ -103,6 +105,7 @@ object TootTextEncoder {
|
|||
accessInfo,
|
||||
mentions = status.mentions,
|
||||
authorDomain = status.account,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
).decodeHTML(status.content)
|
||||
)
|
||||
|
||||
|
@ -271,6 +274,7 @@ object TootTextEncoder {
|
|||
context,
|
||||
accessInfo,
|
||||
authorDomain = who,
|
||||
emojiSizeMode = accessInfo.emojiSizeMode(),
|
||||
).decodeHTML(who.note)
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue