差分取得の並び順の修正、EntityIdの取り扱いのリファクタ

This commit is contained in:
tateisu 2018-08-26 11:09:34 +09:00
parent a320a91093
commit 4e6ae4a935
10 changed files with 127 additions and 112 deletions

View File

@ -2597,6 +2597,7 @@ class Column(
idMin is EntityIdString && ! isMisskey -> {
log.e("EntityId should be Long for non-misskey column! columnType=$column_type")
}
else -> {
val i = idOld?.compareTo(idMin)
if(i == null || i > 0) {
@ -2815,7 +2816,6 @@ class Column(
@Suppress("NON_EXHAUSTIVE_WHEN")
when(pagingType) {
PagingType.Default -> {
if(isMisskey && ! bBottom) src.reverse()
saveRange(bBottom, ! bBottom, firstResult, src)
}
@ -2858,6 +2858,7 @@ class Column(
jsonObject = result?.jsonObject
if(jsonObject != null) {
// pagingType is always default.
result !!.data = misskeyArrayFinder(jsonObject)
}
@ -2869,12 +2870,19 @@ class Column(
}
src = misskeyCustomParser(parser, array)
src.reverse()
addAll(list_tmp, src)
// pagingType is always default.
saveRange(false, true, result, src)
}
// pagingType is always default.
if(isMisskey && ! bBottom){
list_tmp?.sortBy{ it.getOrderId() }
list_tmp?.reverse()
}
if(! isCancelled
&& list_tmp?.isNotEmpty() == true
&& (bHeadGap || Pref.bpForceGap(context))
@ -3120,7 +3128,6 @@ class Column(
var jsonArray = result?.jsonArray
if(jsonArray != null) {
var src = parser.notificationList(jsonArray)
if(isMisskey && ! bBottom) src.reverse()
list_tmp = addWithFilterNotification(null, src)
saveRange(bBottom, ! bBottom, result, src)
@ -3169,7 +3176,6 @@ class Column(
}
src = parser.notificationList(jsonArray)
src.reverse()
saveRange(false, true, result, src)
if(! src.isEmpty()) {
@ -3178,6 +3184,11 @@ class Column(
}
}
if(isMisskey && ! bBottom){
list_tmp?.sortBy{it.getOrderId()}
list_tmp?.reverse()
}
if(! isCancelled
&& list_tmp?.isNotEmpty() == true
&& (bHeadGap || Pref.bpForceGap(context))
@ -3300,8 +3311,7 @@ class Column(
fun getStatusList(
client : TootApiClient,
path_base : String,
misskeyParams : JSONObject? = null
,
misskeyParams : JSONObject? = null,
misskeyCustomParser : (parser : TootParser, jsonArray : JSONArray) -> ArrayList<TootStatus> =
{ parser, jsonArray -> parser.statusList(jsonArray) }
) : TootApiResult? {
@ -3326,7 +3336,7 @@ class Column(
val jsonArray = result?.jsonArray
if(jsonArray != null) {
var src = misskeyCustomParser(parser, jsonArray)
if(isMisskey && ! bBottom) src.reverse()
saveRange(bBottom, ! bBottom, result, src)
list_tmp = addWithFilterStatus(null, src)
@ -3349,7 +3359,7 @@ class Column(
}
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
log.d("refresh-status-offset: read enough. make gap.")
log.d("refresh-status-top: read enough. make gap.")
bHeadGap = true
break
}
@ -3372,12 +3382,16 @@ class Column(
}
src = misskeyCustomParser(parser, jsonArray2)
src.reverse()
saveRange(false, true, result, src)
addWithFilterStatus(list_tmp, src)
}
if(isMisskey && ! bBottom ){
list_tmp?.sortBy { it.getOrderId() }
list_tmp?.reverse()
}
if(! isCancelled
&& list_tmp?.isNotEmpty() == true
@ -3438,10 +3452,9 @@ class Column(
}
src = misskeyCustomParser(parser, jsonArray2)
addWithFilterStatus(list_tmp, src)
}
if(Pref.bpForceGap(context) && ! isCancelled && ! bGapAdded && list_tmp?.isNotEmpty() == true) {
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
}
@ -3495,7 +3508,6 @@ class Column(
}
src = misskeyCustomParser(parser, jsonArray2)
addWithFilterStatus(list_tmp, src)
if(! saveRangeEnd(result, src)) {
@ -4001,7 +4013,6 @@ class Column(
result = r2
val src = misskeyCustomParser(parser, jsonArray)
src.reverse()
if(src.isEmpty()) {
log.d("gap-account: empty.")
break
@ -4010,6 +4021,10 @@ class Column(
addAll(list_tmp, src)
since_id = parseRange(result, src).second
}
if(isMisskey ){
list_tmp?.sortBy{it.getOrderId()}
list_tmp?.reverse()
}
if(bHeadGap) {
addOneFirst(list_tmp, TootGap.mayNull(max_id, since_id))
}
@ -4099,13 +4114,20 @@ class Column(
log.d("gap-report: empty.")
break
}
src.reverse()
addAll(list_tmp, src)
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである
since_id = parseRange(result, src).second
}
// レポート一覧ってそもそもMisskey対応してないので、ここをどうするかは不明
// 多分 sinceIDによるページングではないと思う
if(isMisskey ){
list_tmp?.sortBy{it.getOrderId()}
list_tmp?.reverse()
}
if(bHeadGap) {
addOneFirst(list_tmp, TootGap.mayNull(max_id, since_id))
}
@ -4196,7 +4218,6 @@ class Column(
log.d("gap-notification: empty.")
break
}
src.reverse()
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである
since_id = parseRange(result, src).second
@ -4206,6 +4227,11 @@ class Column(
PollingWorker.injectData(context, access_info, src)
}
if(isMisskey ){
list_tmp?.sortBy{it.getOrderId()}
list_tmp?.reverse()
}
if(bHeadGap) {
addOneFirst(list_tmp, TootGap.mayNull(max_id, since_id))
}
@ -4318,13 +4344,18 @@ class Column(
log.d("gap-statuses: empty.")
break
}
src.reverse()
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである
since_id = parseRange(result, src).second
addWithFilterStatus(list_tmp, src)
}
if(isMisskey ){
list_tmp?.sortBy { it.getOrderId() }
list_tmp?.reverse()
}
if(bHeadGap) {
addOneFirst(list_tmp, TootGap.mayNull(max_id, since_id))
@ -4915,7 +4946,7 @@ class Column(
if(isFiltered(item)) return
}
stream_data_queue.add(item)
val handler = App1.getAppState(context).handler
handler.post(mergeStreamingMessage)
}
@ -4990,15 +5021,14 @@ class Column(
return app_state.stream_reader.getStreamingStatus(access_info, stream_path, streamCallback)
}
private val mergeStreamingMessage :Runnable = object : Runnable {
private val mergeStreamingMessage : Runnable = object : Runnable {
override fun run() {
// 前回マージしてから暫くは待機する
val handler =App1.getAppState(context).handler
val handler = App1.getAppState(context).handler
val now = SystemClock.elapsedRealtime()
val remain = last_show_stream_data.get() + 333L - now
if( remain > 0) {
if(remain > 0) {
handler.removeCallbacks(this)
handler.postDelayed(this, remain)
return
@ -5006,7 +5036,7 @@ class Column(
last_show_stream_data.set(now)
val tmpList = ArrayList<TimelineItem>()
while(stream_data_queue.isNotEmpty() ){
while(stream_data_queue.isNotEmpty()) {
tmpList.add(stream_data_queue.poll())
}
if(tmpList.isEmpty()) return
@ -5016,9 +5046,9 @@ class Column(
val list_new = duplicate_map.filterDuplicate(tmpList)
if(list_new.isEmpty()) return
for( item in list_new){
if( enable_speech && item is TootStatus ){
for(item in list_new) {
if(enable_speech && item is TootStatus) {
App1.getAppState(context).addSpeech(item.reblog ?: item)
}
}
@ -5055,13 +5085,13 @@ class Column(
val tmpRecent = idRecent
val tmpNewMax = new_id_max
if(tmpNewMax is EntityIdString && !isMisskey ){
if(tmpNewMax is EntityIdString && ! isMisskey) {
log.e("EntityId should be Long for non-misskey column! columnType=$column_type")
}else if( tmpRecent is EntityIdString && tmpNewMax is EntityIdLong){
} else if(tmpRecent is EntityIdString && tmpNewMax is EntityIdLong) {
log.e("EntityId type mismatch! recent=String,newMax=Long,columnType=$column_type")
}else if( tmpRecent is EntityIdLong && tmpNewMax is EntityIdString){
} else if(tmpRecent is EntityIdLong && tmpNewMax is EntityIdString) {
log.e("EntityId type mismatch! recent=Long,newMax=String,columnType=$column_type")
}else if(tmpNewMax != null && (tmpRecent?.compareTo(tmpNewMax) ?: - 1) == - 1 ) {
} else if(tmpNewMax != null && (tmpRecent?.compareTo(tmpNewMax) ?: - 1) == - 1) {
idRecent = tmpNewMax
// XXX: コレはリフレッシュ時に取得漏れを引き起こすのでは…?
// しかしコレなしだとリフレッシュ時に大量に読むことになる…

View File

@ -1,65 +1,49 @@
package jp.juggler.subwaytooter.api
import com.google.android.exoplayer2.Timeline
import jp.juggler.subwaytooter.api.entity.*
import java.util.ArrayList
import java.util.HashSet
class DuplicateMap {
private val set_status_id = HashSet<EntityId>()
private val set_notification_id = HashSet<EntityId>()
private val set_report_id = HashSet<EntityId>()
private val set_account_id = HashSet<EntityId>()
private val set_status_uri = HashSet<String>()
private val set_id = HashSet<EntityId>()
private val set_uri = HashSet<String>()
fun clear() {
set_status_id.clear()
set_notification_id.clear()
set_report_id.clear()
set_account_id.clear()
set_status_uri.clear()
set_id.clear()
set_uri.clear()
}
fun isDuplicate(o : TimelineItem) : Boolean {
when(o) {
is TootStatus ->{
val uri = o.uri
val url = o.url
when {
uri?.isNotEmpty() == true -> {
if(set_status_uri.contains(uri)) return true
set_status_uri.add(uri)
}
url?.isNotEmpty() == true -> {
// URIとURLで同じマップを使いまわすが、害はないと思う…
if(set_status_uri.contains(url)) return true
set_status_uri.add(url)
}
else -> {
if(set_status_id.contains(o.id)) return true
set_status_id.add(o.id)
}
if(o is TootStatus) {
val uri = o.uri
val url = o.url
when {
uri?.isNotEmpty() == true -> {
if(set_uri.contains(uri)) return true
set_uri.add(uri)
}
url?.isNotEmpty() == true -> {
// URIとURLで同じマップを使いまわすが、害はないと思う…
if(set_uri.contains(url)) return true
set_uri.add(url)
}
}
}
when(o) {
is TootReport,
is TootStatus,
is TootAccount,
is TootNotification -> {
if(set_notification_id.contains(o.id)) return true
set_notification_id.add(o.id)
}
is TootReport -> {
if(set_report_id.contains(o.id)) return true
set_report_id.add(o.id)
}
is TootAccountRef -> {
val id = o.get().id
if(set_account_id.contains(id)) return true
set_account_id.add(id)
val id = o.getOrderId()
if(id.notDefault){
if(set_id.contains(o.getOrderId())) return true
set_id.add(o.getOrderId())
}
}
}
@ -68,7 +52,7 @@ class DuplicateMap {
fun filterDuplicate(src : Collection<TimelineItem>?) : ArrayList<TimelineItem> {
val list_new = ArrayList<TimelineItem>()
if( src != null ) {
if(src != null) {
for(o in src) {
if(isDuplicate(o)) continue
list_new.add(o)

View File

@ -10,17 +10,25 @@ abstract class EntityId : Comparable<EntityId> {
abstract fun putMisskeyUntil(dst : JSONObject) : JSONObject
abstract fun putMisskeySince(dst : JSONObject) : JSONObject
companion object {
fun from(x : Long) = EntityIdLong(x)
val defaultLong = EntityIdLong(TootStatus.INVALID_ID)
val defaultString = EntityIdString("")
fun mayDefault(x : Long?) = when(x) {
null -> defaultLong
else -> EntityIdLong(x)
}
fun mayDefault(x : String?) = when(x) {
null -> defaultString
else -> EntityIdString(x)
}
fun mayNull(x : Long?) = when(x) {
null -> null
else -> EntityIdLong(x)
}
fun from(x : String) = EntityIdString(x)
fun mayNull(x : String?) = when(x) {
null -> null
@ -29,8 +37,8 @@ abstract class EntityId : Comparable<EntityId> {
fun String.decode():EntityId?{
if(this.isEmpty()) return null
if(this[0]=='L') return from(this.substring(1).toLong())
if(this[0]=='S') return from(this.substring(1))
if(this[0]=='L') return EntityIdLong(this.substring(1).toLong())
if(this[0]=='S') return EntityIdString(this.substring(1))
return null
}
@ -62,6 +70,8 @@ abstract class EntityId : Comparable<EntityId> {
fun putTo(data:JSONObject, key : String):JSONObject = data.put( key,encode())
val notDefault :Boolean
get()= !(this == defaultLong || this== defaultString)
}
class EntityIdLong(val x : Long) : EntityId() {

View File

@ -10,14 +10,14 @@ class NicoProfileEmoji(
val url : String,
private val shortcode : String,
@Suppress("unused") private val account_url : String?,
@Suppress("unused") private val account_id : EntityId?
@Suppress("unused") private val account_id : EntityId
) : Mappable<String> {
constructor(src : JSONObject) : this(
url = src.notEmptyOrThrow("url"),
shortcode = src.notEmptyOrThrow("shortcode"),
account_url = src.parseString("account_url"),
account_id = EntityId.mayNull( src.parseLong("account_id") )
account_id = EntityId.mayDefault( src.parseLong("account_id") )
)
override val mapKey : String

View File

@ -7,7 +7,6 @@ open class TimelineItem{
companion object {
val listViewItemIdSeed = AtomicLong(3) // ヘッダ用にいくつか空けておく
val defaultOrderId = EntityIdLong(TootStatus.INVALID_ID)
}
// AdapterView用のIDを採番する
@ -15,5 +14,5 @@ open class TimelineItem{
// 大小比較のためのIDを取得する
// 比較が不要な場合は0Lを返す
open fun getOrderId() :EntityId = defaultOrderId
open fun getOrderId() :EntityId = EntityId.defaultLong
}

View File

@ -125,7 +125,7 @@ open class TootAccount(parser : TootParser, src : JSONObject) {
// this.user_hides_network = src.optBoolean("user_hides_network")
this.id = EntityId.mayNull(src.parseString("id")) ?: error("missing id")
this.id = EntityId.mayDefault(src.parseString("id"))
this.host = instance
this.acct = when {
@ -193,7 +193,7 @@ open class TootAccount(parser : TootParser, src : JSONObject) {
val hostAccess = parser.linkHelper.host
this.id = EntityId.from(src.parseLong("id") ?: INVALID_ID)
this.id = EntityId.mayDefault(src.parseLong("id"))
this.acct = src.notEmptyOrThrow("acct")
this.host = findHostFromUrl(acct, hostAccess, url)
@ -215,7 +215,7 @@ open class TootAccount(parser : TootParser, src : JSONObject) {
ServiceType.TOOTSEARCH -> {
// tootsearch のアカウントのIDはどのタンス上のものか分からないので役に立たない
this.id = EntityId.from(INVALID_ID) // src.parseLong( "id", INVALID_ID)
this.id = EntityId.defaultLong
sv = src.notEmptyOrThrow("acct")
this.host = findHostFromUrl(sv, null, url)
@ -236,7 +236,7 @@ open class TootAccount(parser : TootParser, src : JSONObject) {
}
ServiceType.MSP -> {
this.id = EntityId.from(src.parseLong("id") ?: INVALID_ID)
this.id = EntityId.mayDefault(src.parseLong("id") )
// MSPはLTLの情報しか持ってないのでacctは常にホスト名部分を持たない
this.host = findHostFromUrl(null, null, url)
@ -304,8 +304,6 @@ open class TootAccount(parser : TootParser, src : JSONObject) {
companion object {
private val log = LogCategory("TootAccount")
const val INVALID_ID = - 1L
@Suppress("HasPlatformType")
private val reWhitespace = Pattern.compile("[\\s\\t\\x0d\\x0a]+")

View File

@ -69,7 +69,7 @@ class TootAttachment(serviceType:ServiceType,src : JSONObject) : TootAttachmentL
when(serviceType) {
ServiceType.MISSKEY -> {
id = EntityId.from( src.parseString("id") ?: error("missing id"))
id = EntityId.mayDefault( src.parseString("id"))
val mimeType = src.parseString("type") ?: "?"
this.type = when{
@ -88,7 +88,7 @@ class TootAttachment(serviceType:ServiceType,src : JSONObject) : TootAttachmentL
isSensitive = src.optBoolean("isSensitive",false)
}
else->{
id= EntityIdLong(src.parseLong("id") ?: - 1L)
id= EntityId.mayDefault(src.parseLong("id") )
type = src.parseString("type")
url = src.parseString("url")
remote_url = src.parseString("remote_url")

View File

@ -69,7 +69,7 @@ class TootFilter( src: JSONObject) :TimelineItem() {
val whole_word : Boolean
init{
id = EntityId.mayNull(src.parseLong("id") ) ?: error("missing id")
id = EntityId.mayDefault(src.parseLong("id") )
phrase = src.parseString("phrase")?: error("missing phrase")
context = parseFilterContext(src.optJSONArray("context"))
expires_at = src.parseString("expires_at") // may null

View File

@ -45,24 +45,14 @@ class TootNotification(parser : TootParser, src : JSONObject) : TimelineItem() {
val account : TootAccount?
get() = accountRef?.get()
override fun getOrderId() = id
private val _orderId :EntityId
override fun getOrderId() = _orderId
init {
json = src
if( parser.serviceType == ServiceType.MISSKEY){
id = when {
// Misskeyはストリーミングからくる通知にIDが振られていない
parser.serviceType == ServiceType.MISSKEY ->
EntityId.mayNull(src.parseString("id")) ?: EntityIdString("")
// Mastodon
else -> EntityId.mayNull(src.parseLong("id")) ?: error("missing id")
}
id = EntityId.mayDefault(src.parseString("id"))
type = src.notEmptyOrThrow("type")
@ -73,11 +63,14 @@ class TootNotification(parser : TootParser, src : JSONObject) : TimelineItem() {
status = parser.status(src.optJSONObject("note"))
reaction = src.parseString("reaction")
// Misskeyの通知APIはページネーションをIDでしか行えない
// これは改善される予定 https://github.com/syuilo/misskey/issues/2275
_orderId = id
}else{
id = when {
parser.serviceType == ServiceType.MISSKEY -> EntityId.mayNull(src.parseString("id"))
else -> EntityId.mayNull(src.parseLong("id"))
} ?: error("missing id")
id = EntityId.mayDefault(src.parseLong("id"))
type = src.notEmptyOrThrow("type")
created_at = src.parseString("created_at")
@ -85,6 +78,7 @@ class TootNotification(parser : TootParser, src : JSONObject) : TimelineItem() {
accountRef = TootAccountRef.mayNull(parser, parser.account(src.optJSONObject("account")))
status = parser.status(src.optJSONObject("status"))
_orderId = id
}
}

View File

@ -5,13 +5,13 @@ import org.json.JSONObject
import jp.juggler.subwaytooter.util.parseString
class TootPushSubscription(src : JSONObject){
val id: EntityId?
val id: EntityId
val endpoint : String?
private val alerts= HashMap<String,Boolean>()
val server_key : String?
init{
id = EntityId.mayNull(src.parseLong("id"))
id = EntityId.mayDefault(src.parseLong("id"))
endpoint = src.parseString("endpoint")
server_key = src.parseString("server_key")