From 0176f18ef5f9aeb533429f12057e85f1f41bb465 Mon Sep 17 00:00:00 2001 From: tateisu Date: Tue, 9 Oct 2018 15:39:05 +0900 Subject: [PATCH] =?UTF-8?q?profile=20field=20=E3=81=AEverified=5Fat?= =?UTF-8?q?=E3=81=8C=E3=81=82=E3=82=8C=E3=81=B0=E8=A1=A8=E7=A4=BA=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../juggler/subwaytooter/ActAccountSetting.kt | 8 +- .../jp/juggler/subwaytooter/ItemViewHolder.kt | 36 +++- .../subwaytooter/ViewHolderHeaderProfile.kt | 27 ++- .../subwaytooter/api/entity/TootAccount.kt | 189 ++++++++++-------- app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 6 files changed, 167 insertions(+), 95 deletions(-) diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.kt b/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.kt index b15d76b3..2ce302a6 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.kt @@ -892,7 +892,7 @@ class ActAccountSetting val text = decodeOptionsNoCustomEmoji.decodeEmoji( when { i >= fields.size -> "" - else -> fields[i].first + else -> fields[i].name } ) et.setText(text) @@ -905,7 +905,7 @@ class ActAccountSetting val text = decodeOptions.decodeEmoji( when { i >= fields.size -> "" - else -> fields[i].second + else -> fields[i].value } ) et.setText(text) @@ -921,7 +921,7 @@ class ActAccountSetting val text = decodeOptionsNoCustomEmoji.decodeEmoji( when { fields == null || i >= fields.size -> "" - else -> fields[i].first + else -> fields[i].name } ) et.setText(text) @@ -934,7 +934,7 @@ class ActAccountSetting val text = decodeOptions.decodeHTML( when { fields == null || i >= fields.size -> "" - else -> fields[i].second + else -> fields[i].value } ) et.text = text diff --git a/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.kt b/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.kt index 09edc3aa..fdf708d9 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ItemViewHolder.kt @@ -27,6 +27,7 @@ import jp.juggler.subwaytooter.api.entity.* import jp.juggler.subwaytooter.dialog.ActionsDialog import jp.juggler.subwaytooter.dialog.DlgConfirm import jp.juggler.subwaytooter.span.EmojiImageSpan +import jp.juggler.subwaytooter.span.NetworkEmojiSpan import jp.juggler.subwaytooter.table.* import jp.juggler.subwaytooter.util.* import jp.juggler.subwaytooter.view.* @@ -449,12 +450,45 @@ internal class ItemViewHolder( private fun showConversationIcons(accounts : ArrayList) { if(accounts.isEmpty()) return -// 消えてしまったりちらついたりするので保留 + // 絵文字スパンにしてもやはり消えたりちらついたりする。なんでだ。 // val density = llExtra.resources.displayMetrics.density // val wh = (activity.avatarIconSize * 0.75f + 0.5f).toInt() // val me = (density * 3f + 0.5f).toInt() // val mt = (density * 3f + 0.5f).toInt() // +// val lp = LinearLayout.LayoutParams( +// LinearLayout.LayoutParams.MATCH_PARENT, +// LinearLayout.LayoutParams.WRAP_CONTENT +// ) +// lp.topMargin = mt +// +// val b = MyTextView(activity) +// b.layoutParams = lp +// b.isAllCaps = false +// llExtra.addView(b) +// +// val sb = SpannableStringBuilder() +// val invalidator = NetworkEmojiInvalidator(activity.handler, b) +// extra_invalidator_list.add(invalidator) +// for(ar in accounts) { +// val a = ar.get() +// val url = access_info.supplyBaseUrl(a.avatar_static) +// if(url?.isNotEmpty() == true) { +// if(sb.isNotEmpty()) sb.append(' ') +// val start = sb.length +// sb.append(a.acct) +// val end = sb.length +// sb.setSpan( +// NetworkEmojiSpan(url) +// , start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE +// ) +// } +// } +// b.text = sb +// invalidator.register(sb) + +// 消えてしまったりちらついたりするので保留 +// // val llIconBar = FlexboxLayout(activity) // val boxLp = LinearLayout.LayoutParams( // LinearLayout.LayoutParams.MATCH_PARENT, diff --git a/app/src/main/java/jp/juggler/subwaytooter/ViewHolderHeaderProfile.kt b/app/src/main/java/jp/juggler/subwaytooter/ViewHolderHeaderProfile.kt index abae1f3c..6316ea75 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/ViewHolderHeaderProfile.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/ViewHolderHeaderProfile.kt @@ -1,9 +1,11 @@ package jp.juggler.subwaytooter +import android.graphics.Color import android.graphics.Typeface import android.support.v4.view.ViewCompat import android.text.SpannableStringBuilder import android.text.Spanned +import android.text.style.ForegroundColorSpan import android.view.View import android.widget.* import jp.juggler.emoji.EmojiMap201709 @@ -273,7 +275,7 @@ internal class ViewHolderHeaderProfile( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT ) - val nameText = decodeOptionsNoCustomEmoji.decodeEmoji(item.first) + val nameText = decodeOptionsNoCustomEmoji.decodeEmoji(item.name) val nameInvalidator = NetworkEmojiInvalidator(activity.handler, nameView) nameInvalidator.register(nameText) @@ -291,7 +293,23 @@ internal class ViewHolderHeaderProfile( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT ) - val valueText = decodeOptions.decodeHTML(item.second) + + val valueText = decodeOptions.decodeHTML(item.value) + if(item.verified_at > 0L){ + valueText.append('\n') + + val start = valueText.length + valueText.append( activity.getString(R.string.verified_at)) + valueText.append( ": ") + valueText.append(TootStatus.formatTime(activity,item.verified_at,false)) + val end = valueText.length + + valueText.setSpan( + ForegroundColorSpan(Color.BLACK or 0x7fbc99) + ,start,end,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE + ) + } + val valueInvalidator = NetworkEmojiInvalidator(activity.handler, valueView) valueInvalidator.register(valueText) @@ -301,6 +319,11 @@ internal class ViewHolderHeaderProfile( valueView.setTextColor(c) valueView.typeface = valueTypeface valueView.movementMethod = MyLinkMovementMethod + + if(item.verified_at > 0L){ + valueView.setBackgroundColor(0x337fbc99) + } + llFields.addView(valueView) } diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.kt b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.kt index 03eb393e..752bae1f 100644 --- a/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.kt +++ b/app/src/main/java/jp/juggler/subwaytooter/api/entity/TootAccount.kt @@ -15,6 +15,105 @@ import java.util.regex.Pattern open class TootAccount(parser : TootParser, src : JSONObject) { + class Field( + val name :String, + val value :String, + val verified_at: Long // 0L if not verified + ) + + companion object { + private val log = LogCategory("TootAccount") + + internal val reWhitespace:Pattern = Pattern.compile("[\\s\\t\\x0d\\x0a]+") + + // host, user ,(instance) + internal val reAccountUrl :Pattern = + Pattern.compile("\\Ahttps://([A-Za-z0-9._-]+)/@([A-Za-z0-9_]+(?:@[A-Za-z0-9._-]+)?)(?:\\z|[?#])") + + fun getAcctFromUrl(url:String):String?{ + val m = reAccountUrl.matcher(url) + return if(m.find()){ + val host = m.group(1) + val user = m.group(2).unescapeUri() + val instance = m.groupOrNull(3)?.unescapeUri() + if( instance?.isNotEmpty() == true){ + "$user@$instance" + }else{ + "$user@$host" + } + }else{ + null + } + } + + private fun parseSource(src : JSONObject?) : Source? { + src ?: return null + return try { + Source(src) + } catch(ex : Throwable) { + log.trace(ex) + log.e("parseSource failed.") + null + } + } + + // Tootsearch用。URLやUriを使ってアカウントのインスタンス名を調べる + fun findHostFromUrl(acct : String?, accessHost : String?, url : String?) : String? { + + // acctから調べる + if(acct != null) { + val pos = acct.indexOf('@') + if(pos != - 1) { + val host = acct.substring(pos + 1) + if(host.isNotEmpty()) return host.toLowerCase() + } + } + + // accessHostから調べる + if(accessHost != null) { + return accessHost + } + + // URLから調べる + if(url != null) { + try { + // たぶんどんなURLでもauthorityの部分にホスト名が来るだろう(慢心) + val host = Uri.parse(url).authority + if( host?.isNotEmpty() == true){ + return host.toLowerCase() + } + log.e("findHostFromUrl: can't parse host from URL $url") + } catch(ex : Throwable) { + log.e(ex, "findHostFromUrl: can't parse host from URL $url") + } + } + + return null + } + + fun parseFields(src : JSONArray?) : ArrayList? { + src ?: return null + val dst = ArrayList() + for(i in 0 until src.length()) { + val item = src.optJSONObject(i) ?: continue + val name = item.parseString("name") ?: continue + val value = item.parseString("value") ?: continue + val svVerifiedAt = item.parseString("verified_at") + val verifiedAt = when(svVerifiedAt){ + null -> 0L + else-> TootStatus.parseTime(svVerifiedAt) + } + dst.add( Field(name,value,verifiedAt)) + } + return if(dst.isEmpty()) { + null + } else { + dst + } + } + } + + //URL of the user's profile page (can be remote) // https://mastodon.juggler.jp/@tateisu // 疑似アカウントではnullになります @@ -76,7 +175,7 @@ open class TootAccount(parser : TootParser, src : JSONObject) { val moved : TootAccount? get() = movedRef?.get() - val fields : ArrayList>? + val fields : ArrayList? val custom_emojis : java.util.HashMap? @@ -274,7 +373,7 @@ open class TootAccount(parser : TootParser, src : JSONObject) { val note : String? // 2.4.0 から? - val fields : ArrayList>? + val fields : ArrayList? init { this.privacy = src.parseString("privacy") @@ -301,92 +400,6 @@ open class TootAccount(parser : TootParser, src : JSONObject) { ).decodeEmoji(sv) } - companion object { - private val log = LogCategory("TootAccount") - - internal val reWhitespace:Pattern = Pattern.compile("[\\s\\t\\x0d\\x0a]+") - - // host, user ,(instance) - internal val reAccountUrl :Pattern = - Pattern.compile("\\Ahttps://([A-Za-z0-9._-]+)/@([A-Za-z0-9_]+(?:@[A-Za-z0-9._-]+)?)(?:\\z|[?#])") - - fun getAcctFromUrl(url:String):String?{ - val m = reAccountUrl.matcher(url) - return if(m.find()){ - val host = m.group(1) - val user = m.group(2).unescapeUri() - val instance = m.groupOrNull(3)?.unescapeUri() - if( instance?.isNotEmpty() == true){ - "$user@$instance" - }else{ - "$user@$host" - } - }else{ - null - } - } - - private fun parseSource(src : JSONObject?) : Source? { - src ?: return null - return try { - Source(src) - } catch(ex : Throwable) { - log.trace(ex) - log.e("parseSource failed.") - null - } - } - - // Tootsearch用。URLやUriを使ってアカウントのインスタンス名を調べる - fun findHostFromUrl(acct : String?, accessHost : String?, url : String?) : String? { - - // acctから調べる - if(acct != null) { - val pos = acct.indexOf('@') - if(pos != - 1) { - val host = acct.substring(pos + 1) - if(host.isNotEmpty()) return host.toLowerCase() - } - } - - // accessHostから調べる - if(accessHost != null) { - return accessHost - } - - // URLから調べる - if(url != null) { - try { - // たぶんどんなURLでもauthorityの部分にホスト名が来るだろう(慢心) - val host = Uri.parse(url).authority - if( host?.isNotEmpty() == true){ - return host.toLowerCase() - } - log.e("findHostFromUrl: can't parse host from URL $url") - } catch(ex : Throwable) { - log.e(ex, "findHostFromUrl: can't parse host from URL $url") - } - } - - return null - } - - fun parseFields(src : JSONArray?) : ArrayList>? { - src ?: return null - val dst = ArrayList>() - for(i in 0 until src.length()) { - val item = src.optJSONObject(i) ?: continue - val k = item.parseString("name") ?: continue - val v = item.parseString("value") ?: continue - dst.add(Pair(k, v)) - } - return if(dst.isEmpty()) { - null - } else { - dst - } - } - } var _orderId : EntityId? = null diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 8af34af3..650e8fa8 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -773,5 +773,6 @@ 周辺のアカウントTL… (Mastodon2.6以降) …周辺のアカウントTL %1$s周辺のアカウントTL + 検証時刻 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 50dd37aa..083c36a0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -792,5 +792,6 @@ Account TL around this toot… (Mastodon 2.6) Account timeline around … Account timeline around %1$s + verified at