(マストドン2.4以降)アカウント表示名、ノート、フィールドのカスタム絵文字対応

This commit is contained in:
tateisu 2018-05-06 20:08:06 +09:00
parent 36e2bf8e68
commit 3fc92b35f3
6 changed files with 97 additions and 42 deletions

View File

@ -12,8 +12,8 @@ android {
minSdkVersion 21
targetSdkVersion 27
versionCode 244
versionName "2.4.4"
versionCode 245
versionName "2.4.5"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
// https://stackoverflow.com/questions/47791227/java-lang-illegalstateexception-dex-archives-setting-dex-extension-only-for

View File

@ -799,8 +799,9 @@ class ActAccountSetting
val decodeOptions = DecodeOptions(
context = this@ActAccountSetting,
linkHelper = account,
emojiMapProfile = src.profile_emojis,
linkHelper = account
emojiMapCustom = src.custom_emojis
)
val display_name = src.display_name
@ -828,59 +829,64 @@ class ActAccountSetting
if(src.source?.fields != null) {
val fields = src.source.fields
listEtFieldName.forEachIndexed { i, et ->
et.setText(
decodeOptions.decodeEmoji(
when {
i >= fields.size -> ""
else -> fields[i].first
}
)
val text = decodeOptions.decodeEmoji(
when {
i >= fields.size -> ""
else -> fields[i].first
}
)
et.setText( text )
et.isEnabled = true
val invalidator = NetworkEmojiInvalidator(et.handler,et)
invalidator.register(text)
}
listEtFieldValue.forEachIndexed { i, et ->
et.setText(
decodeOptions.decodeEmoji(
when {
i >= fields.size -> ""
else -> fields[i].second
}
)
val text =decodeOptions.decodeEmoji(
when {
i >= fields.size -> ""
else -> fields[i].second
}
)
et.setText( text )
et.isEnabled = true
val invalidator = NetworkEmojiInvalidator(et.handler,et)
invalidator.register(text)
}
} else {
val fields = src.fields
listEtFieldName.forEachIndexed { i, et ->
et.setText(
decodeOptions.decodeEmoji(
when {
fields == null || i >= fields.size -> ""
else -> fields[i].first
}
)
val text = decodeOptions.decodeEmoji(
when {
fields == null || i >= fields.size -> ""
else -> fields[i].first
}
)
et.setText(text)
et.isEnabled = true
val invalidator = NetworkEmojiInvalidator(et.handler,et)
invalidator.register(text)
}
listEtFieldValue.forEachIndexed { i, et ->
et.text = decodeOptions.decodeHTML(
val text = decodeOptions.decodeHTML(
when {
fields == null || i >= fields.size -> ""
else -> fields[i].second
}
)
et.text = text
et.isEnabled = true
val invalidator = NetworkEmojiInvalidator(et.handler,et)
invalidator.register(text)
}
}
} finally {
profile_busy = false
}
}
internal fun updateCredential(key : String, value : Any) {

View File

@ -231,7 +231,9 @@ internal class ViewHolderHeaderProfile(
context = activity,
decodeEmoji = true,
linkHelper = access_info,
short = true
short = true,
emojiMapCustom = who.custom_emojis,
emojiMapProfile = who.profile_emojis
)
val content_color = column.content_color
@ -248,24 +250,31 @@ internal class ViewHolderHeaderProfile(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
val nameText = decodeOptions.decodeEmoji(item.first)
val nameInvalidator = NetworkEmojiInvalidator(activity.handler, nameView)
nameInvalidator.register(nameText)
nameLp.topMargin = (density * 6f).toInt()
nameView.layoutParams = nameLp
nameView.text = decodeOptions.decodeEmoji(item.first)
nameView.text = nameText
nameView.setTextColor(c)
nameView.typeface = nameTypeface
nameView.movementMethod = MyLinkMovementMethod
llFields.addView(nameView)
//
// 値の方はHTMLエンコードされている
val valueView = MyTextView(activity)
val valueLp = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
val valueText = decodeOptions.decodeHTML(item.second)
val valueInvalidator = NetworkEmojiInvalidator(activity.handler, valueView)
valueInvalidator.register(valueText)
valueLp.startMargin = (density * 32f).toInt()
valueView.layoutParams = valueLp
valueView.text =
decodeOptions.decodeHTML(item.second) // 値の方はHTML文字参照のエンコードが行われている
valueView.text = valueText
valueView.setTextColor(c)
valueView.typeface = valueTypeface
valueView.movementMethod = MyLinkMovementMethod

View File

@ -82,10 +82,13 @@ open class TootAccount(
val fields : ArrayList<Pair<String, String>>?
val custom_emojis : java.util.HashMap<String, CustomEmoji>?
init {
var sv : String?
// 絵文字データは先に読んでおく
this.custom_emojis = parseMapOrNull(::CustomEmoji, src.optJSONArray("emojis") )
this.profile_emojis = parseMapOrNull(::NicoProfileEmoji, src.optJSONArray("profile_emojis"))
// 疑似アカウントにacctとusernameだけ
@ -104,7 +107,9 @@ open class TootAccount(
parser.linkHelper,
short = true,
decodeEmoji = true,
emojiMapProfile = this.profile_emojis
emojiMapProfile = this.profile_emojis,
emojiMapCustom = this.custom_emojis,
unwrapEmojiImageTag = true
).decodeHTML(this.note)
this.source = parseSource(src.optJSONObject("source"))
@ -218,7 +223,11 @@ open class TootAccount(
val sv = reWhitespace.matcher(display_name).replaceAll(" ")
// decode emoji code
return DecodeOptions(context, emojiMapProfile = profile_emojis).decodeEmoji(sv)
return DecodeOptions(
context,
emojiMapProfile = profile_emojis,
emojiMapCustom = custom_emojis
).decodeEmoji(sv)
}
companion object {

View File

@ -19,7 +19,8 @@ class DecodeOptions(
var linkTag : Any? = null,
var emojiMapCustom : HashMap<String, CustomEmoji>? = null,
var emojiMapProfile : HashMap<String, NicoProfileEmoji>? = null,
var highlightTrie : WordTrieTree? = null
var highlightTrie : WordTrieTree? = null,
var unwrapEmojiImageTag :Boolean = false
) {
internal fun isMediaAttachment(url : String?) : Boolean {

View File

@ -35,6 +35,8 @@ object HTMLDecoder {
private val reTag = Pattern.compile("<(/?)(\\w+)")
private val reTagEnd = Pattern.compile("(/?)>$")
private val reHref = Pattern.compile("\\bhref=\"([^\"]*)\"")
private val reAttribute = Pattern.compile("\\s+([A-Za-z0-9:_-]+)\\s*=([\"'])([^>]*?)\\2")
private val reShortcode = Pattern.compile(":[A-Za-z0-9_-]+:")
private val block_tag : HashSet<String> by lazy {
val set = HashSet<String>()
@ -286,7 +288,24 @@ object HTMLDecoder {
}
if("img" == tag) {
sb_tmp.append("<img/>")
var replaced = false
if(options.unwrapEmojiImageTag) {
val attrs = parseAttributes(text)
val cssClass = attrs["class"]
val title = attrs["title"]
if(cssClass != null
&& title != null
&& cssClass.contains("emojione")
&& reShortcode.matcher(title).find()
) {
replaced = true
sb_tmp.append(options.decodeEmoji(title))
}
}
if(! replaced) {
sb_tmp.append("<img/>")
}
} else {
for(child in child_nodes) {
child.encodeSpan(options, sb_tmp)
@ -299,9 +318,9 @@ object HTMLDecoder {
val start = sb.length
sb.append(
encodeUrl(options,sb_tmp.toString(),href)
encodeUrl(options, sb_tmp.toString(), href)
)
val end = sb.length
if(end > start) {
@ -368,7 +387,6 @@ object HTMLDecoder {
}
}
private fun encodeUrl(
options : DecodeOptions,
display_url : String,
@ -379,7 +397,7 @@ object HTMLDecoder {
if(context == null || ! options.short) {
return display_url
}
if(! display_url.startsWith("http")) {
if(display_url.startsWith("@") && href != null && Pref.bpMentionFullAcct(App1.pref)) {
// メンションをfull acct にする
@ -392,9 +410,9 @@ object HTMLDecoder {
return display_url
}
if( options.isMediaAttachment(href)) {
if(options.isMediaAttachment(href)) {
val sb = SpannableStringBuilder()
sb.append(href)
val start = 0
@ -431,7 +449,19 @@ object HTMLDecoder {
}
}
fun decodeHTML( options : DecodeOptions, src : String? ) : SpannableStringBuilder {
// split attributes
private fun parseAttributes(text : String) : HashMap<String, String> {
val dst = HashMap<String, String>()
val m = reAttribute.matcher(text)
while(m.find()) {
val name = m.group(1).toLowerCase()
val value = decodeEntity(m.group(3))
dst[name] = value
}
return dst
}
fun decodeHTML(options : DecodeOptions, src : String?) : SpannableStringBuilder {
val sb = SpannableStringBuilder()