検索API v1が削除される件の対策 https://github.com/tootsuite/mastodon/pull/11823

This commit is contained in:
tateisu 2019-09-14 00:49:19 +09:00
parent 07ab2e3f52
commit cf3c56ce05
13 changed files with 222 additions and 290 deletions

View File

@ -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"

View File

@ -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
}
}

View File

@ -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)
}
}
}
}
}

View File

@ -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(

View File

@ -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))

View File

@ -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(

View File

@ -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]

View File

@ -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"))
)
}

View File

@ -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"))
)
}

View File

@ -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(
}
}
}
}

View File

@ -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
}
}
}

View File

@ -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 {

View File

@ -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