検索API v1が削除される件の対策 https://github.com/tootsuite/mastodon/pull/11823
This commit is contained in:
parent
07ab2e3f52
commit
cf3c56ce05
|
@ -121,8 +121,6 @@ class Column(
|
|||
internal const val PATH_ACCOUNT = "/api/v1/accounts/%s" // 1:account_id
|
||||
internal const val PATH_STATUSES = "/api/v1/statuses/%s" // 1:status_id
|
||||
internal const val PATH_STATUSES_CONTEXT = "/api/v1/statuses/%s/context" // 1:status_id
|
||||
const val PATH_SEARCH = "/api/v1/search?q=%s"
|
||||
const val PATH_SEARCH_V2 = "/api/v2/search?q=%s"
|
||||
// search args 1: query(urlencoded) , also, append "&resolve=1" if resolve non-local accounts
|
||||
// internal const val PATH_INSTANCE = "/api/v1/instance"
|
||||
internal const val PATH_LIST_INFO = "/api/v1/lists/%s"
|
||||
|
|
|
@ -1,16 +1,11 @@
|
|||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.os.SystemClock
|
||||
import jp.juggler.subwaytooter.api.TootApiCallback
|
||||
import jp.juggler.subwaytooter.api.TootApiClient
|
||||
import jp.juggler.subwaytooter.api.TootApiResult
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.util.*
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class ColumnTask_Gap(
|
||||
columnArg : Column,
|
||||
|
@ -106,15 +101,15 @@ class ColumnTask_Gap(
|
|||
return
|
||||
}
|
||||
|
||||
val list_new = when(column.type){
|
||||
|
||||
val list_new = when(column.type) {
|
||||
|
||||
// 検索カラムはIDによる重複排除が不可能
|
||||
ColumnType.SEARCH -> list_tmp
|
||||
|
||||
|
||||
// 他のカラムは重複排除してから追加
|
||||
else -> column.duplicate_map.filterDuplicate(list_tmp)
|
||||
}
|
||||
|
||||
|
||||
// 0個でもギャップを消すために以下の処理を続ける
|
||||
val changeList = ArrayList<AdapterChange>()
|
||||
|
||||
|
@ -161,17 +156,22 @@ class ColumnTask_Gap(
|
|||
}
|
||||
column.fireShowContent(reason = "gap updated", changeList = changeList)
|
||||
|
||||
if(holder != null) {
|
||||
if(restore_idx >= 0) {
|
||||
// ギャップが画面内にあるなら
|
||||
holder.setListItemTop(restore_idx + added - 1, restore_y)
|
||||
} else {
|
||||
// ギャップが画面内にない場合、何もしない
|
||||
when {
|
||||
|
||||
// ViewHolderがない
|
||||
holder == null -> {
|
||||
val scroll_save = column.scroll_save
|
||||
if(scroll_save != null) {
|
||||
scroll_save.adapterIndex += added - 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val scroll_save = column.scroll_save
|
||||
if(scroll_save != null) {
|
||||
scroll_save.adapterIndex += added - 1
|
||||
|
||||
// ギャップが画面内にあるなら
|
||||
restore_idx >= 0 ->
|
||||
holder.setListItemTop(restore_idx + added - 1, restore_y)
|
||||
|
||||
// ギャップが画面内にない場合、何もしない
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -807,28 +807,19 @@ class ColumnTask_Gap(
|
|||
}
|
||||
|
||||
// https://mastodon2.juggler.jp/api/v2/search?q=gargron&type=accounts&offset=5
|
||||
var path = String.format(
|
||||
Locale.JAPAN,
|
||||
Column.PATH_SEARCH_V2,
|
||||
column.search_query.encodePercent()
|
||||
) + "&type=$type&offset=$offset"
|
||||
var query = "q=${column.search_query.encodePercent()}&type=$type&offset=$offset"
|
||||
if(column.search_resolve) query += "&resolve=1"
|
||||
|
||||
if(column.search_resolve) path += "&resolve=1"
|
||||
|
||||
val result = client.request(path)
|
||||
val jsonObject = result?.jsonObject
|
||||
if(jsonObject != null) {
|
||||
val tmp = parser.resultsV2(jsonObject)
|
||||
if(tmp != null) {
|
||||
list_tmp = ArrayList()
|
||||
addAll(list_tmp, tmp.hashtags)
|
||||
addAll(list_tmp, tmp.accounts)
|
||||
addAll(list_tmp, tmp.statuses)
|
||||
if(list_tmp?.isNotEmpty() == true) {
|
||||
addOne(list_tmp, TootSearchGap(gap.type))
|
||||
}
|
||||
val (apiResult, searchResult) = client.requestMastodonSearch(parser, query)
|
||||
if(searchResult != null) {
|
||||
list_tmp = ArrayList()
|
||||
addAll(list_tmp, searchResult.hashtags)
|
||||
addAll(list_tmp, searchResult.accounts)
|
||||
addAll(list_tmp, searchResult.statuses)
|
||||
if(list_tmp?.isNotEmpty() == true) {
|
||||
addOne(list_tmp, TootSearchGap(gap.type))
|
||||
}
|
||||
}
|
||||
return result
|
||||
return apiResult
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
package jp.juggler.subwaytooter
|
||||
|
||||
import android.os.SystemClock
|
||||
import jp.juggler.subwaytooter.api.TootApiCallback
|
||||
import jp.juggler.subwaytooter.api.TootApiClient
|
||||
import jp.juggler.subwaytooter.api.TootApiResult
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.api.*
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.util.InstanceTicker
|
||||
import jp.juggler.util.*
|
||||
|
@ -980,7 +977,7 @@ class ColumnTask_Loading(
|
|||
)
|
||||
val jsonArray = result?.jsonArray
|
||||
if(jsonArray != null) {
|
||||
val src = TootTag.parseTootTagList(parser, jsonArray)
|
||||
val src = TootTag.parseList(parser, jsonArray)
|
||||
list_tmp = addAll(list_tmp, src)
|
||||
}
|
||||
}
|
||||
|
@ -1019,63 +1016,27 @@ class ColumnTask_Loading(
|
|||
}
|
||||
}
|
||||
|
||||
var query="q=${column.search_query.encodePercent()}"
|
||||
if(column.search_resolve) query += "&resolve=1"
|
||||
|
||||
if(instance?.versionGE(TootInstance.VERSION_2_4_0) == true) {
|
||||
// v2 api を試す
|
||||
var path = String.format(
|
||||
Locale.JAPAN,
|
||||
Column.PATH_SEARCH_V2,
|
||||
column.search_query.encodePercent()
|
||||
)
|
||||
if(column.search_resolve) path += "&resolve=1"
|
||||
|
||||
client.request(path).also { result ->
|
||||
val jsonObject = result?.jsonObject
|
||||
if(jsonObject != null) {
|
||||
val tmp = parser.resultsV2(jsonObject)
|
||||
if(tmp != null) {
|
||||
list_tmp = java.util.ArrayList()
|
||||
addAll(list_tmp, tmp.hashtags)
|
||||
if(tmp.hashtags.isNotEmpty()) {
|
||||
addOne(list_tmp, TootSearchGap(TootSearchGap.SearchType.Hashtag))
|
||||
}
|
||||
addAll(list_tmp, tmp.accounts)
|
||||
if(tmp.accounts.isNotEmpty()) {
|
||||
addOne(list_tmp, TootSearchGap(TootSearchGap.SearchType.Account))
|
||||
}
|
||||
addAll(list_tmp, tmp.statuses)
|
||||
if(tmp.statuses.isNotEmpty()) {
|
||||
addOne(list_tmp, TootSearchGap(TootSearchGap.SearchType.Status))
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
if(instance.versionGE(TootInstance.VERSION_2_4_1_rc1)) {
|
||||
// 2.4.1rc1以降はv2が確実に存在するはずなので、v1へのフォールバックを行わない
|
||||
return result
|
||||
}
|
||||
val(apiResult,searchResult)= client.requestMastodonSearch(parser,query)
|
||||
if( searchResult != null){
|
||||
list_tmp = java.util.ArrayList()
|
||||
addAll(list_tmp, searchResult.hashtags)
|
||||
if(searchResult.searchApiVersion>=2 && searchResult.hashtags.isNotEmpty()) {
|
||||
addOne(list_tmp, TootSearchGap(TootSearchGap.SearchType.Hashtag))
|
||||
}
|
||||
addAll(list_tmp, searchResult.accounts)
|
||||
if(searchResult.searchApiVersion>=2 && searchResult.accounts.isNotEmpty()) {
|
||||
addOne(list_tmp, TootSearchGap(TootSearchGap.SearchType.Account))
|
||||
}
|
||||
addAll(list_tmp, searchResult.statuses)
|
||||
if( searchResult.searchApiVersion>=2 && searchResult.statuses.isNotEmpty()) {
|
||||
addOne(list_tmp, TootSearchGap(TootSearchGap.SearchType.Status))
|
||||
}
|
||||
}
|
||||
return apiResult
|
||||
|
||||
var path = String.format(
|
||||
Locale.JAPAN,
|
||||
Column.PATH_SEARCH,
|
||||
column.search_query.encodePercent()
|
||||
)
|
||||
if(column.search_resolve) path += "&resolve=1"
|
||||
|
||||
client.request(path).also { result ->
|
||||
val jsonObject = result?.jsonObject
|
||||
if(jsonObject != null) {
|
||||
val tmp = parser.results(jsonObject)
|
||||
if(tmp != null) {
|
||||
list_tmp = java.util.ArrayList()
|
||||
addAll(list_tmp, tmp.hashtags)
|
||||
addAll(list_tmp, tmp.accounts)
|
||||
addAll(list_tmp, tmp.statuses)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1265,7 +1265,7 @@ enum class ColumnType(
|
|||
|
||||
loading = { client ->
|
||||
val result = client.request("/api/v1/trends")
|
||||
val src = parser.trendTagList(result?.jsonArray)
|
||||
val src = parser.tagList(result?.jsonArray)
|
||||
|
||||
this.list_tmp = addAll(this.list_tmp, src)
|
||||
this.list_tmp = addOne(
|
||||
|
|
|
@ -107,7 +107,7 @@ internal class ItemViewHolder(
|
|||
private lateinit var tvTrendTagName : TextView
|
||||
private lateinit var tvTrendTagDesc : TextView
|
||||
private lateinit var tvTrendTagCount : TextView
|
||||
private lateinit var cvTrendTagHistory : TrendTagHistoryView
|
||||
private lateinit var cvTagHistory : TagHistoryView
|
||||
|
||||
private lateinit var llList : View
|
||||
private lateinit var btnListTL : Button
|
||||
|
@ -443,7 +443,7 @@ internal class ItemViewHolder(
|
|||
tvMessageHolder.setTextColor(c)
|
||||
tvTrendTagName.setTextColor(c)
|
||||
tvTrendTagCount.setTextColor(c)
|
||||
cvTrendTagHistory.setColor(c)
|
||||
cvTagHistory.setColor(c)
|
||||
tvFilterPhrase.setTextColor(c)
|
||||
tvMediaDescription.setTextColor(c)
|
||||
tvCardText.setTextColor(c)
|
||||
|
@ -513,8 +513,6 @@ internal class ItemViewHolder(
|
|||
|
||||
is TootMessageHolder -> showMessageHolder(item)
|
||||
|
||||
// TootTrendTag の後に TootTagを判定すること
|
||||
is TootTrendTag -> showTrendTag(item)
|
||||
is TootTag -> showSearchTag(item)
|
||||
|
||||
is TootFilter -> showFilter(item)
|
||||
|
@ -743,16 +741,7 @@ internal class ItemViewHolder(
|
|||
}
|
||||
showStatus(item, colorBg)
|
||||
}
|
||||
|
||||
private fun showTrendTag(item : TootTrendTag) {
|
||||
llTrendTag.visibility = View.VISIBLE
|
||||
tvTrendTagName.text = "#${item.name}"
|
||||
tvTrendTagDesc.text =
|
||||
activity.getString(R.string.people_talking, item.accountDaily, item.accountWeekly)
|
||||
tvTrendTagCount.text = "${item.countDaily}(${item.countWeekly})"
|
||||
cvTrendTagHistory.setHistory(item.history)
|
||||
}
|
||||
|
||||
|
||||
private fun showMessageHolder(item : TootMessageHolder) {
|
||||
tvMessageHolder.visibility = View.VISIBLE
|
||||
tvMessageHolder.text = item.text
|
||||
|
@ -1019,8 +1008,18 @@ internal class ItemViewHolder(
|
|||
}
|
||||
|
||||
private fun showSearchTag(tag : TootTag) {
|
||||
llSearchTag.visibility = View.VISIBLE
|
||||
btnSearchTag.text = "#" + tag.name
|
||||
if( tag.history?.isNotEmpty() == true ){
|
||||
llTrendTag.visibility = View.VISIBLE
|
||||
tvTrendTagName.text = "#${tag.name}"
|
||||
tvTrendTagDesc.text =
|
||||
activity.getString(R.string.people_talking, tag.accountDaily, tag.accountWeekly)
|
||||
tvTrendTagCount.text = "${tag.countDaily}(${tag.countWeekly})"
|
||||
cvTagHistory.setHistory(tag.history)
|
||||
}else{
|
||||
llSearchTag.visibility = View.VISIBLE
|
||||
btnSearchTag.text = "#" + tag.name
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun showGap() {
|
||||
|
@ -3592,7 +3591,7 @@ internal class ItemViewHolder(
|
|||
endMargin = dip(6)
|
||||
}
|
||||
|
||||
cvTrendTagHistory = trendTagHistoryView {
|
||||
cvTagHistory = trendTagHistoryView {
|
||||
|
||||
}.lparams(dip(64), dip(32))
|
||||
|
||||
|
|
|
@ -1471,6 +1471,26 @@ class TootApiClient(
|
|||
|
||||
}
|
||||
|
||||
// query: query_string after ? ( ? itself is excluded )
|
||||
fun TootApiClient.requestMastodonSearch(parser:TootParser,query : String) : Pair<TootApiResult?, TootResults?> {
|
||||
|
||||
var searchApiVersion = 2
|
||||
var apiResult = request("/api/v2/search?$query")
|
||||
?: return Pair(null, null)
|
||||
|
||||
if( (apiResult.response?.code?:0) in 400 until 500 ) {
|
||||
searchApiVersion = 1
|
||||
apiResult = request("/api/v1/search?$query")
|
||||
?: return Pair(null, null)
|
||||
|
||||
}
|
||||
|
||||
val searchResult = parser.results(apiResult.jsonObject)
|
||||
searchResult?.searchApiVersion = searchApiVersion
|
||||
|
||||
return Pair(apiResult,searchResult)
|
||||
}
|
||||
|
||||
// result.data に TootAccountRefを格納して返す。もしくはエラーかキャンセル
|
||||
fun TootApiClient.syncAccountByUrl(
|
||||
accessInfo : SavedAccount,
|
||||
|
@ -1490,13 +1510,13 @@ fun TootApiClient.syncAccountByUrl(
|
|||
|
||||
val parser = TootParser(context, accessInfo)
|
||||
|
||||
var ar : TootAccountRef? = null
|
||||
val result = if(accessInfo.isMisskey) {
|
||||
return if(accessInfo.isMisskey) {
|
||||
|
||||
val acct = TootAccount.getAcctFromUrl(who_url)
|
||||
?: return Pair(TootApiResult(context.getString(R.string.user_id_conversion_failed)), ar)
|
||||
?: return Pair(TootApiResult(context.getString(R.string.user_id_conversion_failed)), null)
|
||||
|
||||
request(
|
||||
var ar : TootAccountRef? = null
|
||||
val result = request(
|
||||
"/api/users/show",
|
||||
accessInfo.putMisskeyApiToken(JSONObject()).apply {
|
||||
when(val delm = acct.indexOf('@')) {
|
||||
|
@ -1515,17 +1535,15 @@ fun TootApiClient.syncAccountByUrl(
|
|||
setError(context.getString(R.string.user_id_conversion_failed))
|
||||
}
|
||||
}
|
||||
|
||||
Pair(result, ar)
|
||||
} else {
|
||||
request("/api/v1/search?q=${who_url.encodePercent()}&resolve=true")
|
||||
?.apply {
|
||||
ar = parser.results(jsonObject)?.accounts?.firstOrNull()
|
||||
if(ar == null && error == null) {
|
||||
setError(context.getString(R.string.user_id_conversion_failed))
|
||||
}
|
||||
}
|
||||
val (apiResult, searchResult) = requestMastodonSearch(parser,"q=${who_url.encodePercent()}&resolve=true")
|
||||
val ar = searchResult?.accounts?.firstOrNull()
|
||||
if(apiResult != null && apiResult.error == null && ar == null) {
|
||||
apiResult.setError(context.getString(R.string.user_id_conversion_failed))
|
||||
}
|
||||
Pair(apiResult, ar)
|
||||
}
|
||||
return Pair(result, ar)
|
||||
}
|
||||
|
||||
fun TootApiClient.syncAccountByAcct(
|
||||
|
@ -1534,9 +1552,9 @@ fun TootApiClient.syncAccountByAcct(
|
|||
) : Pair<TootApiResult?, TootAccountRef?> {
|
||||
|
||||
val parser = TootParser(context, accessInfo)
|
||||
var ar : TootAccountRef? = null
|
||||
val result = if(accessInfo.isMisskey) {
|
||||
request(
|
||||
return if(accessInfo.isMisskey) {
|
||||
var ar : TootAccountRef? = null
|
||||
val result = request(
|
||||
"/api/users/show",
|
||||
accessInfo.putMisskeyApiToken()
|
||||
.apply {
|
||||
|
@ -1557,16 +1575,16 @@ fun TootApiClient.syncAccountByAcct(
|
|||
setError(context.getString(R.string.user_id_conversion_failed))
|
||||
}
|
||||
}
|
||||
Pair(result, ar)
|
||||
} else {
|
||||
request("/api/v1/search?q=${acct.encodePercent()}&resolve=true")
|
||||
?.apply {
|
||||
ar = parser.results(jsonObject)?.accounts?.firstOrNull()
|
||||
if(ar == null && error == null) {
|
||||
setError(context.getString(R.string.user_id_conversion_failed))
|
||||
}
|
||||
}
|
||||
val (apiResult, searchResult) = requestMastodonSearch(parser,"q=${acct.encodePercent()}&resolve=true")
|
||||
val ar = searchResult?.accounts?.firstOrNull()
|
||||
if(apiResult != null && apiResult.error == null && ar == null) {
|
||||
apiResult.setError(context.getString(R.string.user_id_conversion_failed))
|
||||
}
|
||||
Pair(apiResult, ar)
|
||||
}
|
||||
return Pair(result, ar)
|
||||
|
||||
}
|
||||
|
||||
fun TootApiClient.syncStatus(
|
||||
|
@ -1611,9 +1629,9 @@ fun TootApiClient.syncStatus(
|
|||
|
||||
// 使いたいタンス上の投稿IDを取得する
|
||||
val parser = TootParser(context, accessInfo)
|
||||
var targetStatus : TootStatus? = null
|
||||
val result = if(accessInfo.isMisskey) {
|
||||
request(
|
||||
return if(accessInfo.isMisskey) {
|
||||
var targetStatus : TootStatus? = null
|
||||
val result = request(
|
||||
"/api/ap/show",
|
||||
accessInfo.putMisskeyApiToken()
|
||||
.put("uri", url)
|
||||
|
@ -1625,16 +1643,16 @@ fun TootApiClient.syncStatus(
|
|||
setError(context.getString(R.string.cant_sync_toot))
|
||||
}
|
||||
}
|
||||
Pair(result, targetStatus)
|
||||
} else {
|
||||
request("/api/v1/search?q=${url.encodePercent()}&resolve=true")
|
||||
?.apply {
|
||||
targetStatus = parser.results(jsonObject)?.statuses?.firstOrNull()
|
||||
if(targetStatus == null && error == null) {
|
||||
setError(context.getString(R.string.cant_sync_toot))
|
||||
}
|
||||
}
|
||||
val(apiResult,searchResult) = requestMastodonSearch(parser,"${url.encodePercent()}&resolve=true")
|
||||
val targetStatus = searchResult?.statuses?.firstOrNull()
|
||||
if( apiResult!= null && apiResult.error==null && targetStatus==null){
|
||||
apiResult.setError(context.getString(R.string.cant_sync_toot))
|
||||
}
|
||||
Pair(apiResult,targetStatus)
|
||||
}
|
||||
return Pair(result, targetStatus)
|
||||
|
||||
}
|
||||
|
||||
fun TootApiClient.syncStatus(
|
||||
|
|
|
@ -42,11 +42,9 @@ class TootParser(
|
|||
fun notification(src : JSONObject?) = parseItem(::TootNotification, this, src)
|
||||
fun notificationList(src : JSONArray?) = parseList(::TootNotification, this, src)
|
||||
|
||||
fun tagList(array : JSONArray?) = parseList(::TootTag, array)
|
||||
fun results(src : JSONObject?) = parseItem(::TootResults, this, src)
|
||||
fun instance(src : JSONObject?) = parseItem(::TootInstance, this, src)
|
||||
fun trendTagList(array : JSONArray?) = parseList(::TootTrendTag, array)
|
||||
|
||||
fun resultsV2(src : JSONObject) = parseItem(::TootResultsV2, this, src)
|
||||
|
||||
fun getMisskeyUserRelation(whoId : EntityId) = misskeyUserRelationMap[whoId]
|
||||
|
||||
|
|
|
@ -6,21 +6,19 @@ import java.util.ArrayList
|
|||
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
|
||||
class TootResults(parser : TootParser, src : JSONObject) {
|
||||
|
||||
// An array of matched Accounts
|
||||
val accounts : ArrayList<TootAccountRef>
|
||||
|
||||
// An array of matched Statuses
|
||||
val statuses : ArrayList<TootStatus>
|
||||
|
||||
// An array of matched hashtags
|
||||
class TootResults private constructor(
|
||||
// An array of matched Accounts
|
||||
val accounts : ArrayList<TootAccountRef>,
|
||||
// An array of matched Statuses
|
||||
val statuses : ArrayList<TootStatus>,
|
||||
// An array of matched hashtags
|
||||
val hashtags : ArrayList<TootTag>
|
||||
) {
|
||||
var searchApiVersion = 0 // 0 means not from search API. such as trend tags.
|
||||
|
||||
init {
|
||||
accounts = parser.accountList(src.optJSONArray("accounts"))
|
||||
statuses = parser.statusList(src.optJSONArray("statuses"))
|
||||
hashtags = TootTag.parseTootTagList(parser,src.optJSONArray("hashtags"))
|
||||
}
|
||||
|
||||
constructor(parser : TootParser, src : JSONObject):this(
|
||||
accounts = parser.accountList(src.optJSONArray("accounts")),
|
||||
statuses = parser.statusList(src.optJSONArray("statuses")),
|
||||
hashtags = TootTag.parseList( parser, src.optJSONArray("hashtags"))
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
package jp.juggler.subwaytooter.api.entity
|
||||
|
||||
import org.json.JSONObject
|
||||
|
||||
import java.util.ArrayList
|
||||
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
|
||||
class TootResultsV2(
|
||||
val accounts : ArrayList<TootAccountRef>, // An array of matched Accounts
|
||||
val statuses : ArrayList<TootStatus>, // An array of matched Statuses
|
||||
val hashtags : ArrayList<TootTrendTag> // An array of matched hashtags
|
||||
) {
|
||||
|
||||
constructor(parser : TootParser, src : JSONObject) : this(
|
||||
accounts = parser.accountList(src.optJSONArray("accounts")),
|
||||
statuses = parser.statusList( src.optJSONArray("statuses")),
|
||||
hashtags = parser.trendTagList(src.optJSONArray("hashtags"))
|
||||
)
|
||||
}
|
|
@ -2,51 +2,108 @@ package jp.juggler.subwaytooter.api.entity
|
|||
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.util.MisskeyMarkdownDecoder
|
||||
import jp.juggler.util.groupEx
|
||||
import jp.juggler.util.notEmptyOrThrow
|
||||
import jp.juggler.util.parseString
|
||||
import jp.juggler.util.*
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.util.regex.Pattern
|
||||
|
||||
open class TootTag(
|
||||
open class TootTag constructor(
|
||||
|
||||
// The hashtag, not including the preceding #
|
||||
val name : String,
|
||||
|
||||
// The URL of the hashtag. may null if generated from TootContext
|
||||
val url : String? = null
|
||||
val url : String? = null,
|
||||
|
||||
// Mastodon /api/v2/search provides history.
|
||||
val history : ArrayList<History>? = null
|
||||
|
||||
) : TimelineItem() {
|
||||
|
||||
constructor(src : JSONObject) : this(
|
||||
val countDaily : Int
|
||||
val countWeekly : Int
|
||||
val accountDaily : Int
|
||||
val accountWeekly : Int
|
||||
|
||||
init {
|
||||
countDaily = history?.first()?.uses ?: 0
|
||||
countWeekly = history?.sumBy { it.uses } ?: 0
|
||||
|
||||
accountDaily = history?.first()?.accounts ?: 0
|
||||
accountWeekly = history?.map { it.accounts }?.max() ?: accountDaily
|
||||
}
|
||||
|
||||
class History(src : JSONObject) {
|
||||
val day : Long
|
||||
val uses : Int
|
||||
val accounts : Int
|
||||
|
||||
init {
|
||||
day = src.parseLong("day")
|
||||
?: throw RuntimeException("TootTrendTag.History: missing day")
|
||||
uses = src.parseInt("uses")
|
||||
?: throw RuntimeException("TootTrendTag.History: missing uses")
|
||||
accounts = src.parseInt("accounts")
|
||||
?: throw RuntimeException("TootTrendTag.History: missing accounts")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// for TREND_TAG column
|
||||
constructor( src : JSONObject) : this(
|
||||
name = src.notEmptyOrThrow("name"),
|
||||
url = src.parseString("url")
|
||||
url = src.parseString("url"),
|
||||
history = parseHistories( src.optJSONArray("history"))
|
||||
)
|
||||
|
||||
companion object {
|
||||
|
||||
// 検索結果のhashtagリストから生成する
|
||||
fun parseTootTagList(parser : TootParser, array : JSONArray?) : ArrayList<TootTag> {
|
||||
val log = LogCategory("TootTag")
|
||||
|
||||
private fun parseHistories(src : JSONArray?) : ArrayList<History>? {
|
||||
src ?: return null
|
||||
|
||||
val dst = ArrayList<History>()
|
||||
for(i in 0 until src.length()) {
|
||||
try {
|
||||
dst.add(History(src.optJSONObject(i)))
|
||||
} catch(ex : Throwable) {
|
||||
log.e(ex, "parseHistories failed.")
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
fun parseList(parser : TootParser, array : JSONArray?) : ArrayList<TootTag> {
|
||||
val result = ArrayList<TootTag>()
|
||||
if(parser.serviceType == ServiceType.MISSKEY) {
|
||||
if(array != null) {
|
||||
if(array != null) {
|
||||
if(parser.serviceType == ServiceType.MISSKEY) {
|
||||
for(i in 0 until array.length()) {
|
||||
val sv = array.parseString(i)
|
||||
if(sv?.isNotEmpty() == true) {
|
||||
result.add(TootTag(name = sv))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if(array != null) {
|
||||
} else {
|
||||
for(i in 0 until array.length()) {
|
||||
val sv = array.parseString(i)
|
||||
if(sv?.isNotEmpty() == true) {
|
||||
result.add(TootTag(name = sv))
|
||||
val tag = try {
|
||||
when(val item = array.opt(i)) {
|
||||
is String -> if(item.isNotEmpty()) {
|
||||
TootTag(name = item)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
is JSONObject -> TootTag(item)
|
||||
else -> null
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.w(ex, "parseList: parse error")
|
||||
null
|
||||
}
|
||||
if(tag != null) result.add(tag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
|
@ -94,7 +151,7 @@ open class TootTag(
|
|||
val m = reTagMastodon.matcher(src)
|
||||
while(m.find()) {
|
||||
if(result == null) result = ArrayList()
|
||||
result.add(m.groupEx(1)!!)
|
||||
result.add(m.groupEx(1) !!)
|
||||
}
|
||||
result
|
||||
}
|
||||
|
@ -107,4 +164,4 @@ open class TootTag(
|
|||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
package jp.juggler.subwaytooter.api.entity
|
||||
|
||||
import jp.juggler.util.*
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
class TootTrendTag(
|
||||
name : String,
|
||||
url : String?,
|
||||
val history : ArrayList<History>
|
||||
) : TootTag(name, url) {
|
||||
|
||||
val countDaily :Int
|
||||
val countWeekly :Int
|
||||
val accountDaily: Int
|
||||
val accountWeekly: Int
|
||||
|
||||
init{
|
||||
countDaily = history.first().uses
|
||||
countWeekly = history.sumBy { it.uses }
|
||||
|
||||
accountDaily = history.first().accounts
|
||||
accountWeekly = history.map { it.accounts }.max() ?: accountDaily
|
||||
}
|
||||
|
||||
class History(src : JSONObject) {
|
||||
val day : Long
|
||||
val uses : Int
|
||||
val accounts : Int
|
||||
|
||||
init {
|
||||
day = src.parseLong("day")
|
||||
?: throw RuntimeException("TootTrendTag.History: missing day")
|
||||
uses = src.parseInt("uses")
|
||||
?: throw RuntimeException("TootTrendTag.History: missing uses")
|
||||
accounts = src.parseInt("accounts")
|
||||
?: throw RuntimeException("TootTrendTag.History: missing accounts")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
constructor(src : JSONObject) : this(
|
||||
name = src.notEmptyOrThrow("name"),
|
||||
url = src.parseString("url"),
|
||||
history = parseHistory(src.optJSONArray("history"))
|
||||
)
|
||||
|
||||
companion object {
|
||||
val log = LogCategory("TootTrendTag")
|
||||
|
||||
private fun parseHistory(src : JSONArray?) : ArrayList<History> {
|
||||
src ?: throw RuntimeException("TootTrendTag: missing history")
|
||||
|
||||
val dst = ArrayList<History>()
|
||||
for(i in 0 until src.length()) {
|
||||
try {
|
||||
dst.add(History(src.optJSONObject(i)))
|
||||
} catch(ex : Throwable) {
|
||||
log.e(ex, "history parse failed.")
|
||||
}
|
||||
}
|
||||
|
||||
if(dst.isEmpty()) {
|
||||
throw RuntimeException("TootTrendTag: empty history")
|
||||
}
|
||||
|
||||
return dst
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import android.view.ViewManager
|
|||
import jp.juggler.subwaytooter.view.BlurhashView
|
||||
import jp.juggler.subwaytooter.view.MyNetworkImageView
|
||||
import jp.juggler.subwaytooter.view.MyTextView
|
||||
import jp.juggler.subwaytooter.view.TrendTagHistoryView
|
||||
import jp.juggler.subwaytooter.view.TagHistoryView
|
||||
import org.jetbrains.anko.custom.ankoView
|
||||
|
||||
// Anko Layout中にカスタムビューを指定する為に拡張関数を定義する
|
||||
|
@ -18,8 +18,8 @@ inline fun ViewManager.myTextView(init: MyTextView.() -> Unit) : MyTextView {
|
|||
}
|
||||
|
||||
|
||||
inline fun ViewManager.trendTagHistoryView(init: TrendTagHistoryView.() -> Unit): TrendTagHistoryView {
|
||||
return ankoView({ TrendTagHistoryView(it) }, theme = 0, init = init)
|
||||
inline fun ViewManager.trendTagHistoryView(init: TagHistoryView.() -> Unit): TagHistoryView {
|
||||
return ankoView({ TagHistoryView(it) }, theme = 0, init = init)
|
||||
}
|
||||
|
||||
inline fun ViewManager.blurhashView(init: BlurhashView.() -> Unit): BlurhashView {
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
package jp.juggler.subwaytooter.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Path
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import jp.juggler.subwaytooter.api.entity.TootTrendTag
|
||||
import jp.juggler.util.*
|
||||
import jp.juggler.subwaytooter.api.entity.TootTag
|
||||
import jp.juggler.util.clipRange
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
class TrendTagHistoryView : View {
|
||||
class TagHistoryView : View {
|
||||
|
||||
private val paint = Paint()
|
||||
private var values : List<Float>? = null
|
||||
|
@ -47,7 +49,7 @@ class TrendTagHistoryView : View {
|
|||
invalidate()
|
||||
}
|
||||
|
||||
fun setHistory(history : ArrayList<TootTrendTag.History>?) {
|
||||
fun setHistory(history : ArrayList<TootTag.History>?) {
|
||||
if(history?.isEmpty() != false) {
|
||||
delta = 0f
|
||||
values = null
|
Loading…
Reference in New Issue