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