Misskeyのカスタム絵文字の入力補完でaliasesに対応。Misskeyのプロフカラムで情報が足りない問題に対応。
This commit is contained in:
parent
0c00e5060c
commit
9c12e5f85d
|
@ -12,6 +12,7 @@ import jp.juggler.emoji.EmojiMap201709
|
|||
|
||||
import jp.juggler.subwaytooter.action.Action_Follow
|
||||
import jp.juggler.subwaytooter.action.Action_User
|
||||
import jp.juggler.subwaytooter.api.MisskeyAccountDetailMap
|
||||
import jp.juggler.subwaytooter.api.entity.TootAccount
|
||||
import jp.juggler.subwaytooter.api.entity.TootAccountRef
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
|
@ -129,6 +130,7 @@ internal class ViewHolderHeaderProfile(
|
|||
override fun bindData(column : Column) {
|
||||
super.bindData(column)
|
||||
|
||||
|
||||
if(! activity.timeline_font_size_sp.isNaN()) {
|
||||
tvMovedName.textSize = activity.timeline_font_size_sp
|
||||
tvMoved.textSize = activity.timeline_font_size_sp
|
||||
|
@ -142,6 +144,13 @@ internal class ViewHolderHeaderProfile(
|
|||
val whoRef = column.who_account
|
||||
this.whoRef = whoRef
|
||||
val who = whoRef?.get()
|
||||
|
||||
// Misskeyの場合はNote中のUserエンティティと /api/users/show の情報量がかなり異なる
|
||||
val whoDetail = if(who == null) {
|
||||
null
|
||||
} else {
|
||||
MisskeyAccountDetailMap.get(access_info, who.id)
|
||||
}
|
||||
|
||||
showColor()
|
||||
|
||||
|
@ -184,7 +193,7 @@ internal class ViewHolderHeaderProfile(
|
|||
access_info.supplyBaseUrl(who.avatar)
|
||||
)
|
||||
|
||||
val name = whoRef.decoded_display_name
|
||||
val name = whoDetail?.decodeDisplayName(activity) ?: whoRef.decoded_display_name
|
||||
tvDisplayName.text = name
|
||||
name_invalidator.register(name)
|
||||
|
||||
|
@ -193,7 +202,7 @@ internal class ViewHolderHeaderProfile(
|
|||
|
||||
val sb = SpannableStringBuilder()
|
||||
sb.append("@").append(access_info.getFullAcct(who))
|
||||
if(who.locked) {
|
||||
if(whoDetail?.locked ?: who.locked) {
|
||||
sb.append(" ")
|
||||
val start = sb.length
|
||||
sb.append("locked")
|
||||
|
@ -208,7 +217,7 @@ internal class ViewHolderHeaderProfile(
|
|||
)
|
||||
}
|
||||
}
|
||||
if(who.bot){
|
||||
if(who.bot) {
|
||||
sb.append(" ")
|
||||
val start = sb.length
|
||||
sb.append("bot")
|
||||
|
@ -229,9 +238,12 @@ internal class ViewHolderHeaderProfile(
|
|||
tvNote.text = note
|
||||
note_invalidator.register(note)
|
||||
|
||||
btnStatusCount.text = activity.getString(R.string.statuses) + "\n" + who.statuses_count
|
||||
btnFollowing.text = activity.getString(R.string.following) + "\n" + who.following_count
|
||||
btnFollowers.text = activity.getString(R.string.followers) + "\n" + who.followers_count
|
||||
btnStatusCount.text = activity.getString(R.string.statuses) + "\n" +
|
||||
(whoDetail?.statuses_count ?: who.statuses_count)
|
||||
btnFollowing.text = activity.getString(R.string.following) + "\n" +
|
||||
(whoDetail?.following_count ?: who.following_count)
|
||||
btnFollowers.text = activity.getString(R.string.followers) + "\n" +
|
||||
(whoDetail?.followers_count ?: who.followers_count)
|
||||
|
||||
val relation = UserRelation.load(access_info.db_id, who.id)
|
||||
Styler.setFollowIcon(activity, btnFollow, ivFollowedBy, relation, who)
|
||||
|
@ -259,8 +271,7 @@ internal class ViewHolderHeaderProfile(
|
|||
short = true,
|
||||
emojiMapProfile = who.profile_emojis
|
||||
)
|
||||
|
||||
|
||||
|
||||
val content_color = column.content_color
|
||||
val c = if(content_color != 0) content_color else default_color
|
||||
|
||||
|
@ -295,18 +306,18 @@ internal class ViewHolderHeaderProfile(
|
|||
)
|
||||
|
||||
val valueText = decodeOptions.decodeHTML(item.value)
|
||||
if(item.verified_at > 0L){
|
||||
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))
|
||||
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
|
||||
, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -320,7 +331,7 @@ internal class ViewHolderHeaderProfile(
|
|||
valueView.typeface = valueTypeface
|
||||
valueView.movementMethod = MyLinkMovementMethod
|
||||
|
||||
if(item.verified_at > 0L){
|
||||
if(item.verified_at > 0L) {
|
||||
valueView.setBackgroundColor(0x337fbc99)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package jp.juggler.subwaytooter.api
|
||||
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
object MisskeyAccountDetailMap {
|
||||
|
||||
private class AccountKey(
|
||||
val db_id : Long,
|
||||
val id : EntityId
|
||||
) {
|
||||
|
||||
override fun hashCode() : Int {
|
||||
val h1 = (db_id xor db_id.ushr(32)).toInt()
|
||||
val h2 = id.hashCode()
|
||||
return (h1 xor h2)
|
||||
}
|
||||
|
||||
override fun equals(other : Any?) : Boolean {
|
||||
return other is AccountKey && other.db_id == db_id && other.id == id
|
||||
}
|
||||
}
|
||||
|
||||
private val accountMap = ConcurrentHashMap<AccountKey, TootAccount>()
|
||||
|
||||
fun fromAccount(parser : TootParser, src : TootAccount, id : EntityId) {
|
||||
// SavedAccountが不明なら何もしない
|
||||
val access_info = parser.linkHelper as? SavedAccount ?: return
|
||||
|
||||
// アカウントのjsonがフォロー数を含まないなら何もしない
|
||||
if((src.followers_count ?: - 1) < 0L) return
|
||||
|
||||
val key = AccountKey(access_info.db_id, id)
|
||||
accountMap[key] = src
|
||||
}
|
||||
|
||||
fun get(accessInfo : SavedAccount, id : EntityId) : TootAccount? {
|
||||
return accountMap[AccountKey(accessInfo.db_id, id)]
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ class TootParser(
|
|||
var misskeyDecodeProfilePin :Boolean = false
|
||||
) {
|
||||
val misskeyUserRelationMap = HashMap<EntityId, UserRelation>()
|
||||
val misskeyAccountDetailMap = HashMap<EntityId, TootAccount>()
|
||||
|
||||
init{
|
||||
if(linkHelper.isMisskey) serviceType = ServiceType.MISSKEY
|
||||
|
|
|
@ -2,15 +2,25 @@ package jp.juggler.subwaytooter.api.entity
|
|||
|
||||
import jp.juggler.subwaytooter.util.notEmptyOrThrow
|
||||
import jp.juggler.subwaytooter.util.parseString
|
||||
import org.json.JSONArray
|
||||
|
||||
import org.json.JSONObject
|
||||
|
||||
class CustomEmoji(
|
||||
val shortcode : String, // shortcode (コロンを含まない)
|
||||
val url : String, // 画像URL
|
||||
val static_url : String? // アニメーションなしの画像URL
|
||||
val static_url : String?, // アニメーションなしの画像URL
|
||||
val aliases : ArrayList<String>? = null,
|
||||
val alias:String? =null
|
||||
) : Mappable<String> {
|
||||
|
||||
fun makeAlias(alias : String) = CustomEmoji (
|
||||
shortcode= this.shortcode,
|
||||
url = this.url,
|
||||
static_url = this.static_url,
|
||||
alias = alias
|
||||
)
|
||||
|
||||
override val mapKey : String
|
||||
get() = shortcode
|
||||
|
||||
|
@ -24,31 +34,31 @@ class CustomEmoji(
|
|||
}
|
||||
val decodeMisskey : (JSONObject) -> CustomEmoji = { src ->
|
||||
val url = src.parseString("url") ?: error("missing url")
|
||||
val name = src.parseString("name") ?: error("missing name")
|
||||
|
||||
// 使い方が分からない val aliases = parseAliases(src.optJSONArray("aliases"))
|
||||
|
||||
CustomEmoji(
|
||||
shortcode = name,
|
||||
shortcode = src.parseString("name") ?: error("missing name"),
|
||||
url = url,
|
||||
static_url = url
|
||||
static_url = url,
|
||||
aliases = parseAliases(src.optJSONArray("aliases"))
|
||||
)
|
||||
}
|
||||
|
||||
// private fun parseAliases(src : JSONArray?) : ArrayList<String>? {
|
||||
// var dst = null as ArrayList<String>?
|
||||
// if(src != null) {
|
||||
// val size = src.length()
|
||||
// for(i in 0 until size) {
|
||||
// val str = src.parseString(i) ?: continue
|
||||
// if(str.isNotEmpty()) {
|
||||
// if(dst == null) dst = ArrayList(size)
|
||||
// dst.add(str)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// return dst
|
||||
// }
|
||||
private fun parseAliases(src : JSONArray?) : ArrayList<String>? {
|
||||
var dst = null as ArrayList<String>?
|
||||
if(src != null) {
|
||||
val size = src.length()
|
||||
if( size > 0){
|
||||
dst = ArrayList(size)
|
||||
for(i in 0 until size) {
|
||||
val str = src.parseString(i) ?: continue
|
||||
if(str.isNotEmpty()) {
|
||||
dst.add(str)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return if(dst?.isNotEmpty() == true ) dst else null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,15 +3,13 @@ package jp.juggler.subwaytooter.api.entity
|
|||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.text.Spannable
|
||||
import jp.juggler.subwaytooter.api.MisskeyAccountDetailMap
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
import jp.juggler.subwaytooter.table.UserRelationMisskey
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
import java.util.ArrayList
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
open class TootAccount(parser : TootParser, src : JSONObject) {
|
||||
|
@ -252,7 +250,7 @@ open class TootAccount(parser : TootParser, src : JSONObject) {
|
|||
}
|
||||
|
||||
UserRelationMisskey.fromAccount(parser,src,id)
|
||||
|
||||
MisskeyAccountDetailMap.fromAccount(parser,this,id)
|
||||
|
||||
} else {
|
||||
|
||||
|
|
|
@ -27,7 +27,11 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
get() = SystemClock.elapsedRealtime()
|
||||
}
|
||||
|
||||
internal class CacheItem(val instance : String, var list : ArrayList<CustomEmoji>?) {
|
||||
internal class CacheItem(
|
||||
val instance : String,
|
||||
var list : ArrayList<CustomEmoji>? = null,
|
||||
var listWithAliases : ArrayList<CustomEmoji>? = null
|
||||
) {
|
||||
|
||||
// 参照された時刻
|
||||
var time_used : Long = 0
|
||||
|
@ -44,6 +48,7 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
internal class Request(
|
||||
val instance : String,
|
||||
val isMisskey : Boolean,
|
||||
val reportWithAliases : Boolean = false,
|
||||
val onListLoaded : (list : ArrayList<CustomEmoji>) -> Unit?
|
||||
)
|
||||
|
||||
|
@ -53,7 +58,7 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
// エラーキャッシュ
|
||||
internal val cache_error = ConcurrentHashMap<String, Long>()
|
||||
|
||||
private val cache_error_item = CacheItem("error", null)
|
||||
private val cache_error_item = CacheItem("error")
|
||||
|
||||
// ロード要求
|
||||
internal val queue = ConcurrentLinkedQueue<Request>()
|
||||
|
@ -100,7 +105,36 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
if(item != null) return item.list
|
||||
}
|
||||
|
||||
queue.add(Request(instance, isMisskey, onListLoaded))
|
||||
queue.add(Request(instance, isMisskey, onListLoaded = onListLoaded))
|
||||
worker.notifyEx()
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun getListWithAliases(
|
||||
_instance : String,
|
||||
isMisskey : Boolean,
|
||||
onListLoaded : (list : ArrayList<CustomEmoji>) -> Unit
|
||||
) : ArrayList<CustomEmoji>? {
|
||||
try {
|
||||
if(_instance.isEmpty()) return null
|
||||
val instance = _instance.toLowerCase()
|
||||
|
||||
synchronized(cache) {
|
||||
val item = getCached(elapsedTime, instance)
|
||||
if(item != null) return item.listWithAliases
|
||||
}
|
||||
|
||||
queue.add(
|
||||
Request(
|
||||
instance,
|
||||
isMisskey,
|
||||
reportWithAliases = true,
|
||||
onListLoaded = onListLoaded
|
||||
)
|
||||
)
|
||||
worker.notifyEx()
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
|
@ -141,8 +175,9 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
val item = getCached(elapsedTime, request.instance)
|
||||
return@synchronized if(item != null) {
|
||||
val list = item.list
|
||||
if(list != null) {
|
||||
fireCallback(request, list)
|
||||
val listWithAliases = item.listWithAliases
|
||||
if(list != null && listWithAliases != null) {
|
||||
fireCallback(request, list, listWithAliases)
|
||||
}
|
||||
true
|
||||
} else {
|
||||
|
@ -154,6 +189,7 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
if(cached) continue
|
||||
|
||||
var list : ArrayList<CustomEmoji>? = null
|
||||
var listWithAlias : ArrayList<CustomEmoji>? = null
|
||||
try {
|
||||
val data = if(request.isMisskey) {
|
||||
App1.getHttpCachedString("https://" + request.instance + "/api/meta") { builder ->
|
||||
|
@ -165,29 +201,32 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
} else {
|
||||
App1.getHttpCachedString("https://" + request.instance + "/api/v1/custom_emojis")
|
||||
}
|
||||
|
||||
|
||||
if(data != null) {
|
||||
list = decodeEmojiList(data, request.instance, request.isMisskey)
|
||||
val a = decodeEmojiList(data, request.instance, request.isMisskey)
|
||||
list = a
|
||||
listWithAlias = makeListWithAlias(a)
|
||||
}
|
||||
|
||||
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
synchronized(cache) {
|
||||
val now = elapsedTime
|
||||
if(list == null) {
|
||||
if(list == null || listWithAlias == null) {
|
||||
cache_error.put(request.instance, now)
|
||||
} else {
|
||||
var item : CacheItem? = cache[request.instance]
|
||||
if(item == null) {
|
||||
item = CacheItem(request.instance, list)
|
||||
item = CacheItem(request.instance, list, listWithAlias)
|
||||
cache[request.instance] = item
|
||||
} else {
|
||||
item.list = list
|
||||
item.listWithAliases = listWithAlias
|
||||
item.time_update = now
|
||||
}
|
||||
fireCallback(request, list)
|
||||
fireCallback(request, list, listWithAlias)
|
||||
}
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
|
@ -197,8 +236,21 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun fireCallback(request : Request, list : ArrayList<CustomEmoji>) {
|
||||
handler.post { request.onListLoaded(list) }
|
||||
|
||||
private fun fireCallback(
|
||||
request : Request,
|
||||
list : ArrayList<CustomEmoji>,
|
||||
listWithAliases : ArrayList<CustomEmoji>
|
||||
) {
|
||||
handler.post {
|
||||
request.onListLoaded(
|
||||
if(request.reportWithAliases) {
|
||||
listWithAliases
|
||||
} else {
|
||||
list
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// キャッシュの掃除
|
||||
|
@ -243,6 +295,23 @@ class CustomEmojiLister(internal val context : Context) {
|
|||
null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun makeListWithAlias(list : ArrayList<CustomEmoji>?) : ArrayList<CustomEmoji> {
|
||||
val dst = ArrayList<CustomEmoji>()
|
||||
if( list != null) {
|
||||
dst.addAll(list)
|
||||
for(item in list) {
|
||||
val aliases = item.aliases ?: continue
|
||||
for(alias in aliases) {
|
||||
if( alias.equals(item.shortcode,ignoreCase = true)) continue
|
||||
dst.add(item.makeAlias(alias))
|
||||
}
|
||||
}
|
||||
dst.sortWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it.alias ?: it.shortcode })
|
||||
}
|
||||
return dst
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -36,21 +36,21 @@ object EmojiDecoder {
|
|||
return when(cp) {
|
||||
- 1 -> true
|
||||
cpColon -> false
|
||||
// rubyの (Letter | Mark | Decimal_Number) はNG
|
||||
// ftp://unicode.org/Public/5.1.0/ucd/UCD.html#General_Category_Values
|
||||
// rubyの (Letter | Mark | Decimal_Number) はNG
|
||||
// ftp://unicode.org/Public/5.1.0/ucd/UCD.html#General_Category_Values
|
||||
else -> when(java.lang.Character.getType(cp).toByte()) {
|
||||
// Letter
|
||||
// LCはエイリアスなので文字から得られることはないはず
|
||||
// Letter
|
||||
// LCはエイリアスなので文字から得られることはないはず
|
||||
Character.UPPERCASE_LETTER,
|
||||
Character.LOWERCASE_LETTER,
|
||||
Character.TITLECASE_LETTER,
|
||||
Character.MODIFIER_LETTER,
|
||||
Character.OTHER_LETTER -> false
|
||||
// Mark
|
||||
// Mark
|
||||
Character.NON_SPACING_MARK,
|
||||
Character.COMBINING_SPACING_MARK,
|
||||
Character.ENCLOSING_MARK -> false
|
||||
// Decimal_Number
|
||||
// Decimal_Number
|
||||
Character.DECIMAL_DIGIT_NUMBER -> false
|
||||
|
||||
else -> true
|
||||
|
@ -106,7 +106,7 @@ object EmojiDecoder {
|
|||
sb.append(text)
|
||||
val end = sb.length
|
||||
sb.setSpan(
|
||||
NetworkEmojiSpan(url,scale = options.enlargeCustomEmoji),
|
||||
NetworkEmojiSpan(url, scale = options.enlargeCustomEmoji),
|
||||
start,
|
||||
end,
|
||||
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
|
@ -177,7 +177,7 @@ object EmojiDecoder {
|
|||
val c = s[i ++]
|
||||
sb.append(
|
||||
when(c) {
|
||||
// https://github.com/tateisu/SubwayTooter/issues/69
|
||||
// https://github.com/tateisu/SubwayTooter/issues/69
|
||||
'\u00AD' -> '-'
|
||||
else -> c
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.util.ArrayList
|
|||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.Styler
|
||||
import jp.juggler.subwaytooter.view.MyEditText
|
||||
import java.util.regex.Pattern
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
internal class PopupAutoCompleteAcct(
|
||||
|
@ -30,6 +31,9 @@ internal class PopupAutoCompleteAcct(
|
|||
companion object {
|
||||
|
||||
internal val log = LogCategory("PopupAutoCompleteAcct")
|
||||
|
||||
// 絵文字ショートコードにマッチするとても雑な正規表現
|
||||
private val reLastShortCode = Pattern.compile(""":([^\s:]+):\z""")
|
||||
}
|
||||
|
||||
private val acct_popup : PopupWindow
|
||||
|
@ -135,7 +139,7 @@ internal class PopupAutoCompleteAcct(
|
|||
if(acct[0] == ' ') {
|
||||
// 絵文字ショートコード
|
||||
if(! EmojiDecoder.canStartShortCode(sb, start)) sb.append(' ')
|
||||
sb.append(acct.subSequence(2, acct.length))
|
||||
sb.append( findShortCode(acct.toString()))
|
||||
} else {
|
||||
// @user@host, #hashtag
|
||||
// 直後に空白を付与する
|
||||
|
@ -159,6 +163,14 @@ internal class PopupAutoCompleteAcct(
|
|||
updatePosition()
|
||||
}
|
||||
|
||||
|
||||
|
||||
private fun findShortCode(acct : String) : String {
|
||||
val m = reLastShortCode.matcher(acct)
|
||||
if(m.find()) return m.group(0)
|
||||
return acct
|
||||
}
|
||||
|
||||
fun updatePosition() {
|
||||
|
||||
val location = IntArray(2)
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.os.SystemClock
|
|||
import android.support.v7.app.AlertDialog
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.text.*
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.view.View
|
||||
import jp.juggler.emoji.EmojiMap201709
|
||||
|
||||
|
@ -19,6 +20,7 @@ import java.util.regex.Pattern
|
|||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.Styler
|
||||
import jp.juggler.subwaytooter.api.TootApiClient
|
||||
import jp.juggler.subwaytooter.api.TootApiResult
|
||||
import jp.juggler.subwaytooter.api.TootTask
|
||||
|
@ -159,7 +161,7 @@ class PostHelper(
|
|||
|
||||
if(! bConfirmTag) {
|
||||
|
||||
if( !account.isMisskey
|
||||
if(! account.isMisskey
|
||||
&& visibility != TootVisibility.Public
|
||||
&& reTag.matcher(content).find()
|
||||
) {
|
||||
|
@ -228,18 +230,18 @@ class PostHelper(
|
|||
|
||||
var credential_tmp : TootAccount? = null
|
||||
|
||||
val parser = TootParser(activity, account )
|
||||
val parser = TootParser(activity, account)
|
||||
|
||||
fun getInstanceInformation(client : TootApiClient) : TootApiResult? {
|
||||
val result = if( account.isMisskey){
|
||||
val params = JSONObject().apply{
|
||||
put("dummy",1)
|
||||
val result = if(account.isMisskey) {
|
||||
val params = JSONObject().apply {
|
||||
put("dummy", 1)
|
||||
}
|
||||
client.request("/api/meta",params.toPostRequestBuilder())
|
||||
}else{
|
||||
client.request("/api/meta", params.toPostRequestBuilder())
|
||||
} else {
|
||||
client.request("/api/v1/instance")
|
||||
}
|
||||
instance_tmp = parseItem(::TootInstance,parser , result?.jsonObject)
|
||||
instance_tmp = parseItem(::TootInstance, parser, result?.jsonObject)
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -255,15 +257,15 @@ class PostHelper(
|
|||
|
||||
// 元の投稿を削除する
|
||||
if(redraft_status_id != null) {
|
||||
result = if( isMisskey ){
|
||||
val params = account.putMisskeyApiToken(JSONObject()).apply{
|
||||
put("noteId",redraft_status_id)
|
||||
result = if(isMisskey) {
|
||||
val params = account.putMisskeyApiToken(JSONObject()).apply {
|
||||
put("noteId", redraft_status_id)
|
||||
}
|
||||
client.request(
|
||||
"/api/notes/delete",
|
||||
params.toPostRequestBuilder()
|
||||
)
|
||||
}else{
|
||||
} else {
|
||||
client.request(
|
||||
"/api/v1/statuses/$redraft_status_id",
|
||||
Request.Builder().delete()
|
||||
|
@ -283,16 +285,17 @@ class PostHelper(
|
|||
account.instance = instance
|
||||
}
|
||||
|
||||
if( visibility == TootVisibility.WebSetting) {
|
||||
visibility_checked = if(account.isMisskey || instance.versionGE(TootInstance.VERSION_1_6)) {
|
||||
null
|
||||
} else {
|
||||
val r2 = getCredential(client)
|
||||
val credential_tmp = this.credential_tmp ?: return r2
|
||||
val privacy = credential_tmp.source?.privacy
|
||||
?: return TootApiResult(activity.getString(R.string.cant_get_web_setting_visibility))
|
||||
TootVisibility.parseMastodon(privacy)
|
||||
}
|
||||
if(visibility == TootVisibility.WebSetting) {
|
||||
visibility_checked =
|
||||
if(account.isMisskey || instance.versionGE(TootInstance.VERSION_1_6)) {
|
||||
null
|
||||
} else {
|
||||
val r2 = getCredential(client)
|
||||
val credential_tmp = this.credential_tmp ?: return r2
|
||||
val privacy = credential_tmp.source?.privacy
|
||||
?: return TootApiResult(activity.getString(R.string.cant_get_web_setting_visibility))
|
||||
TootVisibility.parseMastodon(privacy)
|
||||
}
|
||||
}
|
||||
|
||||
val json = JSONObject()
|
||||
|
@ -308,34 +311,43 @@ class PostHelper(
|
|||
)
|
||||
if(visibility_checked != null) {
|
||||
|
||||
if( visibility_checked == TootVisibility.DirectSpecified ){
|
||||
if(visibility_checked == TootVisibility.DirectSpecified) {
|
||||
val userIds = JSONArray()
|
||||
val reMention = Pattern.compile("(?:\\A|\\s)@([a-zA-Z0-9_]{1,20})(?:@([\\w.:-]+))?(?:\\z|\\s)")
|
||||
val reMention =
|
||||
Pattern.compile("(?:\\A|\\s)@([a-zA-Z0-9_]{1,20})(?:@([\\w.:-]+))?(?:\\z|\\s)")
|
||||
val m = reMention.matcher(content)
|
||||
while(m.find()){
|
||||
while(m.find()) {
|
||||
val username = m.group(1)
|
||||
val host = m.group(2)
|
||||
val queryParams = account.putMisskeyApiToken(JSONObject())
|
||||
if(username?.isNotEmpty()==true) queryParams.put("username",username)
|
||||
if(host?.isNotEmpty()==true) queryParams.put("host",host)
|
||||
result = client.request("/api/users/show",queryParams.toPostRequestBuilder())
|
||||
if(username?.isNotEmpty() == true) queryParams.put(
|
||||
"username",
|
||||
username
|
||||
)
|
||||
if(host?.isNotEmpty() == true) queryParams.put("host", host)
|
||||
result = client.request(
|
||||
"/api/users/show",
|
||||
queryParams.toPostRequestBuilder()
|
||||
)
|
||||
val id = result?.jsonObject?.parseString("id")
|
||||
if( id?.isNotEmpty() == true ){
|
||||
userIds.put( id)
|
||||
if(id?.isNotEmpty() == true) {
|
||||
userIds.put(id)
|
||||
}
|
||||
}
|
||||
json.put("visibility",if( userIds.length() == 0 ){
|
||||
"private"
|
||||
}else{
|
||||
json.put("visibleUserIds",userIds)
|
||||
"specified"
|
||||
})
|
||||
}else {
|
||||
json.put("visibility",visibility_checked.strMisskey)
|
||||
json.put(
|
||||
"visibility", if(userIds.length() == 0) {
|
||||
"private"
|
||||
} else {
|
||||
json.put("visibleUserIds", userIds)
|
||||
"specified"
|
||||
}
|
||||
)
|
||||
} else {
|
||||
json.put("visibility", visibility_checked.strMisskey)
|
||||
}
|
||||
}
|
||||
|
||||
if(spoiler_text?.isNotEmpty() == true ) {
|
||||
if(spoiler_text?.isNotEmpty() == true) {
|
||||
json.put(
|
||||
"cw",
|
||||
EmojiDecoder.decodeShortCode(
|
||||
|
@ -349,7 +361,7 @@ class PostHelper(
|
|||
json.put("replyId", in_reply_to_id.toString())
|
||||
}
|
||||
|
||||
json.put("viaMobile",true)
|
||||
json.put("viaMobile", true)
|
||||
|
||||
if(attachment_list != null) {
|
||||
val array = JSONArray()
|
||||
|
@ -359,31 +371,34 @@ class PostHelper(
|
|||
array.put(a.id.toString())
|
||||
|
||||
// Misskeyの場合、NSFWするにはアップロード済みの画像を drive/files/update で更新する
|
||||
if( bNSFW){
|
||||
if(bNSFW) {
|
||||
val params = account.putMisskeyApiToken(JSONObject())
|
||||
.put("fileId",a.id.toString())
|
||||
.put("isSensitive",true)
|
||||
val r = client.request("/api/drive/files/update",params.toPostRequestBuilder())
|
||||
if(r==null|| r.error!=null ) return r
|
||||
.put("fileId", a.id.toString())
|
||||
.put("isSensitive", true)
|
||||
val r = client.request(
|
||||
"/api/drive/files/update",
|
||||
params.toPostRequestBuilder()
|
||||
)
|
||||
if(r == null || r.error != null) return r
|
||||
}
|
||||
}
|
||||
if( array.length() > 0) json.put("mediaIds", array)
|
||||
if(array.length() > 0) json.put("mediaIds", array)
|
||||
}
|
||||
|
||||
if(enquete_items?.isNotEmpty() == true) {
|
||||
val choices = JSONArray().apply {
|
||||
for(item in enquete_items) {
|
||||
val text =EmojiDecoder.decodeShortCode(
|
||||
val text = EmojiDecoder.decodeShortCode(
|
||||
item,
|
||||
emojiMapCustom = emojiMapCustom
|
||||
)
|
||||
if( text.isEmpty() ) continue
|
||||
if(text.isEmpty()) continue
|
||||
put(text)
|
||||
}
|
||||
}
|
||||
if( choices.length() > 0 ) {
|
||||
json.put("poll",JSONObject().apply {
|
||||
put("choices",choices)
|
||||
if(choices.length() > 0) {
|
||||
json.put("poll", JSONObject().apply {
|
||||
put("choices", choices)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -453,19 +468,21 @@ class PostHelper(
|
|||
val digest = (body_string + account.acct).digestSHA256Hex()
|
||||
request_builder.header("Idempotency-Key", digest)
|
||||
}
|
||||
|
||||
result = if(isMisskey){
|
||||
|
||||
result = if(isMisskey) {
|
||||
client.request("/api/notes/create", request_builder)
|
||||
// TODO {"error":{}} が返ってきた時にどう扱えばいい?
|
||||
}else{
|
||||
} else {
|
||||
client.request("/api/v1/statuses", request_builder)
|
||||
}
|
||||
|
||||
val status = parser.status(if(isMisskey) {
|
||||
result?.jsonObject?.optJSONObject("createdNote") ?: result?.jsonObject
|
||||
}else{
|
||||
result?.jsonObject
|
||||
})
|
||||
val status = parser.status(
|
||||
if(isMisskey) {
|
||||
result?.jsonObject?.optJSONObject("createdNote") ?: result?.jsonObject
|
||||
} else {
|
||||
result?.jsonObject
|
||||
}
|
||||
)
|
||||
this.status = status
|
||||
if(status != null) {
|
||||
|
||||
|
@ -664,18 +681,21 @@ class PostHelper(
|
|||
et, last_colon, end, null, picker_caption_emoji, open_picker_emoji
|
||||
)
|
||||
} else {
|
||||
// 絵文字を部分一致で検索
|
||||
|
||||
val code_list = ArrayList<CharSequence>()
|
||||
val limit = 100
|
||||
val s = src.substring(last_colon + 1, end).toLowerCase().replace('-', '_')
|
||||
val code_list = EmojiDecoder.searchShortCode(activity, s, limit)
|
||||
log.d("checkEmoji: search for %s, result=%d", s, code_list.size)
|
||||
|
||||
// カスタム絵文字を検索
|
||||
val instance = this@PostHelper.instance
|
||||
if(instance != null && instance.isNotEmpty() ) {
|
||||
val custom_list = App1.custom_emoji_lister.getList(instance,isMisskey, onEmojiListLoad)
|
||||
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
|
||||
if(! item.shortcode.contains(needle)) continue
|
||||
|
@ -689,14 +709,43 @@ class PostHelper(
|
|||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 通常の絵文字を部分一致で検索
|
||||
val remain = limit - code_list.size
|
||||
if(remain > 0) {
|
||||
val s = src.substring(last_colon + 1, end).toLowerCase().replace('-', '_')
|
||||
val src = EmojiDecoder.searchShortCode(activity, s, remain)
|
||||
log.d("checkEmoji: search for %s, result=%d", s, src.size)
|
||||
code_list.addAll(src)
|
||||
|
||||
}
|
||||
|
||||
openPopup()?.setList(
|
||||
et,
|
||||
last_colon,
|
||||
|
@ -731,8 +780,8 @@ class PostHelper(
|
|||
this.instance = instance
|
||||
this.isMisskey = isMisskey
|
||||
|
||||
if(instance != null ) {
|
||||
App1.custom_emoji_lister.getList(instance, isMisskey,onEmojiListLoad)
|
||||
if(instance != null) {
|
||||
App1.custom_emoji_lister.getList(instance, isMisskey, onEmojiListLoad)
|
||||
}
|
||||
|
||||
val popup = this.popup
|
||||
|
@ -841,7 +890,7 @@ class PostHelper(
|
|||
.appendEmoji(name, instance, bInstanceHasCustomEmoji)
|
||||
|
||||
val newSelection = sb.length
|
||||
if(end < src_length) sb.append(src.subSequence(end, src_length) )
|
||||
if(end < src_length) sb.append(src.subSequence(end, src_length))
|
||||
|
||||
et.text = sb
|
||||
et.setSelection(newSelection)
|
||||
|
@ -864,11 +913,11 @@ class PostHelper(
|
|||
val end = Math.min(src_length, et.selectionEnd)
|
||||
|
||||
val sb = SpannableStringBuilder()
|
||||
.append(src.subSequence(0, start) )
|
||||
.append(src.subSequence(0, start))
|
||||
.appendEmoji(name, instance, bInstanceHasCustomEmoji)
|
||||
|
||||
val newSelection = sb.length
|
||||
if(end < src_length) sb.append(src.subSequence(end, src_length) )
|
||||
if(end < src_length) sb.append(src.subSequence(end, src_length))
|
||||
|
||||
et.text = sb
|
||||
et.setSelection(newSelection)
|
||||
|
|
Loading…
Reference in New Issue