リファクタ
This commit is contained in:
parent
e207421fcd
commit
e45ab4600e
|
@ -25,6 +25,12 @@ open class TootAccount(parser : TootParser, src : JSONObject) {
|
|||
|
||||
internal val reWhitespace : Pattern = Pattern.compile("[\\s\\t\\x0d\\x0a]+")
|
||||
|
||||
// メンション @username @username@host
|
||||
internal val reMention = Pattern.compile(
|
||||
"""\A@([a-z0-9_]+(?:[a-z0-9_.-]+[a-z0-9_]+)?)(?:@([A-Za-z0-9][A-Za-z0-9._-]+))?"""
|
||||
, Pattern.CASE_INSENSITIVE
|
||||
)
|
||||
|
||||
// host, user ,(instance)
|
||||
internal val reAccountUrl : Pattern =
|
||||
Pattern.compile("""\Ahttps://([A-Za-z0-9][A-Za-z0-9._-]+)/@([\w][\w.-]+)(?:@([A-Za-z0-9][A-Za-z0-9._-]+))?(?=\z|[?#])""")
|
||||
|
|
|
@ -15,6 +15,7 @@ import jp.juggler.subwaytooter.App1
|
|||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.api.entity.EntityIdLong
|
||||
import jp.juggler.subwaytooter.api.entity.TootAccount
|
||||
import jp.juggler.subwaytooter.api.entity.TootMention
|
||||
import jp.juggler.subwaytooter.span.EmojiImageSpan
|
||||
import jp.juggler.subwaytooter.span.HighlightSpan
|
||||
|
@ -1367,14 +1368,9 @@ object MisskeyMarkdownDecoder {
|
|||
addParser("検Ss", searchParser)
|
||||
addParser("?", linkParser)
|
||||
|
||||
// メンション @username @username@host
|
||||
val reMention = Pattern.compile(
|
||||
"""\A@([a-z0-9_]+(?:[a-z0-9_.-]+[a-z0-9_]+)?)(?:@([A-Za-z0-9][A-Za-z0-9._-]+))?"""
|
||||
, Pattern.CASE_INSENSITIVE
|
||||
)
|
||||
|
||||
|
||||
addParser("@", {
|
||||
val matcher = remainMatcher(reMention)
|
||||
val matcher = remainMatcher(TootAccount.reMention)
|
||||
when {
|
||||
! matcher.find() -> null
|
||||
else -> makeDetected(
|
||||
|
|
|
@ -344,11 +344,14 @@ class PostHelper(
|
|||
}
|
||||
)
|
||||
} else {
|
||||
val localVis = visibility_checked.strMisskey.replace("^local-".toRegex(),"")
|
||||
if( localVis != visibility_checked.strMisskey){
|
||||
val localVis = visibility_checked.strMisskey.replace(
|
||||
"^local-".toRegex(),
|
||||
""
|
||||
)
|
||||
if(localVis != visibility_checked.strMisskey) {
|
||||
json.put("localOnly", true)
|
||||
json.put("visibility", localVis)
|
||||
}else {
|
||||
} else {
|
||||
json.put("visibility", visibility_checked.strMisskey)
|
||||
}
|
||||
}
|
||||
|
@ -365,9 +368,9 @@ class PostHelper(
|
|||
}
|
||||
|
||||
if(in_reply_to_id != null) {
|
||||
if( useQuotedRenote){
|
||||
if(useQuotedRenote) {
|
||||
json.put("renoteId", in_reply_to_id.toString())
|
||||
}else{
|
||||
} else {
|
||||
json.put("replyId", in_reply_to_id.toString())
|
||||
}
|
||||
}
|
||||
|
@ -481,7 +484,7 @@ class PostHelper(
|
|||
}
|
||||
|
||||
result = if(isMisskey) {
|
||||
log.d("misskey json %s",body_string)
|
||||
log.d("misskey json %s", body_string)
|
||||
|
||||
client.request("/api/notes/create", request_builder)
|
||||
} else {
|
||||
|
@ -567,59 +570,55 @@ class PostHelper(
|
|||
}
|
||||
|
||||
private val proc_text_changed = object : Runnable {
|
||||
|
||||
override fun run() {
|
||||
val et = this@PostHelper.et
|
||||
if(et == null || callback2?.canOpenPopup() != true) {
|
||||
if(et == null // EditTextを特定できない
|
||||
|| et.selectionStart != et.selectionEnd // 範囲選択中
|
||||
|| callback2?.canOpenPopup() != true // 何らかの理由でポップアップが許可されていない
|
||||
) {
|
||||
closeAcctPopup()
|
||||
return
|
||||
}
|
||||
|
||||
var start = et.selectionStart
|
||||
val end = et.selectionEnd
|
||||
if(start != end) {
|
||||
closeAcctPopup()
|
||||
return
|
||||
}
|
||||
val src = et.text.toString()
|
||||
checkMention(et, et.text.toString())
|
||||
}
|
||||
|
||||
private fun checkMention(et : MyEditText, src : String) {
|
||||
|
||||
var count_atMark = 0
|
||||
val pos_atMark = IntArray(2)
|
||||
while(true) {
|
||||
if(count_atMark >= 2) break
|
||||
|
||||
if(start == 0) break
|
||||
val c = src[start - 1]
|
||||
val end = et.selectionEnd
|
||||
var start : Int = - 1
|
||||
var i = end
|
||||
while(i > 0) {
|
||||
val c = src[i - 1]
|
||||
|
||||
if(c == '@') {
|
||||
-- start
|
||||
pos_atMark[count_atMark ++] = start
|
||||
continue
|
||||
start = -- i
|
||||
if(++ count_atMark >= 2) break else continue
|
||||
} else if('0' <= c && c <= '9'
|
||||
|| 'A' <= c && c <= 'Z'
|
||||
|| 'a' <= c && c <= 'z'
|
||||
|| c == '_' || c == '-' || c == '.') {
|
||||
-- start
|
||||
|| c == '_' || c == '-' || c == '.'
|
||||
) {
|
||||
-- i
|
||||
continue
|
||||
}
|
||||
// その他の文字種が出たら探索打ち切り
|
||||
break
|
||||
}
|
||||
// 登場した@の数
|
||||
start = when(count_atMark) {
|
||||
1 -> pos_atMark[0]
|
||||
2 -> pos_atMark[1]
|
||||
|
||||
else -> {
|
||||
// 次はAcctじゃなくてHashtagの補完を試みる
|
||||
checkTag()
|
||||
return
|
||||
}
|
||||
|
||||
if(start == - 1) {
|
||||
checkTag(et, src)
|
||||
return
|
||||
}
|
||||
// 最低でも2文字ないと補完しない
|
||||
|
||||
// 最低でも2文字ないと補完しない
|
||||
if(end - start < 2) {
|
||||
closeAcctPopup()
|
||||
return
|
||||
}
|
||||
|
||||
val limit = 100
|
||||
val s = src.substring(start, end)
|
||||
val acct_list = AcctSet.searchPrefix(s, limit)
|
||||
|
@ -631,23 +630,20 @@ class PostHelper(
|
|||
}
|
||||
}
|
||||
|
||||
private fun checkTag() {
|
||||
val et = this@PostHelper.et ?: return
|
||||
private fun checkTag(et : MyEditText, src : String) {
|
||||
|
||||
val end = et.selectionEnd
|
||||
|
||||
val src = et.text.toString()
|
||||
val last_sharp = src.lastIndexOf('#', end - 1)
|
||||
|
||||
if(last_sharp == - 1 || end - last_sharp < 2) {
|
||||
checkEmoji()
|
||||
checkEmoji(et, src)
|
||||
return
|
||||
}
|
||||
|
||||
val part = src.substring(last_sharp + 1, end)
|
||||
if(reCharsNotTag.matcher(part).find()) {
|
||||
// warning.d( "checkTag: character not tag in string %s", part );
|
||||
checkEmoji()
|
||||
checkEmoji(et, src)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -662,112 +658,121 @@ class PostHelper(
|
|||
}
|
||||
}
|
||||
|
||||
private fun checkEmoji() {
|
||||
val et = this@PostHelper.et ?: return
|
||||
private fun checkEmoji(et : MyEditText, src : String) {
|
||||
|
||||
val end = et.selectionEnd
|
||||
val src = et.text.toString()
|
||||
val last_colon = src.lastIndexOf(':', end - 1)
|
||||
|
||||
if(last_colon == - 1 || end - last_colon < 1) {
|
||||
closeAcctPopup()
|
||||
return
|
||||
}
|
||||
val part = src.substring(last_colon + 1, end)
|
||||
|
||||
if(reCharsNotEmoji.matcher(part).find()) {
|
||||
log.d("checkEmoji: character not short code in string.")
|
||||
closeAcctPopup()
|
||||
return
|
||||
}
|
||||
|
||||
// : の手前は始端か改行か空白でなければならない
|
||||
if(! EmojiDecoder.canStartShortCode(src, last_colon)) {
|
||||
// : の手前は始端か改行か空白でなければならない
|
||||
log.d("checkEmoji: invalid character before shortcode.")
|
||||
closeAcctPopup()
|
||||
return
|
||||
}
|
||||
|
||||
val part = src.substring(last_colon + 1, end)
|
||||
|
||||
if(part.isEmpty()) {
|
||||
// :を入力した直後は候補は0で、「閉じる」と「絵文字を選ぶ」だけが表示されたポップアップを出す
|
||||
openPopup()?.setList(
|
||||
et, last_colon, end, null, picker_caption_emoji, open_picker_emoji
|
||||
)
|
||||
} else {
|
||||
|
||||
val code_list = ArrayList<CharSequence>()
|
||||
val limit = 100
|
||||
|
||||
// カスタム絵文字を検索
|
||||
val instance = this@PostHelper.instance
|
||||
if(instance != null && instance.isNotEmpty()) {
|
||||
val custom_list = App1.custom_emoji_lister.getListWithAliases(
|
||||
instance,
|
||||
isMisskey,
|
||||
onEmojiListLoad
|
||||
)
|
||||
if(custom_list != null) {
|
||||
val needle = src.substring(last_colon + 1, end)
|
||||
return
|
||||
}
|
||||
|
||||
if(reCharsNotEmoji.matcher(part).find()) {
|
||||
// 範囲内に絵文字に使えない文字がある
|
||||
closeAcctPopup()
|
||||
return
|
||||
}
|
||||
|
||||
val code_list = ArrayList<CharSequence>()
|
||||
val limit = 100
|
||||
|
||||
// カスタム絵文字の候補を部分一致検索
|
||||
code_list.addAll(customEmojiCodeList(this@PostHelper.instance, limit, part))
|
||||
|
||||
// 通常の絵文字を部分一致で検索
|
||||
val remain = limit - code_list.size
|
||||
if(remain > 0) {
|
||||
val s = src.substring(last_colon + 1, end).toLowerCase().replace('-', '_')
|
||||
val matches = EmojiDecoder.searchShortCode(activity, s, remain)
|
||||
log.d("checkEmoji: search for %s, result=%d", s, matches.size)
|
||||
code_list.addAll(matches)
|
||||
}
|
||||
|
||||
openPopup()?.setList(
|
||||
et,
|
||||
last_colon,
|
||||
end,
|
||||
code_list,
|
||||
picker_caption_emoji,
|
||||
open_picker_emoji
|
||||
)
|
||||
}
|
||||
|
||||
// カスタム絵文字の候補を作る
|
||||
private fun customEmojiCodeList(
|
||||
instance : String?,
|
||||
limit : Int,
|
||||
needle : String
|
||||
) : ArrayList<CharSequence> {
|
||||
val dst = ArrayList<CharSequence>()
|
||||
|
||||
if(instance?.isNotEmpty() == true) {
|
||||
|
||||
val custom_list = App1.custom_emoji_lister.getListWithAliases(
|
||||
instance,
|
||||
isMisskey,
|
||||
onEmojiListLoad
|
||||
)
|
||||
|
||||
if(custom_list != null) {
|
||||
|
||||
for(item in custom_list) {
|
||||
if(dst.size >= limit) break
|
||||
if(! item.shortcode.contains(needle)) continue
|
||||
|
||||
for(item in custom_list) {
|
||||
if(code_list.size >= limit) break
|
||||
if(! item.shortcode.contains(needle)) continue
|
||||
|
||||
val sb = SpannableStringBuilder()
|
||||
sb.append(' ')
|
||||
val sb = SpannableStringBuilder()
|
||||
sb.append(' ')
|
||||
sb.setSpan(
|
||||
NetworkEmojiSpan(item.url),
|
||||
0,
|
||||
sb.length,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
sb.append(' ')
|
||||
if(item.alias != null) {
|
||||
val start = sb.length
|
||||
sb.append(":")
|
||||
sb.append(item.alias)
|
||||
sb.append(": → ")
|
||||
sb.setSpan(
|
||||
NetworkEmojiSpan(item.url),
|
||||
0,
|
||||
ForegroundColorSpan(
|
||||
Styler.getAttributeColor(
|
||||
activity,
|
||||
R.attr.colorTimeSmall
|
||||
)
|
||||
),
|
||||
start,
|
||||
sb.length,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
sb.append(' ')
|
||||
if(item.alias != null) {
|
||||
val start = sb.length
|
||||
sb.append(":")
|
||||
sb.append(item.alias)
|
||||
sb.append(": → ")
|
||||
sb.setSpan(
|
||||
ForegroundColorSpan(
|
||||
Styler.getAttributeColor(
|
||||
activity,
|
||||
R.attr.colorTimeSmall
|
||||
)
|
||||
),
|
||||
start,
|
||||
sb.length,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
sb.append(':')
|
||||
sb.append(item.shortcode)
|
||||
sb.append(':')
|
||||
|
||||
code_list.add(sb)
|
||||
}
|
||||
|
||||
sb.append(':')
|
||||
sb.append(item.shortcode)
|
||||
sb.append(':')
|
||||
|
||||
dst.add(sb)
|
||||
}
|
||||
}
|
||||
|
||||
// 通常の絵文字を部分一致で検索
|
||||
val remain = limit - code_list.size
|
||||
if(remain > 0) {
|
||||
val s = src.substring(last_colon + 1, end).toLowerCase().replace('-', '_')
|
||||
val matches = EmojiDecoder.searchShortCode(activity, s, remain)
|
||||
log.d("checkEmoji: search for %s, result=%d", s, matches.size)
|
||||
code_list.addAll(matches)
|
||||
|
||||
}
|
||||
|
||||
openPopup()?.setList(
|
||||
et,
|
||||
last_colon,
|
||||
end,
|
||||
code_list,
|
||||
picker_caption_emoji,
|
||||
open_picker_emoji
|
||||
)
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue