絵文字のショートコード解釈時にカスタム絵文字を優先する

This commit is contained in:
tateisu 2018-03-11 01:22:13 +09:00
parent 0da675f4e0
commit 3b8cbbe030
5 changed files with 79 additions and 53 deletions

View File

@ -634,6 +634,8 @@ class ActMain : AppCompatActivity()
post_helper.bNSFW = false post_helper.bNSFW = false
post_helper.in_reply_to_id = - 1L post_helper.in_reply_to_id = - 1L
post_helper.attachment_list = null post_helper.attachment_list = null
post_helper.emojiMapCustom = App1.custom_emoji_lister.getMap(account.host)
etQuickToot.hideKeyboard() etQuickToot.hideKeyboard()
@ -1599,7 +1601,7 @@ class ActMain : AppCompatActivity()
try { try {
val dataId = sv.substring(3).toLong(10) val dataId = sv.substring(3).toLong(10)
val sa = SavedAccount.loadAccount(this@ActMain, dataId) val sa = SavedAccount.loadAccount(this@ActMain, dataId)
?: return TootApiResult("missing account db_id=" + dataId) ?: return TootApiResult("missing account db_id=$dataId")
this.sa = sa this.sa = sa
client.account = sa client.account = sa
} catch(ex : Throwable) { } catch(ex : Throwable) {

View File

@ -549,11 +549,11 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
// 今回メンションを追加する? // 今回メンションを追加する?
val who_acct = account.getFullAcct(reply_status.account) val who_acct = account.getFullAcct(reply_status.account)
if(mention_list.contains("@" + who_acct)) { if(mention_list.contains("@$who_acct")) {
// 既に含まれている // 既に含まれている
} else if(! account.isMe(reply_status.account) || mention_list.isEmpty()) { } else if(! account.isMe(reply_status.account) || mention_list.isEmpty()) {
// 自分ではない、もしくは、メンションが空 // 自分ではない、もしくは、メンションが空
mention_list.add("@" + who_acct) mention_list.add("@$who_acct")
} }
val sb = StringBuilder() val sb = StringBuilder()
@ -729,7 +729,6 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
findViewById(R.id.etChoice2), findViewById(R.id.etChoice2),
findViewById(R.id.etChoice3), findViewById(R.id.etChoice3),
findViewById(R.id.etChoice4) findViewById(R.id.etChoice4)
) )
tvCharCount = findViewById(R.id.tvCharCount) tvCharCount = findViewById(R.id.tvCharCount)
@ -835,6 +834,12 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
btnAccount.setBackgroundResource(R.drawable.btn_bg_transparent) btnAccount.setBackgroundResource(R.drawable.btn_bg_transparent)
} else { } else {
post_helper.setInstance(a.host) post_helper.setInstance(a.host)
// 先読みしてキャッシュに保持しておく
App1.custom_emoji_lister.getList(a.host) {
// 何もしない
}
val acct = a.acct val acct = a.acct
val ac = AcctColor.load(acct) val ac = AcctColor.load(acct)
val nickname = if(AcctColor.hasNickname(ac)) ac.nickname else acct val nickname = if(AcctColor.hasNickname(ac)) ac.nickname else acct
@ -1158,7 +1163,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
TootApiClient.MEDIA_TYPE_JSON, body_string TootApiClient.MEDIA_TYPE_JSON, body_string
) )
val request_builder = Request.Builder().put(request_body) val request_builder = Request.Builder().put(request_body)
val result = client.request("/api/v1/media/" + attachment_id, request_builder) val result = client.request("/api/v1/media/$attachment_id", request_builder)
new_attachment = parseItem(::TootAttachment, result?.jsonObject) new_attachment = parseItem(::TootAttachment, result?.jsonObject)
return result return result
} }
@ -1240,6 +1245,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
internal interface InputStreamOpener { internal interface InputStreamOpener {
val mimeType : String val mimeType : String
@Throws(IOException::class) @Throws(IOException::class)
fun open() : InputStream fun open() : InputStream
@ -1276,7 +1282,6 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
break break
} }
cache_dir.mkdir() cache_dir.mkdir()
val temp_file = File(cache_dir, "tmp." + Thread.currentThread().id) val temp_file = File(cache_dir, "tmp." + Thread.currentThread().id)
@ -1288,7 +1293,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
} }
} }
return object : ActPost.InputStreamOpener { return object : InputStreamOpener {
override val mimeType : String override val mimeType : String
get() = mime_type get() = mime_type
@ -1314,6 +1319,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
break break
} }
return object : InputStreamOpener { return object : InputStreamOpener {
override val mimeType : String override val mimeType : String
@ -1646,40 +1652,29 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
private fun performMore() { private fun performMore() {
val dialog = ActionsDialog() val dialog = ActionsDialog()
dialog.addAction( dialog.addAction(getString(R.string.open_picker_emoji)) {
getString(R.string.open_picker_emoji)
) {
post_helper.openEmojiPickerFromMore() post_helper.openEmojiPickerFromMore()
} }
dialog.addAction(getString(R.string.clear_text)) {
dialog.addAction(
getString(R.string.clear_text)
) {
etContent.setText("") etContent.setText("")
etContentWarning.setText("") etContentWarning.setText("")
} }
dialog.addAction( dialog.addAction(getString(R.string.clear_text_and_media)) {
getString(R.string.clear_text_and_media)
) {
etContent.setText("") etContent.setText("")
etContentWarning.setText("") etContentWarning.setText("")
attachment_list.clear() attachment_list.clear()
showMediaAttachment() showMediaAttachment()
} }
if(PostDraft.hasDraft()) { if(PostDraft.hasDraft()) dialog.addAction(getString(R.string.restore_draft)) {
dialog.addAction( openDraftPicker()
getString(R.string.restore_draft)
) { openDraftPicker() }
} }
dialog.addAction( dialog.addAction(getString(R.string.recommended_plugin)) {
getString(R.string.recommended_plugin) showRecommendedPlugin(null)
) { showRecommendedPlugin(null) } }
dialog.show(this, null) dialog.show(this, null)
} }
@ -1723,6 +1718,8 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
post_helper.attachment_list = this.attachment_list post_helper.attachment_list = this.attachment_list
post_helper.emojiMapCustom = App1.custom_emoji_lister.getMap(account.host)
post_helper.post(account, false, false) { target_account, status -> post_helper.post(account, false, false) { target_account, status ->
val data = Intent() val data = Intent()
data.putExtra(EXTRA_POSTED_ACCT, target_account.acct) data.putExtra(EXTRA_POSTED_ACCT, target_account.acct)
@ -1884,7 +1881,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
api_client.account = account api_client.account = account
if(in_reply_to_id != - 1L) { if(in_reply_to_id != - 1L) {
val result = api_client.request("/api/v1/statuses/" + in_reply_to_id) val result = api_client.request("/api/v1/statuses/$in_reply_to_id")
if(isCancelled) return null if(isCancelled) return null
val jsonObject = result?.jsonObject val jsonObject = result?.jsonObject
if(jsonObject == null) { if(jsonObject == null) {

View File

@ -11,6 +11,7 @@ import java.util.concurrent.ConcurrentLinkedQueue
import jp.juggler.subwaytooter.App1 import jp.juggler.subwaytooter.App1
import jp.juggler.subwaytooter.api.entity.CustomEmoji import jp.juggler.subwaytooter.api.entity.CustomEmoji
import jp.juggler.subwaytooter.api.entity.parseList import jp.juggler.subwaytooter.api.entity.parseList
import java.util.HashMap
class CustomEmojiLister(internal val context : Context) { class CustomEmojiLister(internal val context : Context) {
@ -105,6 +106,18 @@ class CustomEmojiLister(internal val context : Context) {
return null return null
} }
fun getMap(host : String) : HashMap<String, CustomEmoji>? {
val list = getList(host,{
// 遅延ロード非対応
}) ?: return null
//
val dst = HashMap<String, CustomEmoji>()
for( e in list){
dst[e.shortcode] = e
}
return dst
}
private inner class Worker : WorkerBase() { private inner class Worker : WorkerBase() {
override fun cancel() { override fun cancel() {

View File

@ -13,10 +13,12 @@ import java.util.ArrayList
import jp.juggler.subwaytooter.App1 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.CustomEmoji
import jp.juggler.subwaytooter.span.EmojiImageSpan import jp.juggler.subwaytooter.span.EmojiImageSpan
import jp.juggler.subwaytooter.span.HighlightSpan import jp.juggler.subwaytooter.span.HighlightSpan
import jp.juggler.subwaytooter.span.NetworkEmojiSpan import jp.juggler.subwaytooter.span.NetworkEmojiSpan
import jp.juggler.subwaytooter.table.HighlightWord import jp.juggler.subwaytooter.table.HighlightWord
import java.util.HashMap
import java.util.regex.Pattern import java.util.regex.Pattern
object EmojiDecoder { object EmojiDecoder {
@ -245,12 +247,18 @@ object EmojiDecoder {
} }
override fun onShortCode(part : String, name : String) { override fun onShortCode(part : String, name : String) {
// 通常の絵文字
val info = EmojiMap201709.sShortNameToImageId[name.toLowerCase().replace('-', '_')] // フレニコのプロフ絵文字
if(info != null) { if(emojiMapProfile != null && name.length >= 2 && name[0] == '@') {
builder.addImageSpan(part, info.image_id) val emojiProfile = emojiMapProfile[name] ?: emojiMapProfile[name.substring(1)]
if(emojiProfile != null) {
val url = emojiProfile.url
if(url.isNotEmpty()) {
builder.addNetworkEmojiSpan(part, url)
return return
} }
}
}
// カスタム絵文字 // カスタム絵文字
val emojiCustom = emojiMapCustom?.get(name) val emojiCustom = emojiMapCustom?.get(name)
@ -263,17 +271,12 @@ object EmojiDecoder {
return return
} }
// フレニコのプロフ絵文字 // 通常の絵文字
if(emojiMapProfile != null && name.length >= 2 && name[0] == '@') { val info = EmojiMap201709.sShortNameToImageId[name.toLowerCase().replace('-', '_')]
val emojiProfile = emojiMapProfile[name] ?: emojiMapProfile[name.substring(1)] if(info != null) {
if(emojiProfile != null) { builder.addImageSpan(part, info.image_id)
val url = emojiProfile.url
if(url.isNotEmpty()) {
builder.addNetworkEmojiSpan(part, url)
return return
} }
}
}
when { when {
reHohoemi.matcher(name).find() -> builder.addImageSpan( reHohoemi.matcher(name).find() -> builder.addImageSpan(
@ -297,7 +300,7 @@ object EmojiDecoder {
// 投稿などの際、表示は不要だがショートコード=>Unicodeの解決を行いたい場合がある // 投稿などの際、表示は不要だがショートコード=>Unicodeの解決を行いたい場合がある
// カスタム絵文字の変換も行わない // カスタム絵文字の変換も行わない
fun decodeShortCode(s : String) : String { fun decodeShortCode(s : String ,emojiMapCustom : HashMap<String, CustomEmoji>? =null ) : String {
val sb = StringBuilder() val sb = StringBuilder()
@ -307,6 +310,15 @@ object EmojiDecoder {
} }
override fun onShortCode(part : String, name : String) { override fun onShortCode(part : String, name : String) {
// カスタム絵文字にマッチするなら変換しない
val emojiCustom = emojiMapCustom?.get(name)
if(emojiCustom != null) {
sb.append(part)
return
}
// カスタム絵文字ではなく通常の絵文字のショートコードなら絵文字に変換する
val info = EmojiMap201709.sShortNameToImageId[name.toLowerCase().replace('-', '_')] val info = EmojiMap201709.sShortNameToImageId[name.toLowerCase().replace('-', '_')]
sb.append(info?.unified ?: part) sb.append(info?.unified ?: part)
} }

View File

@ -35,6 +35,7 @@ import jp.juggler.subwaytooter.table.TagSet
import jp.juggler.subwaytooter.view.MyEditText import jp.juggler.subwaytooter.view.MyEditText
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody
import java.util.HashMap
class PostHelper( class PostHelper(
private val activity : AppCompatActivity, private val activity : AppCompatActivity,
@ -71,6 +72,7 @@ class PostHelper(
var in_reply_to_id : Long = 0 var in_reply_to_id : Long = 0
var attachment_list : ArrayList<PostAttachment>? = null var attachment_list : ArrayList<PostAttachment>? = null
var enquete_items : ArrayList<String>? = null var enquete_items : ArrayList<String>? = null
var emojiMapCustom : HashMap<String, CustomEmoji>? =null
fun post( fun post(
account : SavedAccount, account : SavedAccount,
@ -221,12 +223,12 @@ class PostHelper(
val json = JSONObject() val json = JSONObject()
try { try {
json.put("status", EmojiDecoder.decodeShortCode(content)) json.put("status", EmojiDecoder.decodeShortCode(content,emojiMapCustom=emojiMapCustom))
if(visibility_checked != null) { if(visibility_checked != null) {
json.put("visibility", visibility_checked) json.put("visibility", visibility_checked)
} }
json.put("sensitive", bNSFW) json.put("sensitive", bNSFW)
json.put("spoiler_text", EmojiDecoder.decodeShortCode(spoiler_text ?: "")) json.put("spoiler_text", EmojiDecoder.decodeShortCode(spoiler_text ?: "",emojiMapCustom=emojiMapCustom))
json.put( json.put(
"in_reply_to_id", "in_reply_to_id",
if(in_reply_to_id == - 1L) null else in_reply_to_id if(in_reply_to_id == - 1L) null else in_reply_to_id
@ -242,7 +244,7 @@ class PostHelper(
json.put("isEnquete", true) json.put("isEnquete", true)
array = JSONArray() array = JSONArray()
for(item in enquete_items) { for(item in enquete_items) {
array.put(EmojiDecoder.decodeShortCode(item)) array.put(EmojiDecoder.decodeShortCode(item,emojiMapCustom=emojiMapCustom))
} }
json.put("enquete_items", array) json.put("enquete_items", array)
} catch(ex : JSONException) { } catch(ex : JSONException) {
@ -258,7 +260,7 @@ class PostHelper(
val sb = StringBuilder() val sb = StringBuilder()
sb.append("status=") sb.append("status=")
sb.append(EmojiDecoder.decodeShortCode(content).encodePercent()) sb.append(EmojiDecoder.decodeShortCode(content,emojiMapCustom=emojiMapCustom).encodePercent())
if(visibility_checked != null) { if(visibility_checked != null) {
sb.append("&visibility=") sb.append("&visibility=")
@ -271,7 +273,7 @@ class PostHelper(
if(spoiler_text?.isNotEmpty() == true) { if(spoiler_text?.isNotEmpty() == true) {
sb.append("&spoiler_text=") sb.append("&spoiler_text=")
sb.append(EmojiDecoder.decodeShortCode(spoiler_text).encodePercent()) sb.append(EmojiDecoder.decodeShortCode(spoiler_text,emojiMapCustom=emojiMapCustom).encodePercent())
} }
if(in_reply_to_id != - 1L) { if(in_reply_to_id != - 1L) {