試験機能:misskeyのタンスを疑似アカウントで追加して、ローカルTLとグローバルTLを見れる
This commit is contained in:
parent
b58552c990
commit
7ceb9e4f51
|
@ -101,7 +101,8 @@ class App1 : Application() {
|
|||
// 2018/5/16 v252 24=>25 SubscriptionServerKey テーブルを追加
|
||||
// 2018/5/16 v252 25=>26 SubscriptionServerKey テーブルを丸ごと変更
|
||||
// 2018/8/5 v264 26 => 27 SavedAccountテーブルに項目追加
|
||||
internal const val DB_VERSION = 27
|
||||
// 2018/8/17 v267 27 => 28 SavedAccountテーブルに項目追加
|
||||
internal const val DB_VERSION = 28
|
||||
|
||||
private val tableList = arrayOf(
|
||||
LogData,
|
||||
|
|
|
@ -7,19 +7,17 @@ import android.os.AsyncTask
|
|||
import android.os.SystemClock
|
||||
import android.view.Gravity
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.table.*
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.lang.ref.WeakReference
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.regex.Pattern
|
||||
|
||||
enum class StreamingIndicatorState {
|
||||
NONE,
|
||||
|
@ -309,6 +307,9 @@ class Column(
|
|||
|
||||
private val streamPath : String?
|
||||
get() {
|
||||
// misskeyの疑似アカウントはストリーミング対応していない
|
||||
if(access_info.isPseudo && access_info.isMisskey) return null
|
||||
|
||||
return when(column_type) {
|
||||
TYPE_HOME, TYPE_NOTIFICATIONS -> "/api/v1/streaming/?stream=user"
|
||||
TYPE_LOCAL -> "/api/v1/streaming/?stream=public:local"
|
||||
|
@ -320,7 +321,7 @@ class Column(
|
|||
TYPE_HASHTAG -> when(instance_local) {
|
||||
true -> "/api/v1/streaming/?stream=" + Uri.encode("hashtag:local") + "&tag=" + hashtag.encodePercent()
|
||||
else -> "/api/v1/streaming/?stream=hashtag&tag=" + hashtag.encodePercent()
|
||||
// タグ先頭の#を含まない
|
||||
// タグ先頭の#を含まない
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
@ -816,7 +817,7 @@ class Column(
|
|||
}
|
||||
}
|
||||
|
||||
fun removeUser(targetAccount : SavedAccount,columnType:Int,who_id:Long){
|
||||
fun removeUser(targetAccount : SavedAccount, columnType : Int, who_id : Long) {
|
||||
if(column_type == columnType && targetAccount.acct == access_info.acct) {
|
||||
val tmp_list = ArrayList<TimelineItem>(list_data.size)
|
||||
for(o in list_data) {
|
||||
|
@ -1502,13 +1503,36 @@ class Column(
|
|||
log.d("getStatusesPinned: list size=%s", list_pinned?.size ?: - 1)
|
||||
}
|
||||
|
||||
fun getStatuses(client : TootApiClient, path_base : String) : TootApiResult? {
|
||||
fun getStatuses(
|
||||
client : TootApiClient,
|
||||
path_base : String,
|
||||
isMisskey : Boolean = false
|
||||
) : TootApiResult? {
|
||||
|
||||
val params = JSONObject()
|
||||
if(isMisskey) {
|
||||
parser.serviceType = ServiceType.MISSKEY
|
||||
params.put("limit", 100)
|
||||
if(with_attachment) {
|
||||
params.put("mediaOnly", true)
|
||||
}
|
||||
}
|
||||
|
||||
val time_start = SystemClock.elapsedRealtime()
|
||||
val result = client.request(path_base)
|
||||
val result = if(isMisskey) {
|
||||
client.request(path_base, params.toPostRequestBuilder())
|
||||
} else {
|
||||
client.request(path_base)
|
||||
}
|
||||
|
||||
var jsonArray = result?.jsonArray
|
||||
if(jsonArray != null) {
|
||||
saveRange(result, true, true)
|
||||
if(isMisskey) {
|
||||
saveRangeMisskey(jsonArray, true, true)
|
||||
} else {
|
||||
saveRange(result, true, true)
|
||||
}
|
||||
|
||||
//
|
||||
var src = parser.statusList(jsonArray)
|
||||
|
||||
|
@ -1540,9 +1564,16 @@ class Column(
|
|||
log.d("loading-statuses: timeout.")
|
||||
break
|
||||
}
|
||||
val path = path_base + delimiter + "max_id=" + max_id
|
||||
val result2 = client.request(path)
|
||||
val result2 = if(isMisskey) {
|
||||
params.put("untilId", max_id)
|
||||
client.request(path_base, params.toPostRequestBuilder())
|
||||
} else {
|
||||
val path = path_base + delimiter + "max_id=" + max_id
|
||||
client.request(path)
|
||||
}
|
||||
|
||||
jsonArray = result2?.jsonArray
|
||||
|
||||
if(jsonArray == null) {
|
||||
log.d("loading-statuses: error or cancelled.")
|
||||
break
|
||||
|
@ -1552,9 +1583,16 @@ class Column(
|
|||
|
||||
addWithFilterStatus(list_tmp, src)
|
||||
|
||||
if(! saveRangeEnd(result2)) {
|
||||
log.d("loading-statuses: missing range info.")
|
||||
break
|
||||
if(isMisskey) {
|
||||
if(! saveRangeEndMisskey(jsonArray)) {
|
||||
log.d("loading-statuses: missing range info.")
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if(! saveRangeEnd(result2)) {
|
||||
log.d("loading-statuses: missing range info.")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1748,9 +1786,22 @@ class Column(
|
|||
|
||||
TYPE_DIRECT_MESSAGES -> return getStatuses(client, PATH_DIRECT_MESSAGES)
|
||||
|
||||
TYPE_LOCAL -> return getStatuses(client, makePublicLocalUrl())
|
||||
|
||||
TYPE_FEDERATE -> return getStatuses(client, makePublicFederateUrl())
|
||||
TYPE_LOCAL -> return when(access_info.isMisskey) {
|
||||
true -> getStatuses(
|
||||
client,
|
||||
"/api/notes/local-timeline",
|
||||
isMisskey = true
|
||||
)
|
||||
else -> getStatuses(client, makePublicLocalUrl())
|
||||
}
|
||||
TYPE_FEDERATE -> return when(access_info.isMisskey) {
|
||||
true -> getStatuses(
|
||||
client,
|
||||
"/api/notes/global-timeline",
|
||||
isMisskey = true
|
||||
)
|
||||
else -> return getStatuses(client, makePublicFederateUrl())
|
||||
}
|
||||
|
||||
TYPE_PROFILE -> {
|
||||
|
||||
|
@ -1903,7 +1954,10 @@ class Column(
|
|||
//
|
||||
} else {
|
||||
this.list_tmp = addOne(this.list_tmp, target_status)
|
||||
this.list_tmp = addOne(this.list_tmp, TootMessageHolder(context.getString(R.string.toot_context_parse_failed)))
|
||||
this.list_tmp = addOne(
|
||||
this.list_tmp,
|
||||
TootMessageHolder(context.getString(R.string.toot_context_parse_failed))
|
||||
)
|
||||
}
|
||||
|
||||
// カードを取得する
|
||||
|
@ -2132,6 +2186,31 @@ class Column(
|
|||
}
|
||||
}
|
||||
|
||||
private fun saveRangeMisskey(src : JSONArray?, bBottom : Boolean, bTop : Boolean) {
|
||||
src ?: return
|
||||
var id_min : String? = null
|
||||
var id_max : String? = null
|
||||
for(i in 0 until src.length()) {
|
||||
val id = src.optJSONObject(i)?.optString("id", null) ?: continue
|
||||
if(id_min == null || id < id_min) id_min = id
|
||||
if(id_max == null || id > id_max) id_max = id
|
||||
}
|
||||
if(bBottom) {
|
||||
when {
|
||||
id_min == null -> max_id = ""
|
||||
max_id.isEmpty() || id_min < max_id -> max_id = id_min
|
||||
}
|
||||
}
|
||||
if(bTop) {
|
||||
when {
|
||||
id_max == null -> {
|
||||
}
|
||||
|
||||
since_id.isEmpty() || id_max > since_id -> since_id = id_max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveRangeEnd(result : TootApiResult?) : Boolean {
|
||||
if(result != null) {
|
||||
if(result.link_older == null) {
|
||||
|
@ -2147,6 +2226,23 @@ class Column(
|
|||
return false
|
||||
}
|
||||
|
||||
private fun saveRangeEndMisskey(src : JSONArray?) : Boolean {
|
||||
if(src != null) {
|
||||
var id_min : String? = null
|
||||
for(i in 0 until src.length()) {
|
||||
val id = src.optJSONObject(i)?.optString("id", null) ?: continue
|
||||
if(id_min == null || id < id_min) id_min = id
|
||||
}
|
||||
if(id_min?.isEmpty() != false) {
|
||||
max_id = ""
|
||||
} else {
|
||||
max_id = id_min
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun addRange(bBottom : Boolean, path : String) : String {
|
||||
val delimiter = if(- 1 != path.indexOf('?')) '&' else '?'
|
||||
if(bBottom) {
|
||||
|
@ -2610,18 +2706,43 @@ class Column(
|
|||
|
||||
fun getStatusList(
|
||||
client : TootApiClient,
|
||||
path_base : String
|
||||
path_base : String,
|
||||
isMisskey : Boolean = false
|
||||
) : TootApiResult? {
|
||||
|
||||
val params = JSONObject()
|
||||
if(isMisskey) {
|
||||
parser.serviceType = ServiceType.MISSKEY
|
||||
params.put("limit", 100)
|
||||
if(with_attachment) {
|
||||
params.put("mediaOnly", true)
|
||||
}
|
||||
}
|
||||
|
||||
val time_start = SystemClock.elapsedRealtime()
|
||||
|
||||
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
||||
val last_since_id = since_id
|
||||
|
||||
val result = client.request(addRange(bBottom, path_base))
|
||||
val result = if(isMisskey) {
|
||||
if(bBottom) {
|
||||
if(max_id.isNotEmpty()) params.put("untilId", max_id)
|
||||
} else {
|
||||
if(since_id.isNotEmpty()) params.put("sinceId", since_id)
|
||||
}
|
||||
client.request(path_base, params.toPostRequestBuilder())
|
||||
} else {
|
||||
client.request(addRange(bBottom, path_base))
|
||||
}
|
||||
|
||||
var jsonArray = result?.jsonArray
|
||||
if(jsonArray != null) {
|
||||
saveRange(result, bBottom, ! bBottom)
|
||||
if(isMisskey) {
|
||||
saveRangeMisskey(jsonArray, bBottom, ! bBottom)
|
||||
} else {
|
||||
saveRange(result, bBottom, ! bBottom)
|
||||
}
|
||||
|
||||
var src = parser.statusList(jsonArray)
|
||||
list_tmp = addWithFilterStatus(null, src)
|
||||
|
||||
|
@ -2657,8 +2778,14 @@ class Column(
|
|||
break
|
||||
}
|
||||
|
||||
val path = path_base + delimiter + "max_id=" + max_id
|
||||
val result2 = client.request(path)
|
||||
val result2 = if(isMisskey) {
|
||||
params.put("untilId", max_id)
|
||||
client.request(path_base, params.toPostRequestBuilder())
|
||||
} else {
|
||||
val path = path_base + delimiter + "max_id=" + max_id
|
||||
client.request(path)
|
||||
}
|
||||
|
||||
jsonArray = result2?.jsonArray
|
||||
if(jsonArray == null) {
|
||||
log.d("refresh-status-bottom: error or cancelled.")
|
||||
|
@ -2669,9 +2796,16 @@ class Column(
|
|||
|
||||
addWithFilterStatus(list_tmp, src)
|
||||
|
||||
if(! saveRangeEnd(result2)) {
|
||||
log.d("refresh-status-bottom: saveRangeEnd failed.")
|
||||
break
|
||||
if(isMisskey) {
|
||||
if(! saveRangeEndMisskey(jsonArray)) {
|
||||
log.d("refresh-status-bottom: saveRangeEnd failed.")
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if(! saveRangeEnd(result2)) {
|
||||
log.d("refresh-status-bottom: saveRangeEnd failed.")
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -2711,6 +2845,13 @@ class Column(
|
|||
break
|
||||
}
|
||||
|
||||
if(isMisskey) {
|
||||
log.d("refresh-status-offset: misskey does not allows gap reading.")
|
||||
addOne(list_tmp, TootGap(max_id, last_since_id))
|
||||
bGapAdded = true
|
||||
break
|
||||
}
|
||||
|
||||
val path =
|
||||
path_base + delimiter + "max_id=" + max_id + "&since_id=" + last_since_id
|
||||
val result2 = client.request(path)
|
||||
|
@ -2767,9 +2908,23 @@ class Column(
|
|||
|
||||
TYPE_DIRECT_MESSAGES -> getStatusList(client, PATH_DIRECT_MESSAGES)
|
||||
|
||||
TYPE_LOCAL -> getStatusList(client, makePublicLocalUrl())
|
||||
TYPE_LOCAL -> when(access_info.isMisskey) {
|
||||
true -> getStatusList(
|
||||
client,
|
||||
"/api/notes/local-timeline",
|
||||
isMisskey = true
|
||||
)
|
||||
else -> getStatusList(client, makePublicLocalUrl())
|
||||
}
|
||||
|
||||
TYPE_FEDERATE -> getStatusList(client, makePublicFederateUrl())
|
||||
TYPE_FEDERATE -> when(access_info.isMisskey) {
|
||||
true -> getStatusList(
|
||||
client,
|
||||
"/api/notes/global-timeline",
|
||||
isMisskey = true
|
||||
)
|
||||
else -> getStatusList(client, makePublicFederateUrl())
|
||||
}
|
||||
|
||||
TYPE_FAVOURITES -> getStatusList(client, PATH_FAVOURITES)
|
||||
|
||||
|
@ -3015,15 +3170,15 @@ class Column(
|
|||
//
|
||||
val scroll_save = this@Column.scroll_save
|
||||
when {
|
||||
// ViewHolderがある場合は増加件数分+deltaの位置にスクロールする
|
||||
// ViewHolderがある場合は増加件数分+deltaの位置にスクロールする
|
||||
sp != null -> {
|
||||
sp.adapterIndex += added
|
||||
val delta = if(bSilent) 0f else - 20f
|
||||
holder?.setScrollPosition(sp, delta)
|
||||
}
|
||||
// ViewHolderがなくて保存中の位置がある場合、増加件数分ずらす。deltaは難しいので反映しない
|
||||
// ViewHolderがなくて保存中の位置がある場合、増加件数分ずらす。deltaは難しいので反映しない
|
||||
scroll_save != null -> scroll_save.adapterIndex += added
|
||||
// 保存中の位置がない場合、保存中の位置を新しく作る
|
||||
// 保存中の位置がない場合、保存中の位置を新しく作る
|
||||
else -> this@Column.scroll_save =
|
||||
ScrollPosition(toAdapterIndex(added), 0)
|
||||
}
|
||||
|
@ -3054,6 +3209,8 @@ class Column(
|
|||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
viewHolder?.refreshLayout?.isRefreshing = true
|
||||
|
||||
bRefreshLoading = true
|
||||
|
@ -3557,9 +3714,9 @@ class Column(
|
|||
log.d("onStart: column is in initial loading.")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// フィルタ一覧のリロードが必要
|
||||
if( filter_reload_required ){
|
||||
if(filter_reload_required) {
|
||||
filter_reload_required = false
|
||||
startLoading()
|
||||
return
|
||||
|
@ -3659,8 +3816,11 @@ class Column(
|
|||
return canStreaming() && column_type != TYPE_NOTIFICATIONS
|
||||
}
|
||||
|
||||
internal fun canStreaming() : Boolean {
|
||||
return ! access_info.isNA && if(access_info.isPseudo) isPublicStream else streamPath != null
|
||||
internal fun canStreaming() = when {
|
||||
access_info.isNA -> false
|
||||
access_info.isMisskey -> false
|
||||
access_info.isPseudo -> isPublicStream
|
||||
else -> streamPath != null
|
||||
}
|
||||
|
||||
private val streamCallback = object : StreamReader.StreamCallback {
|
||||
|
@ -3918,6 +4078,7 @@ class Column(
|
|||
} else {
|
||||
PATH_LOCAL
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun makePublicFederateUrl() : String {
|
||||
|
@ -3940,7 +4101,7 @@ class Column(
|
|||
}
|
||||
|
||||
private fun loadFilter2(client : TootApiClient) : ArrayList<TootFilter>? {
|
||||
if( access_info.isPseudo ) return null
|
||||
if(access_info.isPseudo) return null
|
||||
val column_context = getFilterContext()
|
||||
if(column_context == 0) return null
|
||||
val result = client.request(PATH_FILTERS)
|
||||
|
@ -3955,10 +4116,12 @@ class Column(
|
|||
val tree = WordTrieTree()
|
||||
for(filter in filterList) {
|
||||
if((filter.context and column_context) != 0) {
|
||||
tree.add(filter.phrase,validator = when(filter.whole_word){
|
||||
true -> WordTrieTree.WORD_VALIDATOR
|
||||
tree.add(
|
||||
filter.phrase, validator = when(filter.whole_word) {
|
||||
true -> WordTrieTree.WORD_VALIDATOR
|
||||
else -> WordTrieTree.EMPTY_VALIDATOR
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
return tree
|
||||
|
@ -4023,5 +4186,4 @@ class Column(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -1028,7 +1028,13 @@ internal class ItemViewHolder(
|
|||
}
|
||||
|
||||
btnSearchTag, llTrendTag -> when(item) {
|
||||
is TootGap -> column.startGap(item)
|
||||
is TootGap -> {
|
||||
if( access_info.isMisskey){
|
||||
showToast(activity,false, "Misskey does not allows gap reading.")
|
||||
}else {
|
||||
column.startGap(item)
|
||||
}
|
||||
}
|
||||
|
||||
is TootDomainBlock -> {
|
||||
val domain = item.domain
|
||||
|
|
|
@ -25,7 +25,7 @@ internal fun findAccountByName(
|
|||
) {
|
||||
TootTaskRunner(activity).run(access_info, object : TootTask {
|
||||
|
||||
internal var who : TootAccount? = null
|
||||
var who : TootAccount? = null
|
||||
|
||||
override fun background(client : TootApiClient) : TootApiResult? {
|
||||
|
||||
|
@ -38,10 +38,9 @@ internal fun findAccountByName(
|
|||
for(i in 0 until array.length()) {
|
||||
val a = parser.account(array.optJSONObject(i))
|
||||
if(a != null) {
|
||||
if(a.username == user && access_info.getFullAcct(a).equals(
|
||||
user + "@" + host,
|
||||
ignoreCase = true
|
||||
)) {
|
||||
if(a.username == user
|
||||
&& access_info.getFullAcct(a).equals("$user@$host", ignoreCase = true)
|
||||
) {
|
||||
who = a
|
||||
break
|
||||
}
|
||||
|
@ -62,11 +61,14 @@ internal fun findAccountByName(
|
|||
// 既に存在する場合は再利用する
|
||||
// 実アカウントを返すことはない
|
||||
internal fun addPseudoAccount(
|
||||
context : Context, host : String
|
||||
context : Context,
|
||||
host : String,
|
||||
isMisskey : Boolean = false
|
||||
) : SavedAccount? {
|
||||
|
||||
try {
|
||||
val username = "?"
|
||||
val full_acct = username + "@" + host
|
||||
val full_acct = "$username@$host"
|
||||
|
||||
var account = SavedAccount.loadAccountByAcct(context, full_acct)
|
||||
if(account != null) {
|
||||
|
@ -77,7 +79,8 @@ internal fun addPseudoAccount(
|
|||
account_info.put("username", username)
|
||||
account_info.put("acct", username)
|
||||
|
||||
val row_id = SavedAccount.insert(host, full_acct, account_info, JSONObject())
|
||||
val row_id =
|
||||
SavedAccount.insert(host, full_acct, account_info, JSONObject(), isMisskey = isMisskey)
|
||||
account = SavedAccount.loadAccount(context, row_id)
|
||||
if(account == null) {
|
||||
throw RuntimeException("loadAccount returns null.")
|
||||
|
@ -132,7 +135,7 @@ internal fun loadRelation1(
|
|||
client : TootApiClient, access_info : SavedAccount, who_id : Long
|
||||
) : RelationResult {
|
||||
val rr = RelationResult()
|
||||
rr.result = client.request("/api/v1/accounts/relationships?id=" + who_id)
|
||||
rr.result = client.request("/api/v1/accounts/relationships?id=$who_id")
|
||||
val r2 = rr.result
|
||||
val jsonArray = r2?.jsonArray
|
||||
if(jsonArray != null) {
|
||||
|
|
|
@ -95,7 +95,7 @@ object Action_Account {
|
|||
|
||||
} else {
|
||||
// 疑似アカウントを追加
|
||||
val a = addPseudoAccount(activity, instance)
|
||||
val a = addPseudoAccount(activity, instance, data.optBoolean("isMisskey",false))
|
||||
if(a != null) {
|
||||
showToast(activity, false, R.string.server_confirmed)
|
||||
val pos = App1.getAppState(activity).column_list.size
|
||||
|
|
|
@ -18,6 +18,7 @@ object TootAccountMap{
|
|||
ServiceType.MASTODON -> requireNotNull(parser.linkHelper.host)
|
||||
ServiceType.TOOTSEARCH -> "?tootsearch"
|
||||
ServiceType.MSP -> "?msp"
|
||||
ServiceType.MISSKEY -> "?misskey"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -445,10 +445,36 @@ class TootApiClient(
|
|||
fun getInstanceInformation() : TootApiResult? {
|
||||
val result = TootApiResult.makeWithCaption(instance)
|
||||
if(result.error != null) return result
|
||||
if(! sendRequest(result) {
|
||||
|
||||
if(sendRequest(result) {
|
||||
Request.Builder().url("https://$instance/api/v1/instance").build()
|
||||
}) return result
|
||||
return parseJson(result)
|
||||
}
|
||||
&& parseJson(result) != null
|
||||
&& result.jsonObject != null
|
||||
) {
|
||||
// インスタンス情報のjsonを読めたらマストドンのインスタンス
|
||||
return result
|
||||
}
|
||||
|
||||
// misskeyか試してみる
|
||||
val r2 = TootApiResult.makeWithCaption(instance)
|
||||
if(sendRequest(r2) {
|
||||
Request.Builder().post(RequestBody.create(MEDIA_TYPE_JSON,JSONObject().apply{
|
||||
put("dummy",1)
|
||||
}.toString()))
|
||||
.url("https://$instance/api/notes/local-timeline").build()
|
||||
}
|
||||
) {
|
||||
if(parseJson(r2) != null && r2.jsonArray != null) {
|
||||
r2.data = JSONObject().apply{
|
||||
put("isMisskey", true)
|
||||
}
|
||||
return r2
|
||||
}
|
||||
}
|
||||
|
||||
// misskeyの事は忘れて本来のエラー情報を返す
|
||||
return result
|
||||
}
|
||||
|
||||
// インスタンス情報を取得する
|
||||
|
@ -845,7 +871,7 @@ class TootApiClient(
|
|||
////////////////////////////////////////////////////////////////////////
|
||||
// JSONデータ以外を扱うリクエスト
|
||||
|
||||
fun http(req:Request) : TootApiResult? {
|
||||
fun http(req : Request) : TootApiResult? {
|
||||
val result = TootApiResult.makeWithCaption(req.url().host())
|
||||
if(result.error != null) return result
|
||||
|
||||
|
@ -853,20 +879,19 @@ class TootApiClient(
|
|||
return result
|
||||
}
|
||||
|
||||
fun requestJson(req:Request) : TootApiResult? {
|
||||
fun requestJson(req : Request) : TootApiResult? {
|
||||
val result = TootApiResult.makeWithCaption(req.url().host())
|
||||
if(result.error != null) return result
|
||||
if( sendRequest(result, progressPath = null) { req } ){
|
||||
if(sendRequest(result, progressPath = null) { req }) {
|
||||
parseJson(result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
// 疑似アカウントでステータスURLからステータスIDを取得するためにHTMLを取得する
|
||||
fun getHttp(url:String): TootApiResult? {
|
||||
fun getHttp(url : String) : TootApiResult? {
|
||||
val result = http(Request.Builder().url(url).build())
|
||||
if(result !=null && result.error == null){
|
||||
if(result != null && result.error == null) {
|
||||
parseString(result)
|
||||
}
|
||||
return result
|
||||
|
|
|
@ -4,4 +4,5 @@ enum class ServiceType {
|
|||
MASTODON,
|
||||
TOOTSEARCH,
|
||||
MSP,
|
||||
MISSKEY,
|
||||
}
|
||||
|
|
|
@ -90,106 +90,155 @@ open class TootAccount(
|
|||
init {
|
||||
var sv : String?
|
||||
|
||||
// 絵文字データは先に読んでおく
|
||||
this.custom_emojis = parseMapOrNull(::CustomEmoji, src.optJSONArray("emojis"))
|
||||
this.profile_emojis = parseMapOrNull(::NicoProfileEmoji, src.optJSONArray("profile_emojis"))
|
||||
|
||||
// 疑似アカウントにacctとusernameだけ
|
||||
this.url = src.parseString("url")
|
||||
this.username = src.notEmptyOrThrow("username")
|
||||
|
||||
//
|
||||
sv = src.parseString("display_name")
|
||||
this.display_name = if(sv?.isNotEmpty() == true) sv.sanitizeBDI() else username
|
||||
|
||||
//
|
||||
this.note = src.parseString("note")
|
||||
|
||||
this.source = parseSource(src.optJSONObject("source"))
|
||||
this.movedRef = TootAccountRef.mayNull(
|
||||
parser,
|
||||
src.optJSONObject("moved")?.let {
|
||||
TootAccount(parser, it)
|
||||
}
|
||||
)
|
||||
this.locked = src.optBoolean("locked")
|
||||
|
||||
this.fields = parseFields(src.optJSONArray("fields"))
|
||||
|
||||
this.bot = src.optBoolean("bot", false)
|
||||
|
||||
// this.user_hides_network = src.optBoolean("user_hides_network")
|
||||
|
||||
when(parser.serviceType) {
|
||||
ServiceType.MASTODON -> {
|
||||
|
||||
val hostAccess = parser.linkHelper.host
|
||||
|
||||
this.id = src.parseLong("id") ?: INVALID_ID
|
||||
|
||||
this.acct = src.notEmptyOrThrow("acct")
|
||||
this.host = findHostFromUrl(acct, hostAccess, url)
|
||||
?: throw RuntimeException("can't get host from acct or url")
|
||||
|
||||
this.followers_count = src.parseLong("followers_count")
|
||||
this.following_count = src.parseLong("following_count")
|
||||
this.statuses_count = src.parseLong("statuses_count")
|
||||
|
||||
this.created_at = src.parseString("created_at")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
|
||||
this.avatar = src.parseString("avatar")
|
||||
this.avatar_static = src.parseString("avatar_static")
|
||||
this.header = src.parseString("header")
|
||||
this.header_static = src.parseString("header_static")
|
||||
|
||||
}
|
||||
if(parser.serviceType == ServiceType.MISSKEY) {
|
||||
|
||||
ServiceType.TOOTSEARCH -> {
|
||||
// tootsearch のアカウントのIDはどのタンス上のものか分からないので役に立たない
|
||||
this.id = INVALID_ID // src.parseLong( "id", INVALID_ID)
|
||||
|
||||
sv = src.notEmptyOrThrow("acct")
|
||||
this.host = findHostFromUrl(sv, null, url)
|
||||
?: throw RuntimeException("can't get host from acct or url")
|
||||
this.acct = this.username + "@" + this.host
|
||||
|
||||
this.followers_count = src.parseLong("followers_count")
|
||||
this.following_count = src.parseLong("following_count")
|
||||
this.statuses_count = src.parseLong("statuses_count")
|
||||
|
||||
this.created_at = src.parseString("created_at")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
|
||||
this.avatar = src.parseString("avatar")
|
||||
this.avatar_static = src.parseString("avatar_static")
|
||||
this.header = src.parseString("header")
|
||||
this.header_static = src.parseString("header_static")
|
||||
}
|
||||
val instance = src.parseString("host") ?: parser.linkHelper.host ?: error("missing host")
|
||||
|
||||
ServiceType.MSP -> {
|
||||
this.id = src.parseLong("id") ?: INVALID_ID
|
||||
|
||||
// MSPはLTLの情報しか持ってないのでacctは常にホスト名部分を持たない
|
||||
this.host = findHostFromUrl(null, null, url)
|
||||
?: throw RuntimeException("can't get host from url")
|
||||
this.acct = this.username + "@" + host
|
||||
|
||||
this.followers_count = null
|
||||
this.following_count = null
|
||||
this.statuses_count = null
|
||||
|
||||
this.created_at = null
|
||||
this.time_created_at = 0L
|
||||
|
||||
val avatar = src.parseString("avatar")
|
||||
this.avatar = avatar
|
||||
this.avatar_static = avatar
|
||||
this.header = null
|
||||
this.header_static = null
|
||||
|
||||
}
|
||||
this.custom_emojis = null
|
||||
this.profile_emojis = null
|
||||
|
||||
this.username = src.notEmptyOrThrow("username")
|
||||
this.url = "https://$instance/@$username"
|
||||
|
||||
//
|
||||
sv = src.parseString("name")
|
||||
this.display_name = if(sv?.isNotEmpty() == true) sv.sanitizeBDI() else username
|
||||
|
||||
//
|
||||
this.note = src.parseString("description")
|
||||
|
||||
this.source = null
|
||||
this.movedRef = null
|
||||
this.locked = src.optBoolean("isLocked")
|
||||
|
||||
this.fields = null
|
||||
|
||||
this.bot = src.optBoolean("isBot", false)
|
||||
|
||||
// this.user_hides_network = src.optBoolean("user_hides_network")
|
||||
|
||||
this.id = INVALID_ID
|
||||
|
||||
this.acct = "$username@$instance"
|
||||
this.host = instance
|
||||
|
||||
this.followers_count = src.parseLong("followersCount") ?: -1L
|
||||
this.following_count = src.parseLong("followingCount") ?: -1L
|
||||
this.statuses_count = src.parseLong("notesCount") ?: -1L
|
||||
|
||||
this.created_at = src.parseString("createdAt")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
|
||||
this.avatar = src.parseString("avatarUrl")
|
||||
this.avatar_static = src.parseString("avatarUrl")
|
||||
this.header =src.parseString("bannerUrl")
|
||||
this.header_static = src.parseString("bannerUrl")
|
||||
|
||||
} else {
|
||||
|
||||
// 絵文字データは先に読んでおく
|
||||
this.custom_emojis = parseMapOrNull(::CustomEmoji, src.optJSONArray("emojis"))
|
||||
this.profile_emojis =
|
||||
parseMapOrNull(::NicoProfileEmoji, src.optJSONArray("profile_emojis"))
|
||||
|
||||
// 疑似アカウントにacctとusernameだけ
|
||||
this.url = src.parseString("url")
|
||||
this.username = src.notEmptyOrThrow("username")
|
||||
|
||||
//
|
||||
sv = src.parseString("display_name")
|
||||
this.display_name = if(sv?.isNotEmpty() == true) sv.sanitizeBDI() else username
|
||||
|
||||
//
|
||||
this.note = src.parseString("note")
|
||||
|
||||
this.source = parseSource(src.optJSONObject("source"))
|
||||
this.movedRef = TootAccountRef.mayNull(
|
||||
parser,
|
||||
src.optJSONObject("moved")?.let {
|
||||
TootAccount(parser, it)
|
||||
}
|
||||
)
|
||||
this.locked = src.optBoolean("locked")
|
||||
|
||||
this.fields = parseFields(src.optJSONArray("fields"))
|
||||
|
||||
this.bot = src.optBoolean("bot", false)
|
||||
|
||||
// this.user_hides_network = src.optBoolean("user_hides_network")
|
||||
|
||||
when(parser.serviceType) {
|
||||
ServiceType.MASTODON -> {
|
||||
|
||||
val hostAccess = parser.linkHelper.host
|
||||
|
||||
this.id = src.parseLong("id") ?: INVALID_ID
|
||||
|
||||
this.acct = src.notEmptyOrThrow("acct")
|
||||
this.host = findHostFromUrl(acct, hostAccess, url)
|
||||
?: throw RuntimeException("can't get host from acct or url")
|
||||
|
||||
this.followers_count = src.parseLong("followers_count")
|
||||
this.following_count = src.parseLong("following_count")
|
||||
this.statuses_count = src.parseLong("statuses_count")
|
||||
|
||||
this.created_at = src.parseString("created_at")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
|
||||
this.avatar = src.parseString("avatar")
|
||||
this.avatar_static = src.parseString("avatar_static")
|
||||
this.header = src.parseString("header")
|
||||
this.header_static = src.parseString("header_static")
|
||||
|
||||
}
|
||||
|
||||
ServiceType.TOOTSEARCH -> {
|
||||
// tootsearch のアカウントのIDはどのタンス上のものか分からないので役に立たない
|
||||
this.id = INVALID_ID // src.parseLong( "id", INVALID_ID)
|
||||
|
||||
sv = src.notEmptyOrThrow("acct")
|
||||
this.host = findHostFromUrl(sv, null, url)
|
||||
?: throw RuntimeException("can't get host from acct or url")
|
||||
this.acct = this.username + "@" + this.host
|
||||
|
||||
this.followers_count = src.parseLong("followers_count")
|
||||
this.following_count = src.parseLong("following_count")
|
||||
this.statuses_count = src.parseLong("statuses_count")
|
||||
|
||||
this.created_at = src.parseString("created_at")
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
|
||||
this.avatar = src.parseString("avatar")
|
||||
this.avatar_static = src.parseString("avatar_static")
|
||||
this.header = src.parseString("header")
|
||||
this.header_static = src.parseString("header_static")
|
||||
}
|
||||
|
||||
ServiceType.MSP -> {
|
||||
this.id = src.parseLong("id") ?: INVALID_ID
|
||||
|
||||
// MSPはLTLの情報しか持ってないのでacctは常にホスト名部分を持たない
|
||||
this.host = findHostFromUrl(null, null, url)
|
||||
?: throw RuntimeException("can't get host from url")
|
||||
this.acct = this.username + "@" + host
|
||||
|
||||
this.followers_count = null
|
||||
this.following_count = null
|
||||
this.statuses_count = null
|
||||
|
||||
this.created_at = null
|
||||
this.time_created_at = 0L
|
||||
|
||||
val avatar = src.parseString("avatar")
|
||||
this.avatar = avatar
|
||||
this.avatar_static = avatar
|
||||
this.header = null
|
||||
this.header_static = null
|
||||
|
||||
}
|
||||
|
||||
else -> error("will not happen")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,4 +13,9 @@ class TootApplication(
|
|||
name = src.parseString("name"),
|
||||
website = src.parseString("website")
|
||||
)
|
||||
|
||||
constructor(src:String?):this(
|
||||
name = src,
|
||||
website = null
|
||||
)
|
||||
}
|
||||
|
|
|
@ -18,8 +18,10 @@ import jp.juggler.subwaytooter.api.TootParser
|
|||
import jp.juggler.subwaytooter.table.HighlightWord
|
||||
import jp.juggler.subwaytooter.table.SavedAccount
|
||||
import jp.juggler.subwaytooter.util.*
|
||||
import org.json.JSONArray
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
@Suppress("MemberVisibilityCanPrivate")
|
||||
class TootStatus(parser : TootParser, src : JSONObject) :
|
||||
|
@ -155,133 +157,271 @@ class TootStatus(parser : TootParser, src : JSONObject) :
|
|||
init {
|
||||
this.json = src
|
||||
|
||||
this.uri = src.parseString("uri") // MSPだとuriは提供されない
|
||||
this.url = src.parseString("url") // 頻繁にnullになる
|
||||
this.created_at = src.parseString("created_at")
|
||||
|
||||
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
|
||||
this.custom_emojis = parseMapOrNull(::CustomEmoji, src.optJSONArray("emojis"), log)
|
||||
this.profile_emojis =
|
||||
parseMapOrNull(::NicoProfileEmoji, src.optJSONArray("profile_emojis"), log)
|
||||
|
||||
val who = parser.account(src.optJSONObject("account"))
|
||||
?: throw RuntimeException("missing account")
|
||||
|
||||
this.accountRef = TootAccountRef(parser, who)
|
||||
|
||||
this.reblogs_count = src.parseLong("reblogs_count")
|
||||
this.favourites_count = src.parseLong("favourites_count")
|
||||
this.replies_count = src.parseLong("replies_count")
|
||||
if( parser.serviceType == ServiceType.MISSKEY) {
|
||||
val instance = parser.linkHelper.host
|
||||
val misskeyId = src.parseString("id")
|
||||
this.host_access = parser.linkHelper.host
|
||||
|
||||
this.uri = "https://$instance/notes/$misskeyId"
|
||||
this.url = "https://$instance/notes/$misskeyId"
|
||||
this.created_at = src.parseString("createdAt")
|
||||
this.time_created_at = parseTime(this.created_at)
|
||||
this.id = INVALID_ID
|
||||
|
||||
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
|
||||
this.custom_emojis = null
|
||||
this.profile_emojis = null
|
||||
|
||||
val who = parser.account(src.optJSONObject("user"))
|
||||
?: throw RuntimeException("missing account")
|
||||
|
||||
this.accountRef = TootAccountRef(parser, who)
|
||||
|
||||
this.reblogs_count = 0L
|
||||
this.favourites_count = 0L
|
||||
this.replies_count = 0L
|
||||
|
||||
this.reblogged = false
|
||||
this.favourited = false
|
||||
|
||||
this.media_attachments = parseMediaAttachmentMisskey(src.optJSONArray("media"))
|
||||
this.visibility = src.parseString("visibility")
|
||||
this.sensitive = src.optBoolean("sensitive")
|
||||
|
||||
|
||||
this.in_reply_to_id = null
|
||||
this.in_reply_to_account_id = null
|
||||
this.mentions = null
|
||||
this.tags = null
|
||||
this.application = parseItem(::TootApplication, src.optJSONObject("appId"), log)
|
||||
this.pinned = parser.pinned
|
||||
this.muted = false
|
||||
this.language = null
|
||||
|
||||
this.decoded_mentions = HTMLDecoder.decodeMentions(
|
||||
parser.linkHelper,
|
||||
this.mentions,
|
||||
this
|
||||
) ?: EMPTY_SPANNABLE
|
||||
|
||||
// this.decoded_tags = HTMLDecoder.decodeTags( account,status.tags );
|
||||
|
||||
// content
|
||||
this.content = src.parseString("text")
|
||||
|
||||
var options = DecodeOptions(
|
||||
parser.context,
|
||||
parser.linkHelper,
|
||||
short = true,
|
||||
decodeEmoji = true,
|
||||
emojiMapCustom = custom_emojis,
|
||||
emojiMapProfile = profile_emojis,
|
||||
attachmentList = media_attachments,
|
||||
highlightTrie = parser.highlightTrie
|
||||
)
|
||||
|
||||
this.decoded_content = options.decodeHTML(content)
|
||||
this.hasHighlight = this.hasHighlight || options.hasHighlight
|
||||
if(options.highlight_sound != null && this.highlight_sound == null) {
|
||||
this.highlight_sound = options.highlight_sound
|
||||
}
|
||||
|
||||
// spoiler_text
|
||||
this.spoiler_text = reWhitespace
|
||||
.matcher(src.parseString("cw") ?: "")
|
||||
.replaceAll(" ")
|
||||
.sanitizeBDI()
|
||||
|
||||
options = DecodeOptions(
|
||||
parser.context,
|
||||
emojiMapCustom = custom_emojis,
|
||||
emojiMapProfile = profile_emojis,
|
||||
highlightTrie = parser.highlightTrie
|
||||
)
|
||||
|
||||
this.decoded_spoiler_text = options.decodeEmoji(spoiler_text)
|
||||
|
||||
this.hasHighlight = this.hasHighlight || options.hasHighlight
|
||||
if(options.highlight_sound != null && this.highlight_sound == null) {
|
||||
this.highlight_sound = options.highlight_sound
|
||||
}
|
||||
|
||||
this.enquete = null
|
||||
|
||||
this.reblog = parser.status(src.optJSONObject("renote"))
|
||||
|
||||
when(parser.serviceType) {
|
||||
ServiceType.MASTODON -> {
|
||||
this.host_access = parser.linkHelper.host
|
||||
}else{
|
||||
this.uri = src.parseString("uri") // MSPだとuriは提供されない
|
||||
this.url = src.parseString("url") // 頻繁にnullになる
|
||||
this.created_at = src.parseString("created_at")
|
||||
|
||||
// 絵文字マップはすぐ後で使うので、最初の方で読んでおく
|
||||
this.custom_emojis = parseMapOrNull(::CustomEmoji, src.optJSONArray("emojis"), log)
|
||||
this.profile_emojis =
|
||||
parseMapOrNull(::NicoProfileEmoji, src.optJSONArray("profile_emojis"), log)
|
||||
|
||||
val who = parser.account(src.optJSONObject("account"))
|
||||
?: throw RuntimeException("missing account")
|
||||
|
||||
this.accountRef = TootAccountRef(parser, who)
|
||||
|
||||
this.reblogs_count = src.parseLong("reblogs_count")
|
||||
this.favourites_count = src.parseLong("favourites_count")
|
||||
this.replies_count = src.parseLong("replies_count")
|
||||
|
||||
when(parser.serviceType) {
|
||||
ServiceType.MASTODON -> {
|
||||
this.host_access = parser.linkHelper.host
|
||||
|
||||
this.id = src.parseLong("id") ?: INVALID_ID
|
||||
|
||||
this.reblogged = src.optBoolean("reblogged")
|
||||
this.favourited = src.optBoolean("favourited")
|
||||
|
||||
this.time_created_at = parseTime(this.created_at)
|
||||
this.media_attachments =
|
||||
parseListOrNull(::TootAttachment, src.optJSONArray("media_attachments"), log)
|
||||
this.visibility = src.parseString("visibility")
|
||||
this.sensitive = src.optBoolean("sensitive")
|
||||
|
||||
}
|
||||
|
||||
this.id = src.parseLong("id") ?: INVALID_ID
|
||||
|
||||
this.reblogged = src.optBoolean("reblogged")
|
||||
this.favourited = src.optBoolean("favourited")
|
||||
|
||||
this.time_created_at = parseTime(this.created_at)
|
||||
this.media_attachments =
|
||||
parseListOrNull(::TootAttachment, src.optJSONArray("media_attachments"), log)
|
||||
this.visibility = src.parseString("visibility")
|
||||
this.sensitive = src.optBoolean("sensitive")
|
||||
ServiceType.TOOTSEARCH -> {
|
||||
this.host_access = null
|
||||
|
||||
// 投稿元タンスでのIDを調べる。失敗するかもしれない
|
||||
this.id = findStatusIdFromUri(uri, url)
|
||||
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
this.media_attachments =
|
||||
parseListOrNull(::TootAttachment, src.optJSONArray("media_attachments"), log)
|
||||
this.visibility = VISIBILITY_PUBLIC
|
||||
this.sensitive = src.optBoolean("sensitive")
|
||||
|
||||
}
|
||||
|
||||
ServiceType.MSP -> {
|
||||
this.host_access = null
|
||||
|
||||
// MSPのデータはLTLから呼んだものなので、常に投稿元タンスでのidが得られる
|
||||
this.id = src.parseLong("id") ?: INVALID_ID
|
||||
|
||||
this.time_created_at = parseTimeMSP(created_at)
|
||||
this.media_attachments =
|
||||
TootAttachmentMSP.parseList(src.optJSONArray("media_attachments"))
|
||||
this.visibility = VISIBILITY_PUBLIC
|
||||
this.sensitive = src.optInt("sensitive", 0) != 0
|
||||
}
|
||||
else-> error("will not happen")
|
||||
}
|
||||
|
||||
ServiceType.TOOTSEARCH -> {
|
||||
this.host_access = null
|
||||
|
||||
// 投稿元タンスでのIDを調べる。失敗するかもしれない
|
||||
this.id = findStatusIdFromUri(uri, url)
|
||||
|
||||
this.time_created_at = TootStatus.parseTime(this.created_at)
|
||||
this.media_attachments =
|
||||
parseListOrNull(::TootAttachment, src.optJSONArray("media_attachments"), log)
|
||||
this.visibility = VISIBILITY_PUBLIC
|
||||
this.sensitive = src.optBoolean("sensitive")
|
||||
|
||||
this.in_reply_to_id = src.parseString("in_reply_to_id")
|
||||
this.in_reply_to_account_id = src.parseString("in_reply_to_account_id")
|
||||
this.mentions = parseListOrNull(::TootMention, src.optJSONArray("mentions"), log)
|
||||
this.tags = parseListOrNull(::TootTag, src.optJSONArray("tags"))
|
||||
this.application = parseItem(::TootApplication, src.optJSONObject("application"), log)
|
||||
this.pinned = parser.pinned || src.optBoolean("pinned")
|
||||
this.muted = src.optBoolean("muted")
|
||||
this.language = src.parseString("language")
|
||||
this.decoded_mentions = HTMLDecoder.decodeMentions(
|
||||
parser.linkHelper,
|
||||
this.mentions,
|
||||
this
|
||||
) ?: EMPTY_SPANNABLE
|
||||
// this.decoded_tags = HTMLDecoder.decodeTags( account,status.tags );
|
||||
|
||||
// content
|
||||
this.content = src.parseString("content")
|
||||
|
||||
var options = DecodeOptions(
|
||||
parser.context,
|
||||
parser.linkHelper,
|
||||
short = true,
|
||||
decodeEmoji = true,
|
||||
emojiMapCustom = custom_emojis,
|
||||
emojiMapProfile = profile_emojis,
|
||||
attachmentList = media_attachments,
|
||||
highlightTrie = parser.highlightTrie
|
||||
)
|
||||
|
||||
this.decoded_content = options.decodeHTML(content)
|
||||
this.hasHighlight = this.hasHighlight || options.hasHighlight
|
||||
if(options.highlight_sound != null && this.highlight_sound == null) {
|
||||
this.highlight_sound = options.highlight_sound
|
||||
}
|
||||
|
||||
ServiceType.MSP -> {
|
||||
this.host_access = null
|
||||
|
||||
// MSPのデータはLTLから呼んだものなので、常に投稿元タンスでのidが得られる
|
||||
this.id = src.parseLong("id") ?: INVALID_ID
|
||||
|
||||
this.time_created_at = parseTimeMSP(created_at)
|
||||
this.media_attachments =
|
||||
TootAttachmentMSP.parseList(src.optJSONArray("media_attachments"))
|
||||
this.visibility = VISIBILITY_PUBLIC
|
||||
this.sensitive = src.optInt("sensitive", 0) != 0
|
||||
// spoiler_text
|
||||
this.spoiler_text = reWhitespace
|
||||
.matcher(src.parseString("spoiler_text") ?: "")
|
||||
.replaceAll(" ")
|
||||
.sanitizeBDI()
|
||||
|
||||
options = DecodeOptions(
|
||||
parser.context,
|
||||
emojiMapCustom = custom_emojis,
|
||||
emojiMapProfile = profile_emojis,
|
||||
highlightTrie = parser.highlightTrie
|
||||
)
|
||||
|
||||
this.decoded_spoiler_text = options.decodeEmoji(spoiler_text)
|
||||
|
||||
this.hasHighlight = this.hasHighlight || options.hasHighlight
|
||||
if(options.highlight_sound != null && this.highlight_sound == null) {
|
||||
this.highlight_sound = options.highlight_sound
|
||||
}
|
||||
|
||||
this.enquete = NicoEnquete.parse(
|
||||
parser,
|
||||
this,
|
||||
media_attachments,
|
||||
src.parseString("enquete")
|
||||
)
|
||||
|
||||
// Pinned TL を取得した時にreblogが登場することはないので、reblogについてpinned 状態を気にする必要はない
|
||||
this.reblog = parser.status(src.optJSONObject("reblog"))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseMediaAttachmentMisskey2(src : JSONObject?) : TootAttachment? {
|
||||
src?: return null
|
||||
|
||||
val mimeType = src.parseString("type")
|
||||
val url = src.parseString("url")
|
||||
val thumbnailUrl = src.parseString("thumbnailUrl")
|
||||
val dst = JSONObject()
|
||||
dst.put("id",-1L)
|
||||
dst.put("type", when{
|
||||
mimeType?.startsWith("image/") ==true -> TootAttachmentLike.TYPE_IMAGE
|
||||
mimeType?.startsWith("video/") ==true -> TootAttachmentLike.TYPE_VIDEO
|
||||
else-> TootAttachmentLike.TYPE_UNKNOWN
|
||||
})
|
||||
dst.put("url",url)
|
||||
dst.put("remote_url",url)
|
||||
dst.put("text_url",url)
|
||||
dst.put("preview_url",thumbnailUrl)
|
||||
dst.put("description",src.parseString("comment"))
|
||||
|
||||
return parseItem(::TootAttachment,dst)
|
||||
}
|
||||
|
||||
private fun parseMediaAttachmentMisskey(src : JSONArray?) : ArrayList<TootAttachmentLike>? {
|
||||
var rv :ArrayList<TootAttachmentLike>? = null
|
||||
if(src!=null){
|
||||
for(i in 0 until src.length() ){
|
||||
val item = try{
|
||||
parseMediaAttachmentMisskey2(src.optJSONObject(i))
|
||||
}catch(ex:Throwable){
|
||||
log.e(ex,"parseMediaAttachmentMisskey")
|
||||
null
|
||||
}
|
||||
if( item != null ){
|
||||
if(rv==null) rv = ArrayList()
|
||||
rv.add(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.in_reply_to_id = src.parseString("in_reply_to_id")
|
||||
this.in_reply_to_account_id = src.parseString("in_reply_to_account_id")
|
||||
this.mentions = parseListOrNull(::TootMention, src.optJSONArray("mentions"), log)
|
||||
this.tags = parseListOrNull(::TootTag, src.optJSONArray("tags"))
|
||||
this.application = parseItem(::TootApplication, src.optJSONObject("application"), log)
|
||||
this.pinned = parser.pinned || src.optBoolean("pinned")
|
||||
this.muted = src.optBoolean("muted")
|
||||
this.language = src.parseString("language")
|
||||
this.decoded_mentions = HTMLDecoder.decodeMentions(
|
||||
parser.linkHelper,
|
||||
this.mentions,
|
||||
this
|
||||
) ?: EMPTY_SPANNABLE
|
||||
// this.decoded_tags = HTMLDecoder.decodeTags( account,status.tags );
|
||||
|
||||
// content
|
||||
this.content = src.parseString("content")
|
||||
|
||||
var options = DecodeOptions(
|
||||
parser.context,
|
||||
parser.linkHelper,
|
||||
short = true,
|
||||
decodeEmoji = true,
|
||||
emojiMapCustom = custom_emojis,
|
||||
emojiMapProfile = profile_emojis,
|
||||
attachmentList = media_attachments,
|
||||
highlightTrie = parser.highlightTrie
|
||||
)
|
||||
|
||||
this.decoded_content = options.decodeHTML(content)
|
||||
this.hasHighlight = this.hasHighlight || options.hasHighlight
|
||||
if(options.highlight_sound != null && this.highlight_sound == null) {
|
||||
this.highlight_sound = options.highlight_sound
|
||||
}
|
||||
|
||||
// spoiler_text
|
||||
this.spoiler_text = reWhitespace
|
||||
.matcher(src.parseString("spoiler_text") ?: "")
|
||||
.replaceAll(" ")
|
||||
.sanitizeBDI()
|
||||
|
||||
options = DecodeOptions(
|
||||
parser.context,
|
||||
emojiMapCustom = custom_emojis,
|
||||
emojiMapProfile = profile_emojis,
|
||||
highlightTrie = parser.highlightTrie
|
||||
)
|
||||
|
||||
this.decoded_spoiler_text = options.decodeEmoji(spoiler_text)
|
||||
|
||||
this.hasHighlight = this.hasHighlight || options.hasHighlight
|
||||
if(options.highlight_sound != null && this.highlight_sound == null) {
|
||||
this.highlight_sound = options.highlight_sound
|
||||
}
|
||||
|
||||
this.enquete = NicoEnquete.parse(
|
||||
parser,
|
||||
this,
|
||||
media_attachments,
|
||||
src.parseString("enquete")
|
||||
)
|
||||
|
||||
// Pinned TL を取得した時にreblogが登場することはないので、reblogについてpinned 状態を気にする必要はない
|
||||
this.reblog = parser.status(src.optJSONObject("reblog"))
|
||||
return rv
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
|
|
@ -28,7 +28,8 @@ class SavedAccount(
|
|||
val acct : String,
|
||||
hostArg : String? = null,
|
||||
var token_info : JSONObject? = null,
|
||||
var loginAccount : TootAccount? = null // 疑似アカウントではnull
|
||||
var loginAccount : TootAccount? = null, // 疑似アカウントではnull
|
||||
var isMisskey :Boolean = false // 疑似アカウントでのみtrue
|
||||
) : LinkHelper {
|
||||
|
||||
val username : String
|
||||
|
@ -156,6 +157,8 @@ class SavedAccount(
|
|||
this.sound_uri = cursor.getString(cursor.getColumnIndex(COL_SOUND_URI))
|
||||
|
||||
this.default_text = cursor.getString(cursor.getColumnIndex(COL_DEFAULT_TEXT)) ?: ""
|
||||
|
||||
this.isMisskey = cursor.getInt(cursor.getColumnIndex(COL_IS_MISSKEY)).i2b()
|
||||
}
|
||||
|
||||
val isNA : Boolean
|
||||
|
@ -405,6 +408,9 @@ class SavedAccount(
|
|||
// スキーマ27から
|
||||
private const val COL_DEFAULT_TEXT = "default_text"
|
||||
|
||||
// スキーマ28から
|
||||
private const val COL_IS_MISSKEY = "is_misskey"
|
||||
|
||||
/////////////////////////////////
|
||||
// login information
|
||||
const val INVALID_DB_ID = - 1L
|
||||
|
@ -466,6 +472,8 @@ class SavedAccount(
|
|||
// 以下はDBスキーマ27で更新
|
||||
+ ",$COL_DEFAULT_TEXT text default ''"
|
||||
|
||||
// 以下はDBスキーマ28で更新
|
||||
+ ",$COL_IS_MISSKEY integer default 0"
|
||||
+ ")"
|
||||
)
|
||||
db.execSQL("create index if not exists ${table}_user on ${table}(u)")
|
||||
|
@ -592,7 +600,14 @@ class SavedAccount(
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
if(oldVersion < 28 && newVersion >= 28) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_IS_MISSKEY integer default 0")
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 横断検索用の、何とも紐ついていないアカウント
|
||||
|
@ -621,7 +636,8 @@ class SavedAccount(
|
|||
host : String,
|
||||
acct : String,
|
||||
account : JSONObject,
|
||||
token : JSONObject
|
||||
token : JSONObject,
|
||||
isMisskey : Boolean = false
|
||||
) : Long {
|
||||
try {
|
||||
val cv = ContentValues()
|
||||
|
@ -629,6 +645,7 @@ class SavedAccount(
|
|||
cv.put(COL_USER, acct)
|
||||
cv.put(COL_ACCOUNT, account.toString())
|
||||
cv.put(COL_TOKEN, token.toString())
|
||||
cv.put(COL_IS_MISSKEY, isMisskey.b2i() )
|
||||
return App1.database.insert(table, null, cv)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
|
|
|
@ -20,7 +20,10 @@ import android.view.ViewGroup
|
|||
import android.view.inputmethod.InputMethodManager
|
||||
import android.webkit.MimeTypeMap
|
||||
import android.widget.Toast
|
||||
import jp.juggler.subwaytooter.api.TootApiClient
|
||||
import me.drakeet.support.toast.ToastCompat
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import org.apache.commons.io.IOUtils
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
@ -651,6 +654,9 @@ fun JSONObject.parseInt(key : String) : Int? {
|
|||
}
|
||||
}
|
||||
|
||||
fun JSONObject.toPostRequestBuilder()=
|
||||
Request.Builder().post(RequestBody.create(TootApiClient.MEDIA_TYPE_JSON,this.toString()))
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Bundle
|
||||
|
||||
|
|
|
@ -836,6 +836,8 @@ mimumedon.com
|
|||
mindful.masto.host
|
||||
minidon.bacardi55.org
|
||||
misanthropy.wang
|
||||
misskey.xyz
|
||||
misskey.jp
|
||||
mist.so
|
||||
mistermi.me
|
||||
mn.kitetu.com
|
||||
|
|
Loading…
Reference in New Issue