トゥート作成画面で残り文字数の計算がURLとメンションを考慮する

This commit is contained in:
tateisu 2019-01-19 03:05:14 +09:00
parent f8476fd7b8
commit f9c45bb194
4 changed files with 45 additions and 36 deletions

View File

@ -41,7 +41,6 @@ import okhttp3.RequestBody
import okio.BufferedSink
import org.json.JSONObject
import java.io.*
import java.util.regex.Pattern
class ActAccountSetting
: AppCompatActivity(), View.OnClickListener, CompoundButton.OnCheckedChangeListener {
@ -1159,7 +1158,7 @@ class ActAccountSetting
val sv = etNote.text.toString()
if(! bConfirmed) {
val length = countNoteText(sv)
val length = TootStatus.countText(sv)
if(length > max_length_note) {
AlertDialog.Builder(this)
.setMessage(
@ -1180,20 +1179,7 @@ class ActAccountSetting
updateCredential("note", EmojiDecoder.decodeShortCode(sv))
}
// Mastodon 2.7 でnoteの文字数計算が変わる
// https://github.com/tootsuite/mastodon/commit/45899cfa691b1e4f43da98c456ae8faa584eb437
private val reLinkUrl = Pattern.compile("""(https?://[\w/:%#@${'$'}&?!()\[\]~.=+\-]+)""")
private val reMention = Pattern.compile(
"""(?<=^|[^/\w\p{Pc}])@((\w+([\w.-]+\w+)?)(?:@[a-z0-9.\-]+[a-z0-9]+)?)""",
Pattern.CASE_INSENSITIVE
)
private val strUrlReplacement = (0 until 23).map { ' ' }.joinToString()
private fun countNoteText(s : String) : Int {
val s2 = s
.replaceAll(reLinkUrl, strUrlReplacement)
.replaceAll(reMention, "@\\2")
return s2.codePointCount(0, s2.length)
}
private fun sendLocked(willLocked : Boolean) {
updateCredential("locked", willLocked)

View File

@ -1116,32 +1116,40 @@ class ActPost : AppCompatActivity(),
private fun updateTextCount() {
var length = 0
var s = EmojiDecoder.decodeShortCode(etContent.text.toString())
length += s.codePointCount(0, s.length)
length += TootStatus.countText(
EmojiDecoder.decodeShortCode(etContent.text.toString())
)
s = if(cbContentWarning.isChecked)
EmojiDecoder.decodeShortCode(etContentWarning.text.toString())
else
""
length += s.codePointCount(0, s.length)
length += TootStatus.countText(
if(cbContentWarning.isChecked)
EmojiDecoder.decodeShortCode(etContentWarning.text.toString())
else
""
)
var max = getMaxCharCount()
if(cbEnquete.isChecked) {
max -= 150 // フレニコ固有。500-150で350になる
for(et in list_etChoice) {
s = EmojiDecoder.decodeShortCode(et.text.toString())
length += s.codePointCount(0, s.length)
length += TootStatus.countText(
EmojiDecoder.decodeShortCode(et.text.toString())
)
}
}
val remain = max - length
tvCharCount.text = Integer.toString(remain)
val color = getAttributeColor(
this,
if(remain < 0) R.attr.colorRegexFilterError else android.R.attr.textColorPrimary
tvCharCount.setTextColor(
getAttributeColor(
this,
if(remain < 0)
R.attr.colorRegexFilterError
else
android.R.attr.textColorPrimary
)
)
tvCharCount.setTextColor(color)
}
private fun updateContentWarning() {
@ -1826,12 +1834,12 @@ class ActPost : AppCompatActivity(),
val sb = StringBuilder(s_length)
var lastEnd = 0
while(m.find()) {
sb.append(s.substring(lastEnd,m.start()))
sb.append(s.substring(lastEnd, m.start()))
val escaped = m.group(1).encodeUTF8().encodeHex()
sb.append(escaped)
lastEnd = m.end()
}
if(lastEnd < s_length) sb.append(s.substring(lastEnd,s_length))
if(lastEnd < s_length) sb.append(s.substring(lastEnd, s_length))
return sb.toString()
}

View File

@ -632,15 +632,15 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
}
}
fun decreaseReaction(reaction:String?,byMe:Boolean, caller : String) : Boolean {
fun decreaseReaction(reaction : String?, byMe : Boolean, caller : String) : Boolean {
reaction ?: return false
MisskeyReaction.shortcodeMap[reaction] ?: return false
synchronized(this) {
if(byMe){
if( this.myReaction != reaction ){
if(byMe) {
if(this.myReaction != reaction) {
// 自分でリアクションしたらUIで更新した後にストリーミングイベントが届くことがある
return false
}
@ -648,7 +648,7 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
}
log.d("decreaseReaction noteId=$id byMe=$byMe caller=$caller")
// カウントを減らす
var map = this.reactionCounts
if(map == null) {
@ -987,6 +987,19 @@ class TootStatus(parser : TootParser, src : JSONObject) : TimelineItem() {
return null
}
private val reLinkUrl = Pattern.compile("""(https?://[\w/:%#@${'$'}&?!()\[\]~.=+\-]+)""")
private val reMention = Pattern.compile(
"""(?<=^|[^/\w\p{Pc}])@((\w+([\w.-]+\w+)?)(?:@[a-z0-9.\-]+[a-z0-9]+)?)""",
Pattern.CASE_INSENSITIVE
)
private val strUrlReplacement = (0 until 23).map { ' ' }.joinToString()
fun countText(s : String) : Int {
return s
.replaceAll(reLinkUrl, strUrlReplacement)
.replaceAll(reMention, "@$2")
.codePointCount()
}
}
}

View File

@ -146,6 +146,8 @@ fun String.encodeUTF8() = this.toByteArray(charsetUTF8)
fun ByteArray.decodeUTF8() = this.toString(charsetUTF8)
fun String.codePointCount(beginIndex:Int = 0): Int = this.codePointCount(beginIndex, this.length)
// 16進ダンプ
fun ByteArray.encodeHex() : String {
val sb = StringBuilder()