2018-01-04 19:52:25 +01:00
|
|
|
|
package jp.juggler.subwaytooter
|
|
|
|
|
|
|
|
|
|
import android.annotation.SuppressLint
|
|
|
|
|
import android.content.Context
|
2018-11-19 23:46:14 +01:00
|
|
|
|
import android.content.SharedPreferences
|
2018-03-25 15:27:58 +02:00
|
|
|
|
import android.net.Uri
|
2018-01-04 19:52:25 +01:00
|
|
|
|
import android.os.AsyncTask
|
2018-09-07 13:34:54 +02:00
|
|
|
|
import android.os.Environment
|
2018-01-04 19:52:25 +01:00
|
|
|
|
import android.os.SystemClock
|
2018-11-18 15:29:35 +01:00
|
|
|
|
import android.support.v4.view.ViewCompat
|
2018-11-18 03:38:52 +01:00
|
|
|
|
import android.support.v7.app.AppCompatActivity
|
2018-05-31 00:09:40 +02:00
|
|
|
|
import android.view.Gravity
|
2018-11-18 15:29:35 +01:00
|
|
|
|
import android.view.View
|
2018-01-04 19:52:25 +01:00
|
|
|
|
import jp.juggler.subwaytooter.api.*
|
|
|
|
|
import jp.juggler.subwaytooter.api.entity.*
|
2018-03-15 17:23:43 +01:00
|
|
|
|
import jp.juggler.subwaytooter.table.*
|
2018-12-01 00:02:18 +01:00
|
|
|
|
import jp.juggler.subwaytooter.util.BucketList
|
2018-12-08 16:29:07 +01:00
|
|
|
|
import jp.juggler.subwaytooter.util.InstanceTicker
|
2018-12-01 00:02:18 +01:00
|
|
|
|
import jp.juggler.subwaytooter.util.ScrollPosition
|
|
|
|
|
import jp.juggler.subwaytooter.util.WordTrieTree
|
|
|
|
|
import jp.juggler.util.*
|
2018-12-07 01:50:11 +01:00
|
|
|
|
import okhttp3.Handshake
|
2018-08-16 21:58:30 +02:00
|
|
|
|
import org.json.JSONArray
|
|
|
|
|
import org.json.JSONException
|
|
|
|
|
import org.json.JSONObject
|
2018-09-07 13:34:54 +02:00
|
|
|
|
import java.io.File
|
2018-08-16 21:58:30 +02:00
|
|
|
|
import java.lang.ref.WeakReference
|
2018-09-07 07:51:59 +02:00
|
|
|
|
import java.nio.ByteBuffer
|
2018-06-11 02:33:01 +02:00
|
|
|
|
import java.text.SimpleDateFormat
|
|
|
|
|
import java.util.*
|
2018-08-25 13:59:57 +02:00
|
|
|
|
import java.util.concurrent.ConcurrentLinkedQueue
|
2018-08-16 21:58:30 +02:00
|
|
|
|
import java.util.concurrent.atomic.AtomicBoolean
|
2018-08-25 13:59:57 +02:00
|
|
|
|
import java.util.concurrent.atomic.AtomicLong
|
2018-08-16 21:58:30 +02:00
|
|
|
|
import java.util.regex.Pattern
|
2018-08-28 18:24:36 +02:00
|
|
|
|
import kotlin.collections.ArrayList
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-04-20 15:22:21 +02:00
|
|
|
|
enum class StreamingIndicatorState {
|
|
|
|
|
NONE,
|
|
|
|
|
REGISTERED, // registered, but not listening
|
|
|
|
|
LISTENING,
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-04 15:35:01 +02:00
|
|
|
|
enum class ColumnTaskType {
|
2018-04-21 01:16:44 +02:00
|
|
|
|
LOADING,
|
|
|
|
|
REFRESH_TOP,
|
|
|
|
|
REFRESH_BOTTOM,
|
|
|
|
|
GAP
|
|
|
|
|
}
|
2018-05-04 15:35:01 +02:00
|
|
|
|
|
2018-04-21 01:16:44 +02:00
|
|
|
|
abstract class ColumnTask(
|
2018-05-04 15:35:01 +02:00
|
|
|
|
val ctType : ColumnTaskType
|
2018-04-21 01:16:44 +02:00
|
|
|
|
) : AsyncTask<Void, Void, TootApiResult?>() {
|
2018-05-04 15:35:01 +02:00
|
|
|
|
|
2018-04-21 01:16:44 +02:00
|
|
|
|
val ctStarted = AtomicBoolean(false)
|
|
|
|
|
val ctClosed = AtomicBoolean(false)
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
class Column(
|
|
|
|
|
val app_state : AppState,
|
|
|
|
|
val context : Context,
|
|
|
|
|
val access_info : SavedAccount,
|
2018-09-07 07:51:59 +02:00
|
|
|
|
val column_type : Int,
|
2018-10-01 02:10:16 +02:00
|
|
|
|
val column_id : String
|
2018-01-12 10:01:25 +01:00
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
|
private val log = LogCategory("Column")
|
|
|
|
|
|
2018-09-07 13:34:54 +02:00
|
|
|
|
private const val DIR_BACKGROUND_IMAGE = "columnBackground"
|
2018-09-07 07:51:59 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
private const val READ_LIMIT = 80 // API側の上限が80です。ただし指定しても40しか返ってこないことが多い
|
|
|
|
|
private const val LOOP_TIMEOUT = 10000L
|
|
|
|
|
private const val LOOP_READ_ENOUGH = 30 // フィルタ後のデータ数がコレ以上ならループを諦めます
|
|
|
|
|
private const val RELATIONSHIP_LOAD_STEP = 40
|
|
|
|
|
private const val ACCT_DB_STEP = 100
|
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
private const val MISSKEY_HASHTAG_LIMIT = 30
|
2018-08-24 10:03:20 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// ステータスのリストを返すAPI
|
2018-03-11 04:53:34 +01:00
|
|
|
|
private const val PATH_HOME = "/api/v1/timelines/home?limit=$READ_LIMIT"
|
2018-04-18 14:38:17 +02:00
|
|
|
|
private const val PATH_DIRECT_MESSAGES = "/api/v1/timelines/direct?limit=$READ_LIMIT"
|
2018-10-09 01:20:43 +02:00
|
|
|
|
private const val PATH_DIRECT_MESSAGES2 = "/api/v1/conversations?limit=$READ_LIMIT"
|
2018-04-18 14:38:17 +02:00
|
|
|
|
|
2018-03-10 01:28:53 +01:00
|
|
|
|
private const val PATH_LOCAL = "/api/v1/timelines/public?limit=$READ_LIMIT&local=true"
|
2018-08-18 12:58:14 +02:00
|
|
|
|
private const val PATH_TL_FEDERATE = "/api/v1/timelines/public?limit=$READ_LIMIT"
|
2018-03-11 04:53:34 +01:00
|
|
|
|
private const val PATH_FAVOURITES = "/api/v1/favourites?limit=$READ_LIMIT"
|
2018-01-19 10:27:35 +01:00
|
|
|
|
private const val PATH_ACCOUNT_STATUSES =
|
2018-08-18 12:58:14 +02:00
|
|
|
|
"/api/v1/accounts/%s/statuses?limit=$READ_LIMIT" // 1:account_id
|
2018-03-11 04:53:34 +01:00
|
|
|
|
private const val PATH_LIST_TL = "/api/v1/timelines/list/%s?limit=$READ_LIMIT"
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
// アカウントのリストを返すAPI
|
2018-01-19 10:27:35 +01:00
|
|
|
|
private const val PATH_ACCOUNT_FOLLOWING =
|
2018-08-18 12:58:14 +02:00
|
|
|
|
"/api/v1/accounts/%s/following?limit=$READ_LIMIT" // 1:account_id
|
2018-01-19 10:27:35 +01:00
|
|
|
|
private const val PATH_ACCOUNT_FOLLOWERS =
|
2018-08-18 12:58:14 +02:00
|
|
|
|
"/api/v1/accounts/%s/followers?limit=$READ_LIMIT" // 1:account_id
|
2018-07-03 06:37:07 +02:00
|
|
|
|
private const val PATH_MUTES = "/api/v1/mutes?limit=$READ_LIMIT"
|
|
|
|
|
private const val PATH_BLOCKS = "/api/v1/blocks?limit=$READ_LIMIT"
|
|
|
|
|
private const val PATH_FOLLOW_REQUESTS = "/api/v1/follow_requests?limit=$READ_LIMIT"
|
|
|
|
|
private const val PATH_FOLLOW_SUGGESTION = "/api/v1/suggestions?limit=$READ_LIMIT"
|
2018-08-28 14:25:45 +02:00
|
|
|
|
private const val PATH_ENDORSEMENT = "/api/v1/endorsements?limit=$READ_LIMIT"
|
2018-07-03 06:37:07 +02:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
private const val PATH_BOOSTED_BY =
|
2018-03-11 04:53:34 +01:00
|
|
|
|
"/api/v1/statuses/%s/reblogged_by?limit=$READ_LIMIT" // 1:status_id
|
2018-01-19 10:27:35 +01:00
|
|
|
|
private const val PATH_FAVOURITED_BY =
|
2018-03-11 04:53:34 +01:00
|
|
|
|
"/api/v1/statuses/%s/favourited_by?limit=$READ_LIMIT" // 1:status_id
|
|
|
|
|
private const val PATH_LIST_MEMBER = "/api/v1/lists/%s/accounts?limit=$READ_LIMIT"
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
// 他のリストを返すAPI
|
2018-03-11 04:53:34 +01:00
|
|
|
|
private const val PATH_REPORTS = "/api/v1/reports?limit=$READ_LIMIT"
|
|
|
|
|
private const val PATH_NOTIFICATIONS = "/api/v1/notifications?limit=$READ_LIMIT"
|
|
|
|
|
private const val PATH_DOMAIN_BLOCK = "/api/v1/domain_blocks?limit=$READ_LIMIT"
|
|
|
|
|
private const val PATH_LIST_LIST = "/api/v1/lists?limit=$READ_LIMIT"
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
// リストではなくオブジェクトを返すAPI
|
2018-08-18 12:58:14 +02:00
|
|
|
|
private const val PATH_ACCOUNT = "/api/v1/accounts/%s" // 1:account_id
|
|
|
|
|
private const val PATH_STATUSES = "/api/v1/statuses/%s" // 1:status_id
|
|
|
|
|
private const val PATH_STATUSES_CONTEXT = "/api/v1/statuses/%s/context" // 1:status_id
|
2018-01-21 13:46:36 +01:00
|
|
|
|
const val PATH_SEARCH = "/api/v1/search?q=%s"
|
2018-05-30 07:18:45 +02:00
|
|
|
|
const val PATH_SEARCH_V2 = "/api/v2/search?q=%s"
|
2018-01-21 13:46:36 +01:00
|
|
|
|
// search args 1: query(urlencoded) , also, append "&resolve=1" if resolve non-local accounts
|
2018-01-04 19:52:25 +01:00
|
|
|
|
private const val PATH_INSTANCE = "/api/v1/instance"
|
|
|
|
|
private const val PATH_LIST_INFO = "/api/v1/lists/%s"
|
|
|
|
|
|
2018-07-06 17:22:22 +02:00
|
|
|
|
const val PATH_FILTERS = "/api/v1/filters"
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
const val PATH_MISSKEY_PROFILE_FOLLOWING = "/api/users/following"
|
|
|
|
|
const val PATH_MISSKEY_PROFILE_FOLLOWERS = "/api/users/followers"
|
|
|
|
|
const val PATH_MISSKEY_PROFILE_STATUSES = "/api/users/notes"
|
2018-08-25 02:29:24 +02:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
const val PATH_MISSKEY_PROFILE = "/api/users/show"
|
2018-08-25 02:29:24 +02:00
|
|
|
|
const val PATH_MISSKEY_MUTES = "/api/mute/list"
|
|
|
|
|
const val PATH_MISSKEY_FOLLOW_REQUESTS = "/api/following/requests/list"
|
|
|
|
|
const val PATH_MISSKEY_FOLLOW_SUGGESTION = "/api/users/recommendation"
|
|
|
|
|
const val PATH_MISSKEY_FAVORITES = "/api/i/favorites"
|
|
|
|
|
|
|
|
|
|
private enum class PagingType {
|
|
|
|
|
Default,
|
|
|
|
|
Cursor,
|
|
|
|
|
Offset,
|
|
|
|
|
None,
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
internal const val KEY_ACCOUNT_ROW_ID = "account_id"
|
|
|
|
|
internal const val KEY_TYPE = "type"
|
2018-09-07 07:51:59 +02:00
|
|
|
|
internal const val KEY_COLUMN_ID = "column_id"
|
2018-01-04 19:52:25 +01:00
|
|
|
|
internal const val KEY_DONT_CLOSE = "dont_close"
|
|
|
|
|
private const val KEY_WITH_ATTACHMENT = "with_attachment"
|
|
|
|
|
private const val KEY_WITH_HIGHLIGHT = "with_highlight"
|
|
|
|
|
private const val KEY_DONT_SHOW_BOOST = "dont_show_boost"
|
|
|
|
|
private const val KEY_DONT_SHOW_FAVOURITE = "dont_show_favourite"
|
|
|
|
|
private const val KEY_DONT_SHOW_FOLLOW = "dont_show_follow"
|
|
|
|
|
private const val KEY_DONT_SHOW_REPLY = "dont_show_reply"
|
2018-08-21 12:19:02 +02:00
|
|
|
|
private const val KEY_DONT_SHOW_REACTION = "dont_show_reaction"
|
|
|
|
|
private const val KEY_DONT_SHOW_VOTE = "dont_show_vote"
|
2018-02-10 01:36:27 +01:00
|
|
|
|
private const val KEY_DONT_SHOW_NORMAL_TOOT = "dont_show_normal_toot"
|
2018-01-04 19:52:25 +01:00
|
|
|
|
private const val KEY_DONT_STREAMING = "dont_streaming"
|
|
|
|
|
private const val KEY_DONT_AUTO_REFRESH = "dont_auto_refresh"
|
|
|
|
|
private const val KEY_HIDE_MEDIA_DEFAULT = "hide_media_default"
|
2018-02-06 21:41:30 +01:00
|
|
|
|
private const val KEY_SYSTEM_NOTIFICATION_NOT_RELATED = "system_notification_not_related"
|
2018-03-25 15:27:58 +02:00
|
|
|
|
private const val KEY_INSTANCE_LOCAL = "instance_local"
|
2018-02-06 21:41:30 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
private const val KEY_ENABLE_SPEECH = "enable_speech"
|
2018-10-10 22:43:10 +02:00
|
|
|
|
private const val KEY_USE_OLD_API = "use_old_api"
|
2018-08-24 15:45:27 +02:00
|
|
|
|
private const val KEY_LAST_VIEWING_ITEM = "lastViewingItem"
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
private const val KEY_REGEX_TEXT = "regex_text"
|
|
|
|
|
|
|
|
|
|
private const val KEY_HEADER_BACKGROUND_COLOR = "header_background_color"
|
|
|
|
|
private const val KEY_HEADER_TEXT_COLOR = "header_text_color"
|
|
|
|
|
private const val KEY_COLUMN_BACKGROUND_COLOR = "column_background_color"
|
|
|
|
|
private const val KEY_COLUMN_ACCT_TEXT_COLOR = "column_acct_text_color"
|
|
|
|
|
private const val KEY_COLUMN_CONTENT_TEXT_COLOR = "column_content_text_color"
|
|
|
|
|
private const val KEY_COLUMN_BACKGROUND_IMAGE = "column_background_image"
|
|
|
|
|
private const val KEY_COLUMN_BACKGROUND_IMAGE_ALPHA = "column_background_image_alpha"
|
|
|
|
|
|
|
|
|
|
private const val KEY_PROFILE_ID = "profile_id"
|
|
|
|
|
private const val KEY_PROFILE_TAB = "tab"
|
|
|
|
|
private const val KEY_STATUS_ID = "status_id"
|
|
|
|
|
private const val KEY_HASHTAG = "hashtag"
|
|
|
|
|
private const val KEY_SEARCH_QUERY = "search_query"
|
|
|
|
|
private const val KEY_SEARCH_RESOLVE = "search_resolve"
|
|
|
|
|
private const val KEY_INSTANCE_URI = "instance_uri"
|
|
|
|
|
|
|
|
|
|
internal const val KEY_COLUMN_ACCESS = "column_access"
|
|
|
|
|
internal const val KEY_COLUMN_ACCESS_COLOR = "column_access_color"
|
|
|
|
|
internal const val KEY_COLUMN_ACCESS_COLOR_BG = "column_access_color_bg"
|
|
|
|
|
internal const val KEY_COLUMN_NAME = "column_name"
|
|
|
|
|
internal const val KEY_OLD_INDEX = "old_index"
|
|
|
|
|
|
|
|
|
|
internal const val TYPE_HOME = 1
|
|
|
|
|
const val TYPE_LOCAL = 2
|
|
|
|
|
internal const val TYPE_FEDERATE = 3
|
|
|
|
|
const val TYPE_PROFILE = 4
|
|
|
|
|
internal const val TYPE_FAVOURITES = 5
|
|
|
|
|
internal const val TYPE_REPORTS = 6
|
|
|
|
|
const val TYPE_NOTIFICATIONS = 7
|
|
|
|
|
const val TYPE_CONVERSATION = 8
|
|
|
|
|
const val TYPE_HASHTAG = 9
|
|
|
|
|
internal const val TYPE_SEARCH = 10
|
|
|
|
|
internal const val TYPE_MUTES = 11
|
|
|
|
|
internal const val TYPE_BLOCKS = 12
|
|
|
|
|
internal const val TYPE_FOLLOW_REQUESTS = 13
|
|
|
|
|
internal const val TYPE_BOOSTED_BY = 14
|
|
|
|
|
internal const val TYPE_FAVOURITED_BY = 15
|
|
|
|
|
internal const val TYPE_DOMAIN_BLOCKS = 16
|
|
|
|
|
internal const val TYPE_SEARCH_MSP = 17
|
|
|
|
|
const val TYPE_INSTANCE_INFORMATION = 18
|
|
|
|
|
internal const val TYPE_LIST_LIST = 19
|
|
|
|
|
internal const val TYPE_LIST_TL = 20
|
|
|
|
|
internal const val TYPE_LIST_MEMBER = 21
|
|
|
|
|
internal const val TYPE_SEARCH_TS = 22
|
2018-04-18 14:38:17 +02:00
|
|
|
|
internal const val TYPE_DIRECT_MESSAGES = 23
|
2018-05-30 07:18:45 +02:00
|
|
|
|
internal const val TYPE_TREND_TAG = 24
|
2018-07-03 06:37:07 +02:00
|
|
|
|
internal const val TYPE_FOLLOW_SUGGESTION = 25
|
2018-07-07 07:15:16 +02:00
|
|
|
|
internal const val TYPE_KEYWORD_FILTER = 26
|
2018-08-23 04:20:10 +02:00
|
|
|
|
internal const val TYPE_MISSKEY_HYBRID = 27
|
2018-08-28 14:25:45 +02:00
|
|
|
|
internal const val TYPE_ENDORSEMENT = 28
|
2018-10-01 02:10:16 +02:00
|
|
|
|
internal const val TYPE_LOCAL_AROUND = 29
|
|
|
|
|
internal const val TYPE_FEDERATED_AROUND = 30
|
|
|
|
|
internal const val TYPE_ACCOUNT_AROUND = 31
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
internal const val TAB_STATUS = 0
|
|
|
|
|
internal const val TAB_FOLLOWING = 1
|
|
|
|
|
internal const val TAB_FOLLOWERS = 2
|
|
|
|
|
|
2018-12-08 16:29:07 +01:00
|
|
|
|
internal var useInstanceTicker = false
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
|
|
|
private inline fun <reified T> getParamAt(params : Array<out Any>, idx : Int) : T {
|
|
|
|
|
return params[idx] as T
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun loadAccount(context : Context, src : JSONObject) : SavedAccount {
|
2018-01-21 13:46:36 +01:00
|
|
|
|
val account_db_id = src.parseLong(KEY_ACCOUNT_ROW_ID) ?: - 1L
|
2018-01-04 19:52:25 +01:00
|
|
|
|
return if(account_db_id >= 0) {
|
2018-01-19 10:27:35 +01:00
|
|
|
|
SavedAccount.loadAccount(context, account_db_id)
|
|
|
|
|
?: throw RuntimeException("missing account")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} else {
|
|
|
|
|
SavedAccount.na
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun getColumnTypeName(context : Context, type : Int) : String {
|
|
|
|
|
return when(type) {
|
|
|
|
|
TYPE_HOME -> context.getString(R.string.home)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
TYPE_LOCAL_AROUND -> context.getString(R.string.ltl_around)
|
|
|
|
|
TYPE_FEDERATED_AROUND -> context.getString(R.string.ftl_around)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
TYPE_ACCOUNT_AROUND -> context.getString(R.string.account_tl_around)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_LOCAL -> context.getString(R.string.local_timeline)
|
|
|
|
|
TYPE_FEDERATE -> context.getString(R.string.federate_timeline)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
TYPE_MISSKEY_HYBRID -> context.getString(R.string.misskey_hybrid_timeline)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_PROFILE -> context.getString(R.string.profile)
|
|
|
|
|
TYPE_FAVOURITES -> context.getString(R.string.favourites)
|
|
|
|
|
TYPE_REPORTS -> context.getString(R.string.reports)
|
|
|
|
|
TYPE_NOTIFICATIONS -> context.getString(R.string.notifications)
|
|
|
|
|
TYPE_CONVERSATION -> context.getString(R.string.conversation)
|
|
|
|
|
TYPE_BOOSTED_BY -> context.getString(R.string.boosted_by)
|
|
|
|
|
TYPE_FAVOURITED_BY -> context.getString(R.string.favourited_by)
|
|
|
|
|
TYPE_HASHTAG -> context.getString(R.string.hashtag)
|
|
|
|
|
TYPE_MUTES -> context.getString(R.string.muted_users)
|
2018-07-07 07:15:16 +02:00
|
|
|
|
TYPE_KEYWORD_FILTER -> context.getString(R.string.keyword_filters)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_BLOCKS -> context.getString(R.string.blocked_users)
|
|
|
|
|
TYPE_DOMAIN_BLOCKS -> context.getString(R.string.blocked_domains)
|
|
|
|
|
TYPE_SEARCH -> context.getString(R.string.search)
|
|
|
|
|
TYPE_SEARCH_MSP -> context.getString(R.string.toot_search_msp)
|
|
|
|
|
TYPE_SEARCH_TS -> context.getString(R.string.toot_search_ts)
|
|
|
|
|
TYPE_INSTANCE_INFORMATION -> context.getString(R.string.instance_information)
|
|
|
|
|
TYPE_FOLLOW_REQUESTS -> context.getString(R.string.follow_requests)
|
2018-07-03 06:37:07 +02:00
|
|
|
|
TYPE_FOLLOW_SUGGESTION -> context.getString(R.string.follow_suggestion)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
TYPE_ENDORSEMENT -> context.getString(R.string.endorse_set)
|
2018-08-28 14:25:45 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_LIST_LIST -> context.getString(R.string.lists)
|
|
|
|
|
TYPE_LIST_MEMBER -> context.getString(R.string.list_member)
|
|
|
|
|
TYPE_LIST_TL -> context.getString(R.string.list_timeline)
|
2018-04-18 14:38:17 +02:00
|
|
|
|
TYPE_DIRECT_MESSAGES -> context.getString(R.string.direct_messages)
|
2018-05-30 21:50:07 +02:00
|
|
|
|
TYPE_TREND_TAG -> context.getString(R.string.trend_tag)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
else -> "?"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun getIconAttrId(acct : String, type : Int) : Int {
|
|
|
|
|
return when(type) {
|
|
|
|
|
TYPE_REPORTS -> R.attr.ic_info
|
|
|
|
|
TYPE_HOME -> R.attr.btn_home
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
TYPE_LOCAL_AROUND -> R.attr.btn_local_tl
|
|
|
|
|
TYPE_FEDERATED_AROUND -> R.attr.btn_federate_tl
|
|
|
|
|
TYPE_ACCOUNT_AROUND -> R.attr.btn_statuses
|
2018-10-09 01:20:43 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_LOCAL -> R.attr.btn_local_tl
|
|
|
|
|
TYPE_FEDERATE -> R.attr.btn_federate_tl
|
2018-10-01 02:10:16 +02:00
|
|
|
|
TYPE_MISSKEY_HYBRID -> R.attr.ic_share
|
2018-10-09 01:20:43 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_PROFILE -> R.attr.btn_statuses
|
|
|
|
|
TYPE_FAVOURITES -> if(SavedAccount.isNicoru(acct)) R.attr.ic_nicoru else R.attr.btn_favourite
|
|
|
|
|
TYPE_NOTIFICATIONS -> R.attr.btn_notification
|
|
|
|
|
TYPE_CONVERSATION -> R.attr.ic_conversation
|
|
|
|
|
TYPE_BOOSTED_BY -> R.attr.btn_boost
|
|
|
|
|
TYPE_FAVOURITED_BY -> if(SavedAccount.isNicoru(acct)) R.attr.ic_nicoru else R.attr.btn_favourite
|
|
|
|
|
TYPE_HASHTAG -> R.attr.ic_hashtag
|
|
|
|
|
TYPE_MUTES -> R.attr.ic_mute
|
2018-07-07 07:15:16 +02:00
|
|
|
|
TYPE_KEYWORD_FILTER -> R.attr.ic_mute
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_BLOCKS -> R.attr.ic_block
|
|
|
|
|
TYPE_DOMAIN_BLOCKS -> R.attr.ic_domain_block
|
|
|
|
|
TYPE_SEARCH, TYPE_SEARCH_MSP, TYPE_SEARCH_TS -> R.attr.ic_search
|
|
|
|
|
TYPE_INSTANCE_INFORMATION -> R.attr.ic_info
|
2018-04-17 18:29:50 +02:00
|
|
|
|
TYPE_FOLLOW_REQUESTS -> R.attr.ic_follow_wait
|
2018-07-03 06:37:07 +02:00
|
|
|
|
TYPE_FOLLOW_SUGGESTION -> R.attr.ic_follow_plus
|
2018-08-28 14:25:45 +02:00
|
|
|
|
TYPE_ENDORSEMENT -> R.attr.ic_follow_plus
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_LIST_LIST -> R.attr.ic_list_list
|
|
|
|
|
TYPE_LIST_MEMBER -> R.attr.ic_list_member
|
|
|
|
|
TYPE_LIST_TL -> R.attr.ic_list_tl
|
2018-04-18 14:38:17 +02:00
|
|
|
|
TYPE_DIRECT_MESSAGES -> R.attr.ic_mail
|
2018-05-30 07:18:45 +02:00
|
|
|
|
TYPE_TREND_TAG -> R.attr.ic_hashtag
|
2018-01-04 19:52:25 +01:00
|
|
|
|
else -> R.attr.ic_info
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-30 23:33:58 +02:00
|
|
|
|
internal val reMaxId = Pattern.compile("[&?]max_id=(\\d+)") // より古いデータの取得に使う
|
|
|
|
|
|
|
|
|
|
private val reMinId = Pattern.compile("[&?]min_id=(\\d+)") // より新しいデータの取得に使う (マストドン2.6.0以降)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
private val reSinceId =
|
|
|
|
|
Pattern.compile("[&?]since_id=(\\d+)") // より新しいデータの取得に使う(マストドン2.6.0未満)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-12-01 00:02:18 +01:00
|
|
|
|
val COLUMN_REGEX_FILTER_DEFAULT : (CharSequence?) -> Boolean = { false }
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-06-11 02:33:01 +02:00
|
|
|
|
private val time_format_hhmm = SimpleDateFormat("HH:mm", Locale.getDefault())
|
|
|
|
|
|
2018-07-03 06:37:07 +02:00
|
|
|
|
private fun getResetTimeString() : String {
|
2018-06-11 02:33:01 +02:00
|
|
|
|
time_format_hhmm.timeZone = TimeZone.getDefault()
|
|
|
|
|
return time_format_hhmm.format(Date(0L))
|
|
|
|
|
}
|
2018-07-06 17:22:22 +02:00
|
|
|
|
|
|
|
|
|
fun onFiltersChanged(context : Context, access_info : SavedAccount) {
|
|
|
|
|
|
|
|
|
|
TootTaskRunner(context, progress_style = TootTaskRunner.PROGRESS_NONE).run(access_info,
|
|
|
|
|
object : TootTask {
|
|
|
|
|
|
|
|
|
|
var filter_list : ArrayList<TootFilter>? = null
|
|
|
|
|
|
|
|
|
|
override fun background(client : TootApiClient) : TootApiResult? {
|
|
|
|
|
val result = client.request(Column.PATH_FILTERS)
|
|
|
|
|
val jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray != null) {
|
|
|
|
|
filter_list = TootFilter.parseList(jsonArray)
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun handleResult(result : TootApiResult?) {
|
|
|
|
|
val filter_list = this.filter_list
|
|
|
|
|
if(filter_list != null) {
|
|
|
|
|
val stream_acct = access_info.acct
|
|
|
|
|
log.d("update filters for $stream_acct")
|
|
|
|
|
for(column in App1.getAppState(context).column_list) {
|
|
|
|
|
if(column.access_info.acct == stream_acct) {
|
|
|
|
|
column.onFiltersChanged2(filter_list)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
2018-08-23 07:45:12 +02:00
|
|
|
|
|
2018-08-25 02:29:24 +02:00
|
|
|
|
private val misskeyArrayFinderUsers = { it : JSONObject -> it.optJSONArray("users") }
|
2018-08-24 18:56:04 +02:00
|
|
|
|
|
2018-08-23 07:45:12 +02:00
|
|
|
|
private val misskeyCustomParserFollowRequest =
|
2018-08-24 10:03:20 +02:00
|
|
|
|
{ parser : TootParser, jsonArray : JSONArray ->
|
2018-08-23 07:45:12 +02:00
|
|
|
|
val dst = ArrayList<TootAccountRef>()
|
2018-08-24 10:03:20 +02:00
|
|
|
|
for(i in 0 until jsonArray.length()) {
|
2018-08-23 07:45:12 +02:00
|
|
|
|
val src = jsonArray.optJSONObject(i) ?: continue
|
|
|
|
|
|
2018-08-24 18:56:04 +02:00
|
|
|
|
val accountRef = TootAccountRef.mayNull(
|
|
|
|
|
parser,
|
|
|
|
|
parser.account(src.optJSONObject("follower"))
|
|
|
|
|
) ?: continue
|
|
|
|
|
|
|
|
|
|
val requestId = EntityId.mayNull(src.parseString("id")) ?: continue
|
|
|
|
|
|
|
|
|
|
accountRef.get()._orderId = requestId
|
|
|
|
|
|
|
|
|
|
dst.add(accountRef)
|
|
|
|
|
}
|
|
|
|
|
dst
|
|
|
|
|
}
|
2018-08-25 02:29:24 +02:00
|
|
|
|
|
2018-11-01 13:48:54 +01:00
|
|
|
|
private val misskeyCustomParserBlocks =
|
|
|
|
|
{ parser : TootParser, jsonArray : JSONArray ->
|
|
|
|
|
val dst = ArrayList<TootAccountRef>()
|
|
|
|
|
for(i in 0 until jsonArray.length()) {
|
|
|
|
|
val src = jsonArray.optJSONObject(i) ?: continue
|
|
|
|
|
|
|
|
|
|
val accountRef = TootAccountRef.mayNull(
|
|
|
|
|
parser,
|
|
|
|
|
parser.account(src.optJSONObject("blockee"))
|
|
|
|
|
) ?: continue
|
|
|
|
|
|
|
|
|
|
val requestId = EntityId.mayNull(src.parseString("id")) ?: continue
|
|
|
|
|
|
|
|
|
|
accountRef.get()._orderId = requestId
|
|
|
|
|
|
|
|
|
|
dst.add(accountRef)
|
|
|
|
|
}
|
|
|
|
|
dst
|
|
|
|
|
}
|
2018-08-24 16:38:32 +02:00
|
|
|
|
private val misskeyCustomParserFavorites =
|
|
|
|
|
{ parser : TootParser, jsonArray : JSONArray ->
|
|
|
|
|
val dst = ArrayList<TootStatus>()
|
|
|
|
|
for(i in 0 until jsonArray.length()) {
|
|
|
|
|
val src = jsonArray.optJSONObject(i) ?: continue
|
|
|
|
|
val note = parser.status(src.optJSONObject("note")) ?: continue
|
|
|
|
|
val favId = EntityId.mayNull(src.parseString("id")) ?: continue
|
|
|
|
|
note.favourited = true
|
|
|
|
|
note._orderId = favId
|
|
|
|
|
dst.add(note)
|
|
|
|
|
}
|
|
|
|
|
dst
|
|
|
|
|
}
|
2018-09-07 07:51:59 +02:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
private val columnIdMap = HashMap<String, WeakReference<Column>?>()
|
|
|
|
|
private fun registerColumnId(id : String, column : Column) {
|
|
|
|
|
synchronized(columnIdMap) {
|
2018-09-07 07:51:59 +02:00
|
|
|
|
columnIdMap[id] = WeakReference(column)
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
private fun generateColumnId() : String {
|
|
|
|
|
synchronized(columnIdMap) {
|
2018-09-07 07:51:59 +02:00
|
|
|
|
val buffer = ByteBuffer.allocate(8)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
var id = ""
|
|
|
|
|
while(id.isEmpty() || columnIdMap.containsKey(id)) {
|
2018-09-07 07:51:59 +02:00
|
|
|
|
if(id.isNotEmpty()) Thread.sleep(1L)
|
|
|
|
|
buffer.clear()
|
|
|
|
|
buffer.putLong(System.currentTimeMillis())
|
|
|
|
|
id = buffer.array().encodeBase64Url()
|
|
|
|
|
}
|
|
|
|
|
columnIdMap[id] = null
|
|
|
|
|
return id
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
private fun decodeColumnId(src : JSONObject) : String {
|
2018-09-07 07:51:59 +02:00
|
|
|
|
return src.parseString(KEY_COLUMN_ID) ?: generateColumnId()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun findColumnById(id : String) : Column? {
|
2018-10-01 02:10:16 +02:00
|
|
|
|
synchronized(columnIdMap) {
|
2018-09-07 07:51:59 +02:00
|
|
|
|
return columnIdMap[id]?.get()
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-07 13:34:54 +02:00
|
|
|
|
|
|
|
|
|
fun getBackgroundImageDir(context : Context) : File {
|
|
|
|
|
val externalDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(externalDir == null) {
|
2018-09-07 13:34:54 +02:00
|
|
|
|
log.e("getExternalFilesDir is null.")
|
2018-10-01 02:10:16 +02:00
|
|
|
|
} else {
|
2018-09-07 13:34:54 +02:00
|
|
|
|
val state = Environment.getExternalStorageState()
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(state != Environment.MEDIA_MOUNTED) {
|
2018-09-07 13:34:54 +02:00
|
|
|
|
log.e("getExternalStorageState: ${state}")
|
2018-10-01 02:10:16 +02:00
|
|
|
|
} else {
|
2018-09-07 13:34:54 +02:00
|
|
|
|
log.i("externalDir: ${externalDir}")
|
|
|
|
|
externalDir.mkdir()
|
2018-10-01 02:10:16 +02:00
|
|
|
|
val backgroundDir = File(externalDir, DIR_BACKGROUND_IMAGE)
|
2018-09-07 13:34:54 +02:00
|
|
|
|
backgroundDir.mkdir()
|
|
|
|
|
log.i("backgroundDir: ${backgroundDir} exists=${backgroundDir.exists()}")
|
|
|
|
|
return backgroundDir
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
val backgroundDir = context.getDir(Column.DIR_BACKGROUND_IMAGE, Context.MODE_PRIVATE)
|
2018-09-07 13:34:54 +02:00
|
|
|
|
log.i("backgroundDir: ${backgroundDir} exists=${backgroundDir.exists()}")
|
|
|
|
|
return backgroundDir
|
|
|
|
|
}
|
2018-11-19 23:46:14 +01:00
|
|
|
|
|
|
|
|
|
private var defaultColorHeaderBg = 0
|
|
|
|
|
private var defaultColorHeaderName = 0
|
|
|
|
|
private var defaultColorHeaderPageNumber = 0
|
|
|
|
|
private var defaultColorContentBg = 0
|
|
|
|
|
private var defaultColorContentAcct = 0
|
|
|
|
|
private var defaultColorContentText = 0
|
|
|
|
|
|
|
|
|
|
fun reloadDefaultColor(activity : AppCompatActivity, pref : SharedPreferences) {
|
|
|
|
|
var c : Int
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
c = Pref.ipCcdHeaderBg(pref)
|
2018-12-01 00:02:18 +01:00
|
|
|
|
if(c == 0) c = getAttributeColor(activity, R.attr.color_column_header)
|
2018-11-19 23:46:14 +01:00
|
|
|
|
defaultColorHeaderBg = c
|
|
|
|
|
//
|
|
|
|
|
c = Pref.ipCcdHeaderFg(pref)
|
2018-12-01 00:02:18 +01:00
|
|
|
|
if(c == 0) c = getAttributeColor(activity, R.attr.colorColumnHeaderName)
|
2018-11-19 23:46:14 +01:00
|
|
|
|
defaultColorHeaderName = c
|
|
|
|
|
//
|
|
|
|
|
c = Pref.ipCcdHeaderFg(pref)
|
2018-12-01 00:02:18 +01:00
|
|
|
|
if(c == 0) c = getAttributeColor(activity, R.attr.colorColumnHeaderPageNumber)
|
2018-11-19 23:46:14 +01:00
|
|
|
|
defaultColorHeaderPageNumber = c
|
|
|
|
|
//
|
|
|
|
|
c = Pref.ipCcdContentBg(pref)
|
|
|
|
|
defaultColorContentBg = c
|
|
|
|
|
//
|
|
|
|
|
c = Pref.ipCcdContentAcct(pref)
|
2018-12-01 00:02:18 +01:00
|
|
|
|
if(c == 0) c = getAttributeColor(activity, R.attr.colorTimeSmall)
|
2018-11-19 23:46:14 +01:00
|
|
|
|
defaultColorContentAcct = c
|
|
|
|
|
//
|
|
|
|
|
c = Pref.ipCcdContentText(pref)
|
2018-12-01 00:02:18 +01:00
|
|
|
|
if(c == 0) c = getAttributeColor(activity, R.attr.colorContentText)
|
2018-11-19 23:46:14 +01:00
|
|
|
|
defaultColorContentText = c
|
|
|
|
|
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private var callback_ref : WeakReference<Callback>? = null
|
|
|
|
|
|
|
|
|
|
private val isActivityStart : Boolean
|
|
|
|
|
get() {
|
2018-01-10 16:47:35 +01:00
|
|
|
|
return callback_ref?.get()?.isActivityStart ?: false
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private val streamPath : String?
|
2018-08-25 02:29:24 +02:00
|
|
|
|
get() = if(isMisskey) {
|
|
|
|
|
val misskeyApiToken = access_info.misskeyApiToken
|
|
|
|
|
if(misskeyApiToken == null) {
|
2018-09-04 19:17:58 +02:00
|
|
|
|
// Misskey 8.25 からLTLだけ認証なしでも見れるようになった
|
|
|
|
|
when(column_type) {
|
|
|
|
|
TYPE_LOCAL -> "/local-timeline"
|
|
|
|
|
else -> null
|
|
|
|
|
}
|
2018-08-25 02:29:24 +02:00
|
|
|
|
} else {
|
|
|
|
|
when(column_type) {
|
|
|
|
|
TYPE_HOME, TYPE_NOTIFICATIONS -> "/?i=$misskeyApiToken"
|
|
|
|
|
TYPE_LOCAL -> "/local-timeline?i=$misskeyApiToken"
|
|
|
|
|
TYPE_MISSKEY_HYBRID -> "/hybrid-timeline?i=$misskeyApiToken"
|
2018-08-25 03:32:10 +02:00
|
|
|
|
TYPE_FEDERATE -> "/global-timeline?i=$misskeyApiToken"
|
2018-08-31 14:47:40 +02:00
|
|
|
|
TYPE_LIST_TL -> "/user-list?i=$misskeyApiToken&listId=$profile_id"
|
2018-08-25 02:29:24 +02:00
|
|
|
|
else -> null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
when(column_type) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_HOME, TYPE_NOTIFICATIONS -> "/api/v1/streaming/?stream=user"
|
|
|
|
|
TYPE_LOCAL -> "/api/v1/streaming/?stream=public:local"
|
|
|
|
|
TYPE_FEDERATE -> "/api/v1/streaming/?stream=public"
|
2018-08-31 14:47:40 +02:00
|
|
|
|
TYPE_LIST_TL -> "/api/v1/streaming/?stream=list&list=$profile_id"
|
2018-03-25 15:27:58 +02:00
|
|
|
|
|
2018-04-18 14:38:17 +02:00
|
|
|
|
TYPE_DIRECT_MESSAGES -> "/api/v1/streaming/?stream=direct"
|
|
|
|
|
|
2018-03-25 15:27:58 +02:00
|
|
|
|
TYPE_HASHTAG -> when(instance_local) {
|
2018-04-18 14:38:17 +02:00
|
|
|
|
true -> "/api/v1/streaming/?stream=" + Uri.encode("hashtag:local") + "&tag=" + hashtag.encodePercent()
|
2018-03-25 15:27:58 +02:00
|
|
|
|
else -> "/api/v1/streaming/?stream=hashtag&tag=" + hashtag.encodePercent()
|
2018-08-16 21:58:30 +02:00
|
|
|
|
// タグ先頭の#を含まない
|
2018-03-25 15:27:58 +02:00
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
else -> null
|
|
|
|
|
}
|
2018-08-25 02:29:24 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private val isPublicStream : Boolean
|
|
|
|
|
get() {
|
|
|
|
|
return when(column_type) {
|
2018-10-01 02:10:16 +02:00
|
|
|
|
TYPE_LOCAL, TYPE_FEDERATE, TYPE_HASHTAG, TYPE_LOCAL_AROUND, TYPE_FEDERATED_AROUND -> true
|
2018-01-04 19:52:25 +01:00
|
|
|
|
else -> false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal var dont_close : Boolean = false
|
|
|
|
|
|
|
|
|
|
internal var with_attachment : Boolean = false
|
|
|
|
|
internal var with_highlight : Boolean = false
|
|
|
|
|
internal var dont_show_boost : Boolean = false
|
|
|
|
|
internal var dont_show_reply : Boolean = false
|
2018-08-21 12:19:02 +02:00
|
|
|
|
internal var dont_show_reaction : Boolean = false
|
|
|
|
|
internal var dont_show_vote : Boolean = false
|
|
|
|
|
|
2018-02-10 01:36:27 +01:00
|
|
|
|
internal var dont_show_normal_toot : Boolean = false
|
2018-01-04 19:52:25 +01:00
|
|
|
|
internal var dont_show_favourite : Boolean = false // 通知カラムのみ
|
|
|
|
|
internal var dont_show_follow : Boolean = false // 通知カラムのみ
|
|
|
|
|
internal var dont_streaming : Boolean = false
|
|
|
|
|
internal var dont_auto_refresh : Boolean = false
|
|
|
|
|
internal var hide_media_default : Boolean = false
|
2018-02-06 21:41:30 +01:00
|
|
|
|
internal var system_notification_not_related : Boolean = false
|
2018-03-25 15:27:58 +02:00
|
|
|
|
internal var instance_local : Boolean = false
|
|
|
|
|
|
2018-10-10 22:43:10 +02:00
|
|
|
|
internal var enable_speech : Boolean = false
|
|
|
|
|
internal var use_old_api = false
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
internal var regex_text : String = ""
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
internal var header_bg_color : Int = 0
|
|
|
|
|
internal var header_fg_color : Int = 0
|
|
|
|
|
internal var column_bg_color : Int = 0
|
|
|
|
|
internal var acct_color : Int = 0
|
|
|
|
|
internal var content_color : Int = 0
|
2018-01-10 16:47:35 +01:00
|
|
|
|
internal var column_bg_image : String = ""
|
2018-01-04 19:52:25 +01:00
|
|
|
|
internal var column_bg_image_alpha = 1f
|
|
|
|
|
|
|
|
|
|
internal var profile_tab = TAB_STATUS
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
private var status_id : EntityId? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
// プロフカラムではアカウントのID。リストカラムではリストのID
|
2018-11-01 13:48:54 +01:00
|
|
|
|
internal var profile_id : EntityId? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
internal var search_query : String = ""
|
|
|
|
|
internal var search_resolve : Boolean = false
|
|
|
|
|
private var hashtag : String = ""
|
|
|
|
|
internal var instance_uri : String = ""
|
|
|
|
|
|
|
|
|
|
// プロフカラムでのアカウント情報
|
2018-01-19 10:27:35 +01:00
|
|
|
|
@Volatile
|
2018-05-08 10:25:02 +02:00
|
|
|
|
internal var who_account : TootAccountRef? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
// リストカラムでのリスト情報
|
2018-01-19 10:27:35 +01:00
|
|
|
|
@Volatile
|
|
|
|
|
private var list_info : TootList? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
// 「インスタンス情報」カラムに表示するインスタンス情報
|
|
|
|
|
// (SavedAccount中のインスタンス情報とは異なるので注意)
|
|
|
|
|
internal var instance_information : TootInstance? = null
|
2018-12-07 01:50:11 +01:00
|
|
|
|
internal var handshake : Handshake? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
internal var scroll_save : ScrollPosition? = null
|
2018-08-25 02:29:24 +02:00
|
|
|
|
private var last_viewing_item_id : EntityId? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
internal val is_dispose = AtomicBoolean()
|
|
|
|
|
|
|
|
|
|
internal var bFirstInitialized = false
|
|
|
|
|
|
2018-07-07 07:15:16 +02:00
|
|
|
|
var filter_reload_required : Boolean = false
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
// カラムを閉じた後のnotifyDataSetChangedのタイミングで、add/removeされる順序が期待通りにならないので
|
|
|
|
|
// 参照を1つだけ持つのではなく、リストを保持して先頭の要素を使うことにする
|
|
|
|
|
|
|
|
|
|
private val _holder_list = LinkedList<ColumnViewHolder>()
|
|
|
|
|
|
|
|
|
|
internal // 複数のリスナがある場合、最も新しいものを返す
|
|
|
|
|
val viewHolder : ColumnViewHolder?
|
|
|
|
|
get() {
|
|
|
|
|
if(is_dispose.get()) return null
|
|
|
|
|
return if(_holder_list.isEmpty()) null else _holder_list.first
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
2018-04-21 01:16:44 +02:00
|
|
|
|
internal var lastTask : ColumnTask? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
internal var bInitialLoading : Boolean = false
|
|
|
|
|
internal var bRefreshLoading : Boolean = false
|
|
|
|
|
|
|
|
|
|
internal var mInitialLoadingError : String = ""
|
|
|
|
|
internal var mRefreshLoadingError : String = ""
|
2018-08-04 20:07:55 +02:00
|
|
|
|
internal var mRefreshLoadingErrorTime : Long = 0L
|
2018-11-20 10:32:30 +01:00
|
|
|
|
internal var mRefreshLoadingErrorPopupState : Int = 0
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
internal var task_progress : String? = null
|
|
|
|
|
|
2018-01-20 07:51:14 +01:00
|
|
|
|
internal val list_data = BucketList<TimelineItem>()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
private val duplicate_map = DuplicateMap()
|
|
|
|
|
|
|
|
|
|
private val isFilterEnabled : Boolean
|
|
|
|
|
get() = (with_attachment
|
|
|
|
|
|| with_highlight
|
|
|
|
|
|| dont_show_boost
|
|
|
|
|
|| dont_show_favourite
|
|
|
|
|
|| dont_show_follow
|
|
|
|
|
|| dont_show_reply
|
2018-08-21 12:19:02 +02:00
|
|
|
|
|| dont_show_reaction
|
|
|
|
|
|| dont_show_vote
|
2018-02-10 01:36:27 +01:00
|
|
|
|
|| dont_show_normal_toot
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|| regex_text.isNotEmpty()
|
|
|
|
|
)
|
|
|
|
|
|
2018-09-17 20:06:15 +02:00
|
|
|
|
@Volatile
|
2018-01-04 19:52:25 +01:00
|
|
|
|
private var column_regex_filter = COLUMN_REGEX_FILTER_DEFAULT
|
2018-09-17 20:06:15 +02:00
|
|
|
|
|
|
|
|
|
@Volatile
|
2018-07-06 17:22:22 +02:00
|
|
|
|
private var muted_word2 : WordTrieTree? = null
|
2018-09-17 20:06:15 +02:00
|
|
|
|
|
|
|
|
|
@Volatile
|
2018-03-15 17:23:43 +01:00
|
|
|
|
private var favMuteSet : HashSet<String>? = null
|
2018-09-17 20:06:15 +02:00
|
|
|
|
|
|
|
|
|
@Volatile
|
2018-01-04 19:52:25 +01:00
|
|
|
|
private var highlight_trie : WordTrieTree? = null
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// タイムライン中のデータの始端と終端
|
|
|
|
|
// misskeyは
|
|
|
|
|
private var idRecent : EntityId? = null
|
|
|
|
|
private var idOld : EntityId? = null
|
2018-08-25 02:29:24 +02:00
|
|
|
|
private var offsetNext : Int = 0
|
|
|
|
|
private var pagingType : PagingType = PagingType.Default
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-04-20 15:22:21 +02:00
|
|
|
|
var bRefreshingTop : Boolean = false
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
// ListViewの表示更新が追いつかないとスクロール位置が崩れるので
|
|
|
|
|
// 一定時間より短期間にはデータ更新しないようにする
|
2018-08-25 13:59:57 +02:00
|
|
|
|
private val last_show_stream_data = AtomicLong(0L)
|
|
|
|
|
private val stream_data_queue = ConcurrentLinkedQueue<TimelineItem>()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
private var bPutGap : Boolean = false
|
|
|
|
|
|
|
|
|
|
@Suppress("unused")
|
|
|
|
|
val listTitle : String
|
|
|
|
|
get() {
|
|
|
|
|
return when(column_type) {
|
|
|
|
|
TYPE_LIST_MEMBER, TYPE_LIST_TL -> {
|
|
|
|
|
val sv = list_info?.title
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(sv != null && sv.isNotEmpty()) sv else profile_id.toString()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else -> "?"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Suppress("unused")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val listId : EntityId?
|
2018-01-04 19:52:25 +01:00
|
|
|
|
get() {
|
|
|
|
|
return when(column_type) {
|
|
|
|
|
TYPE_LIST_MEMBER, TYPE_LIST_TL -> profile_id
|
2018-08-18 12:58:14 +02:00
|
|
|
|
else -> null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val isSearchColumn : Boolean
|
|
|
|
|
get() {
|
|
|
|
|
return when(column_type) {
|
|
|
|
|
TYPE_SEARCH, TYPE_SEARCH_MSP, TYPE_SEARCH_TS -> true
|
|
|
|
|
else -> false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal interface Callback {
|
|
|
|
|
val isActivityStart : Boolean
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
internal constructor(
|
|
|
|
|
app_state : AppState,
|
|
|
|
|
access_info : SavedAccount,
|
|
|
|
|
callback : Callback,
|
|
|
|
|
type : Int,
|
|
|
|
|
vararg params : Any
|
|
|
|
|
)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
: this(app_state, app_state.context, access_info, type, generateColumnId()) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
this.callback_ref = WeakReference(callback)
|
|
|
|
|
when(type) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
TYPE_CONVERSATION, TYPE_BOOSTED_BY, TYPE_FAVOURITED_BY, TYPE_LOCAL_AROUND, TYPE_FEDERATED_AROUND, TYPE_ACCOUNT_AROUND -> status_id =
|
2018-01-19 10:27:35 +01:00
|
|
|
|
getParamAt(params, 0)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_PROFILE, TYPE_LIST_TL, TYPE_LIST_MEMBER -> profile_id = getParamAt(params, 0)
|
|
|
|
|
TYPE_HASHTAG -> hashtag = getParamAt(params, 0)
|
|
|
|
|
|
|
|
|
|
TYPE_SEARCH -> {
|
|
|
|
|
search_query = getParamAt(params, 0)
|
|
|
|
|
search_resolve = getParamAt(params, 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TYPE_SEARCH_MSP, TYPE_SEARCH_TS -> search_query = getParamAt(params, 0)
|
|
|
|
|
TYPE_INSTANCE_INFORMATION -> instance_uri = getParamAt(params, 0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal constructor(app_state : AppState, src : JSONObject)
|
2018-01-19 10:27:35 +01:00
|
|
|
|
: this(
|
|
|
|
|
app_state,
|
|
|
|
|
app_state.context,
|
|
|
|
|
loadAccount(app_state.context, src),
|
2018-09-07 07:51:59 +02:00
|
|
|
|
src.optInt(KEY_TYPE),
|
|
|
|
|
decodeColumnId(src)
|
2018-01-19 10:27:35 +01:00
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
dont_close = src.optBoolean(KEY_DONT_CLOSE)
|
|
|
|
|
with_attachment = src.optBoolean(KEY_WITH_ATTACHMENT)
|
|
|
|
|
with_highlight = src.optBoolean(KEY_WITH_HIGHLIGHT)
|
|
|
|
|
dont_show_boost = src.optBoolean(KEY_DONT_SHOW_BOOST)
|
|
|
|
|
dont_show_follow = src.optBoolean(KEY_DONT_SHOW_FOLLOW)
|
|
|
|
|
dont_show_favourite = src.optBoolean(KEY_DONT_SHOW_FAVOURITE)
|
|
|
|
|
dont_show_reply = src.optBoolean(KEY_DONT_SHOW_REPLY)
|
2018-08-21 12:19:02 +02:00
|
|
|
|
dont_show_reaction = src.optBoolean(KEY_DONT_SHOW_REACTION)
|
|
|
|
|
dont_show_vote = src.optBoolean(KEY_DONT_SHOW_VOTE)
|
2018-02-10 01:36:27 +01:00
|
|
|
|
dont_show_normal_toot = src.optBoolean(KEY_DONT_SHOW_NORMAL_TOOT)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
dont_streaming = src.optBoolean(KEY_DONT_STREAMING)
|
|
|
|
|
dont_auto_refresh = src.optBoolean(KEY_DONT_AUTO_REFRESH)
|
|
|
|
|
hide_media_default = src.optBoolean(KEY_HIDE_MEDIA_DEFAULT)
|
2018-02-06 21:41:30 +01:00
|
|
|
|
system_notification_not_related = src.optBoolean(KEY_SYSTEM_NOTIFICATION_NOT_RELATED)
|
2018-03-25 15:27:58 +02:00
|
|
|
|
instance_local = src.optBoolean(KEY_INSTANCE_LOCAL)
|
2018-02-06 21:41:30 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
enable_speech = src.optBoolean(KEY_ENABLE_SPEECH)
|
2018-10-10 22:43:10 +02:00
|
|
|
|
use_old_api = src.optBoolean(KEY_USE_OLD_API)
|
2018-08-24 16:38:32 +02:00
|
|
|
|
last_viewing_item_id = EntityId.from(src, KEY_LAST_VIEWING_ITEM)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-21 13:46:36 +01:00
|
|
|
|
regex_text = src.parseString(KEY_REGEX_TEXT) ?: ""
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
header_bg_color = src.optInt(KEY_HEADER_BACKGROUND_COLOR)
|
|
|
|
|
header_fg_color = src.optInt(KEY_HEADER_TEXT_COLOR)
|
|
|
|
|
column_bg_color = src.optInt(KEY_COLUMN_BACKGROUND_COLOR)
|
|
|
|
|
acct_color = src.optInt(KEY_COLUMN_ACCT_TEXT_COLOR)
|
|
|
|
|
content_color = src.optInt(KEY_COLUMN_CONTENT_TEXT_COLOR)
|
2018-01-21 13:46:36 +01:00
|
|
|
|
column_bg_image = src.parseString(KEY_COLUMN_BACKGROUND_IMAGE) ?: ""
|
2018-01-04 19:52:25 +01:00
|
|
|
|
column_bg_image_alpha = src.optDouble(KEY_COLUMN_BACKGROUND_IMAGE_ALPHA, 1.0).toFloat()
|
|
|
|
|
|
|
|
|
|
when(column_type) {
|
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
TYPE_CONVERSATION, TYPE_BOOSTED_BY, TYPE_FAVOURITED_BY, TYPE_LOCAL_AROUND, TYPE_FEDERATED_AROUND, TYPE_ACCOUNT_AROUND -> status_id =
|
2018-10-01 02:10:16 +02:00
|
|
|
|
when(isMisskey) {
|
|
|
|
|
true -> EntityId.mayNull(src.parseString(KEY_STATUS_ID))
|
|
|
|
|
else -> EntityId.mayNull(src.parseLong(KEY_STATUS_ID))
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_PROFILE -> {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
profile_id = when(isMisskey) {
|
|
|
|
|
true -> EntityId.mayNull(src.parseString(KEY_PROFILE_ID))
|
|
|
|
|
else -> EntityId.mayNull(src.parseLong(KEY_PROFILE_ID))
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
profile_tab = src.optInt(KEY_PROFILE_TAB)
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
TYPE_LIST_MEMBER, TYPE_LIST_TL -> {
|
|
|
|
|
profile_id = when(isMisskey) {
|
|
|
|
|
true -> EntityId.mayNull(src.parseString(KEY_PROFILE_ID))
|
|
|
|
|
else -> EntityId.mayNull(src.parseLong(KEY_PROFILE_ID))
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_HASHTAG -> hashtag = src.optString(KEY_HASHTAG)
|
|
|
|
|
|
|
|
|
|
TYPE_SEARCH -> {
|
|
|
|
|
search_query = src.optString(KEY_SEARCH_QUERY)
|
|
|
|
|
search_resolve = src.optBoolean(KEY_SEARCH_RESOLVE, false)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TYPE_SEARCH_MSP, TYPE_SEARCH_TS -> search_query = src.optString(KEY_SEARCH_QUERY)
|
|
|
|
|
|
|
|
|
|
TYPE_INSTANCE_INFORMATION -> instance_uri = src.optString(KEY_INSTANCE_URI)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-30 20:29:00 +01:00
|
|
|
|
private fun JSONObject.putIfTrue(key : String, value : Boolean) {
|
|
|
|
|
if(value) put(key, true)
|
2018-10-10 22:43:10 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
@Throws(JSONException::class)
|
|
|
|
|
fun encodeJSON(dst : JSONObject, old_index : Int) {
|
|
|
|
|
dst.put(KEY_ACCOUNT_ROW_ID, access_info.db_id)
|
|
|
|
|
dst.put(KEY_TYPE, column_type)
|
2018-09-07 07:51:59 +02:00
|
|
|
|
dst.put(KEY_COLUMN_ID, column_id)
|
2018-10-10 22:43:10 +02:00
|
|
|
|
|
|
|
|
|
dst.putIfTrue(KEY_DONT_CLOSE, dont_close)
|
|
|
|
|
dst.putIfTrue(KEY_WITH_ATTACHMENT, with_attachment)
|
|
|
|
|
dst.putIfTrue(KEY_WITH_HIGHLIGHT, with_highlight)
|
|
|
|
|
dst.putIfTrue(KEY_DONT_SHOW_BOOST, dont_show_boost)
|
|
|
|
|
dst.putIfTrue(KEY_DONT_SHOW_FOLLOW, dont_show_follow)
|
|
|
|
|
dst.putIfTrue(KEY_DONT_SHOW_FAVOURITE, dont_show_favourite)
|
|
|
|
|
dst.putIfTrue(KEY_DONT_SHOW_REPLY, dont_show_reply)
|
|
|
|
|
dst.putIfTrue(KEY_DONT_SHOW_REACTION, dont_show_reaction)
|
|
|
|
|
dst.putIfTrue(KEY_DONT_SHOW_VOTE, dont_show_vote)
|
|
|
|
|
dst.putIfTrue(KEY_DONT_SHOW_NORMAL_TOOT, dont_show_normal_toot)
|
|
|
|
|
dst.putIfTrue(KEY_DONT_STREAMING, dont_streaming)
|
|
|
|
|
dst.putIfTrue(KEY_DONT_AUTO_REFRESH, dont_auto_refresh)
|
|
|
|
|
dst.putIfTrue(KEY_HIDE_MEDIA_DEFAULT, hide_media_default)
|
|
|
|
|
dst.putIfTrue(KEY_SYSTEM_NOTIFICATION_NOT_RELATED, system_notification_not_related)
|
|
|
|
|
dst.putIfTrue(KEY_INSTANCE_LOCAL, instance_local)
|
|
|
|
|
dst.putIfTrue(KEY_ENABLE_SPEECH, enable_speech)
|
|
|
|
|
dst.putIfTrue(KEY_USE_OLD_API, use_old_api)
|
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
last_viewing_item_id?.putTo(dst, KEY_LAST_VIEWING_ITEM)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
dst.put(KEY_REGEX_TEXT, regex_text)
|
|
|
|
|
|
|
|
|
|
dst.put(KEY_HEADER_BACKGROUND_COLOR, header_bg_color)
|
|
|
|
|
dst.put(KEY_HEADER_TEXT_COLOR, header_fg_color)
|
|
|
|
|
dst.put(KEY_COLUMN_BACKGROUND_COLOR, column_bg_color)
|
|
|
|
|
dst.put(KEY_COLUMN_ACCT_TEXT_COLOR, acct_color)
|
|
|
|
|
dst.put(KEY_COLUMN_CONTENT_TEXT_COLOR, content_color)
|
|
|
|
|
dst.put(KEY_COLUMN_BACKGROUND_IMAGE, column_bg_image)
|
|
|
|
|
dst.put(KEY_COLUMN_BACKGROUND_IMAGE_ALPHA, column_bg_image_alpha.toDouble())
|
|
|
|
|
|
|
|
|
|
when(column_type) {
|
2018-08-20 02:07:55 +02:00
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
TYPE_CONVERSATION, TYPE_BOOSTED_BY, TYPE_FAVOURITED_BY, TYPE_LOCAL_AROUND, TYPE_FEDERATED_AROUND, TYPE_ACCOUNT_AROUND ->
|
2018-08-20 02:07:55 +02:00
|
|
|
|
dst.put(KEY_STATUS_ID, status_id.toString())
|
|
|
|
|
|
2018-08-19 07:54:32 +02:00
|
|
|
|
TYPE_PROFILE ->
|
|
|
|
|
dst.put(KEY_PROFILE_ID, profile_id.toString()).put(KEY_PROFILE_TAB, profile_tab)
|
2018-08-20 02:07:55 +02:00
|
|
|
|
|
2018-08-19 07:54:32 +02:00
|
|
|
|
TYPE_LIST_MEMBER, TYPE_LIST_TL ->
|
|
|
|
|
dst.put(KEY_PROFILE_ID, profile_id.toString())
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_HASHTAG -> dst.put(KEY_HASHTAG, hashtag)
|
2018-08-20 02:07:55 +02:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
TYPE_SEARCH -> dst.put(KEY_SEARCH_QUERY, search_query).put(
|
|
|
|
|
KEY_SEARCH_RESOLVE,
|
|
|
|
|
search_resolve
|
|
|
|
|
)
|
2018-08-20 02:07:55 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_SEARCH_MSP, TYPE_SEARCH_TS -> dst.put(KEY_SEARCH_QUERY, search_query)
|
2018-08-20 02:07:55 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
TYPE_INSTANCE_INFORMATION -> dst.put(KEY_INSTANCE_URI, instance_uri)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 以下は保存には必要ないが、カラムリスト画面で使う
|
|
|
|
|
val ac = AcctColor.load(access_info.acct)
|
|
|
|
|
dst.put(KEY_COLUMN_ACCESS, if(AcctColor.hasNickname(ac)) ac.nickname else access_info.acct)
|
|
|
|
|
dst.put(KEY_COLUMN_ACCESS_COLOR, if(AcctColor.hasColorForeground(ac)) ac.color_fg else 0)
|
|
|
|
|
dst.put(KEY_COLUMN_ACCESS_COLOR_BG, if(AcctColor.hasColorBackground(ac)) ac.color_bg else 0)
|
|
|
|
|
dst.put(KEY_COLUMN_NAME, getColumnName(true))
|
|
|
|
|
dst.put(KEY_OLD_INDEX, old_index)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun isSameSpec(ai : SavedAccount, type : Int, params : Array<out Any>) : Boolean {
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(type != column_type || ai.acct != access_info.acct) return false
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
return try {
|
|
|
|
|
when(type) {
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
TYPE_PROFILE, TYPE_LIST_TL, TYPE_LIST_MEMBER ->
|
|
|
|
|
profile_id == when(isMisskey) {
|
|
|
|
|
true -> EntityIdString(getParamAt(params, 0))
|
|
|
|
|
else -> EntityIdLong(getParamAt(params, 0))
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
TYPE_CONVERSATION, TYPE_BOOSTED_BY, TYPE_FAVOURITED_BY, TYPE_LOCAL_AROUND, TYPE_FEDERATED_AROUND, TYPE_ACCOUNT_AROUND ->
|
2018-08-18 12:58:14 +02:00
|
|
|
|
status_id == when(isMisskey) {
|
|
|
|
|
true -> EntityIdString(getParamAt(params, 0))
|
|
|
|
|
else -> EntityIdLong(getParamAt(params, 0))
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_HASHTAG -> getParamAt<String>(params, 0) == hashtag
|
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
TYPE_SEARCH -> getParamAt<String>(params, 0) == search_query && getParamAt<Boolean>(
|
|
|
|
|
params,
|
|
|
|
|
1
|
|
|
|
|
) == search_resolve
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_SEARCH_MSP, TYPE_SEARCH_TS -> getParamAt<String>(params, 0) == search_query
|
|
|
|
|
|
|
|
|
|
TYPE_INSTANCE_INFORMATION -> getParamAt<String>(params, 0) == instance_uri
|
|
|
|
|
|
|
|
|
|
else -> true
|
|
|
|
|
}
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun getColumnName(bLong : Boolean) : String {
|
|
|
|
|
return when(column_type) {
|
|
|
|
|
|
2018-05-08 10:25:02 +02:00
|
|
|
|
TYPE_PROFILE -> {
|
2018-05-18 19:08:46 +02:00
|
|
|
|
val who = who_account?.get()
|
2018-05-08 10:25:02 +02:00
|
|
|
|
context.getString(
|
|
|
|
|
R.string.profile_of,
|
|
|
|
|
if(who != null)
|
|
|
|
|
AcctColor.getNickname(access_info.getFullAcct(who))
|
|
|
|
|
else
|
|
|
|
|
profile_id.toString()
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
TYPE_LIST_MEMBER -> context.getString(
|
|
|
|
|
R.string.list_member_of,
|
|
|
|
|
list_info?.title ?: profile_id.toString()
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
TYPE_LIST_TL -> context.getString(
|
|
|
|
|
R.string.list_tl_of,
|
|
|
|
|
list_info?.title ?: profile_id.toString()
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
TYPE_CONVERSATION -> context.getString(
|
|
|
|
|
R.string.conversation_around,
|
|
|
|
|
(status_id?.toString() ?: "null")
|
|
|
|
|
)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
TYPE_LOCAL_AROUND -> context.getString(
|
|
|
|
|
R.string.ltl_around_of,
|
|
|
|
|
(status_id?.toString() ?: "null")
|
|
|
|
|
)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
TYPE_FEDERATED_AROUND -> context.getString(
|
|
|
|
|
R.string.ftl_around_of,
|
|
|
|
|
(status_id?.toString() ?: "null")
|
|
|
|
|
)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
|
|
|
|
|
TYPE_ACCOUNT_AROUND -> context.getString(
|
2018-10-01 02:10:16 +02:00
|
|
|
|
R.string.account_tl_around_of,
|
|
|
|
|
(status_id?.toString() ?: "null")
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_HASHTAG -> context.getString(R.string.hashtag_of, hashtag)
|
|
|
|
|
|
|
|
|
|
TYPE_SEARCH ->
|
|
|
|
|
if(bLong) context.getString(R.string.search_of, search_query)
|
|
|
|
|
else getColumnTypeName(context, column_type)
|
|
|
|
|
|
|
|
|
|
TYPE_SEARCH_MSP ->
|
|
|
|
|
if(bLong) context.getString(R.string.toot_search_msp_of, search_query)
|
|
|
|
|
else getColumnTypeName(context, column_type)
|
|
|
|
|
|
|
|
|
|
TYPE_SEARCH_TS ->
|
|
|
|
|
if(bLong) context.getString(R.string.toot_search_ts_of, search_query)
|
|
|
|
|
else getColumnTypeName(context, column_type)
|
|
|
|
|
|
|
|
|
|
TYPE_INSTANCE_INFORMATION ->
|
|
|
|
|
if(bLong) context.getString(R.string.instance_information_of, instance_uri)
|
|
|
|
|
else getColumnTypeName(context, column_type)
|
|
|
|
|
|
2018-02-06 21:41:30 +01:00
|
|
|
|
TYPE_NOTIFICATIONS ->
|
|
|
|
|
context.getString(R.string.notifications) + getNotificationTypeString()
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
else -> getColumnTypeName(context, column_type)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-06 21:41:30 +01:00
|
|
|
|
private fun getNotificationTypeString() : String {
|
2018-08-21 12:19:02 +02:00
|
|
|
|
val sb = StringBuilder()
|
|
|
|
|
sb.append("(")
|
2018-08-24 10:03:20 +02:00
|
|
|
|
|
2018-08-22 01:35:54 +02:00
|
|
|
|
var n = 0
|
2018-08-21 12:19:02 +02:00
|
|
|
|
if(! dont_show_reply) {
|
2018-08-24 10:03:20 +02:00
|
|
|
|
if(n ++ > 0) sb.append(", ")
|
2018-08-21 12:19:02 +02:00
|
|
|
|
sb.append(context.getString(R.string.notification_type_mention))
|
2018-02-06 21:41:30 +01:00
|
|
|
|
}
|
2018-08-21 12:19:02 +02:00
|
|
|
|
if(! dont_show_follow) {
|
2018-08-24 10:03:20 +02:00
|
|
|
|
if(n ++ > 0) sb.append(", ")
|
2018-08-21 12:19:02 +02:00
|
|
|
|
sb.append(context.getString(R.string.notification_type_follow))
|
|
|
|
|
}
|
|
|
|
|
if(! dont_show_boost) {
|
2018-08-24 10:03:20 +02:00
|
|
|
|
if(n ++ > 0) sb.append(", ")
|
2018-08-21 12:19:02 +02:00
|
|
|
|
sb.append(context.getString(R.string.notification_type_boost))
|
|
|
|
|
}
|
|
|
|
|
if(! dont_show_favourite) {
|
2018-08-24 10:03:20 +02:00
|
|
|
|
if(n ++ > 0) sb.append(", ")
|
2018-08-21 12:19:02 +02:00
|
|
|
|
sb.append(context.getString(R.string.notification_type_favourite))
|
|
|
|
|
}
|
2018-08-24 10:03:20 +02:00
|
|
|
|
if(isMisskey && ! dont_show_reaction) {
|
|
|
|
|
if(n ++ > 0) sb.append(", ")
|
2018-08-21 12:19:02 +02:00
|
|
|
|
sb.append(context.getString(R.string.notification_type_reaction))
|
|
|
|
|
}
|
2018-08-24 10:03:20 +02:00
|
|
|
|
if(isMisskey && ! dont_show_vote) {
|
|
|
|
|
if(n ++ > 0) sb.append(", ")
|
2018-08-21 12:19:02 +02:00
|
|
|
|
sb.append(context.getString(R.string.notification_type_vote))
|
|
|
|
|
}
|
2018-08-24 10:03:20 +02:00
|
|
|
|
val n_max = if(isMisskey) {
|
2018-08-22 04:55:55 +02:00
|
|
|
|
6
|
2018-08-24 10:03:20 +02:00
|
|
|
|
} else {
|
2018-08-22 04:55:55 +02:00
|
|
|
|
4
|
|
|
|
|
}
|
2018-08-24 10:03:20 +02:00
|
|
|
|
if(n == 0 || n == n_max) return "" // 全部か皆無なら部分表記は要らない
|
2018-08-21 12:19:02 +02:00
|
|
|
|
sb.append(")")
|
|
|
|
|
return sb.toString()
|
2018-02-06 21:41:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
internal fun dispose() {
|
|
|
|
|
is_dispose.set(true)
|
|
|
|
|
stopStreaming()
|
|
|
|
|
|
|
|
|
|
for(vh in _holder_list) {
|
|
|
|
|
try {
|
|
|
|
|
vh.listView.adapter = null
|
|
|
|
|
} catch(ignored : Throwable) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun getIconAttrId(type : Int) : Int {
|
|
|
|
|
return getIconAttrId(access_info.acct, type)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ブーストやお気に入りの更新に使う。ステータスを列挙する。
|
2018-01-19 10:27:35 +01:00
|
|
|
|
fun findStatus(
|
|
|
|
|
target_instance : String,
|
2018-08-18 12:58:14 +02:00
|
|
|
|
target_status_id : EntityId,
|
2018-01-20 17:59:38 +01:00
|
|
|
|
callback : (account : SavedAccount, status : TootStatus) -> Boolean
|
|
|
|
|
// callback return true if rebind view required
|
2018-01-19 10:27:35 +01:00
|
|
|
|
) {
|
2018-01-20 17:59:38 +01:00
|
|
|
|
if(! access_info.host.equals(target_instance, ignoreCase = true)) return
|
|
|
|
|
|
|
|
|
|
var bChanged = false
|
|
|
|
|
|
|
|
|
|
fun procStatus(status : TootStatus?) {
|
|
|
|
|
if(status != null) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(target_status_id == status.id) {
|
2018-01-20 17:59:38 +01:00
|
|
|
|
if(callback(access_info, status)) bChanged = true
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-01-20 17:59:38 +01:00
|
|
|
|
procStatus(status.reblog)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-01-20 17:59:38 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for(data in list_data) {
|
|
|
|
|
when(data) {
|
|
|
|
|
is TootNotification -> procStatus(data.status)
|
|
|
|
|
is TootStatus -> procStatus(data)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-20 17:59:38 +01:00
|
|
|
|
|
|
|
|
|
if(bChanged) fireRebindAdapterItems()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ミュート、ブロックが成功した時に呼ばれる
|
|
|
|
|
// リストメンバーカラムでメンバーをリストから除去した時に呼ばれる
|
2018-11-01 13:48:54 +01:00
|
|
|
|
fun removeAccountInTimeline(
|
|
|
|
|
target_account : SavedAccount,
|
|
|
|
|
who_id : EntityId,
|
|
|
|
|
removeFromUserList : Boolean = false
|
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(target_account.acct != access_info.acct) return
|
|
|
|
|
|
|
|
|
|
val INVALID_ACCOUNT = - 1L
|
|
|
|
|
|
2018-01-20 07:51:14 +01:00
|
|
|
|
val tmp_list = ArrayList<TimelineItem>(list_data.size)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
for(o in list_data) {
|
|
|
|
|
if(o is TootStatus) {
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(who_id == (o.account.id)) continue
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(who_id == (o.reblog?.account?.id ?: INVALID_ACCOUNT)) continue
|
|
|
|
|
} else if(o is TootNotification) {
|
|
|
|
|
if(who_id == (o.account?.id ?: INVALID_ACCOUNT)) continue
|
|
|
|
|
if(who_id == (o.status?.account?.id ?: INVALID_ACCOUNT)) continue
|
|
|
|
|
if(who_id == (o.status?.reblog?.account?.id ?: INVALID_ACCOUNT)) continue
|
2018-11-01 13:48:54 +01:00
|
|
|
|
} else if(o is TootAccountRef && removeFromUserList) {
|
2018-05-18 19:08:46 +02:00
|
|
|
|
if(who_id == o.get().id) continue
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp_list.add(o)
|
|
|
|
|
}
|
|
|
|
|
if(tmp_list.size != list_data.size) {
|
|
|
|
|
list_data.clear()
|
|
|
|
|
list_data.addAll(tmp_list)
|
2018-01-20 17:59:38 +01:00
|
|
|
|
fireShowContent(reason = "removeAccountInTimeline")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-01 13:48:54 +01:00
|
|
|
|
// misskeyカラムやプロフカラムでブロック成功した時に呼ばれる
|
|
|
|
|
fun updateFollowIcons(target_account : SavedAccount) {
|
|
|
|
|
if(target_account.acct != access_info.acct) return
|
|
|
|
|
|
|
|
|
|
fireShowContent(reason = "updateFollowIcons", reset = true)
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
fun removeUser(targetAccount : SavedAccount, columnType : Int, who_id : EntityId) {
|
2018-07-16 20:19:19 +02:00
|
|
|
|
if(column_type == columnType && targetAccount.acct == access_info.acct) {
|
2018-01-20 07:51:14 +01:00
|
|
|
|
val tmp_list = ArrayList<TimelineItem>(list_data.size)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
for(o in list_data) {
|
2018-05-08 10:25:02 +02:00
|
|
|
|
if(o is TootAccountRef) {
|
2018-05-18 19:08:46 +02:00
|
|
|
|
if(o.get().id == who_id) continue
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
tmp_list.add(o)
|
|
|
|
|
}
|
|
|
|
|
if(tmp_list.size != list_data.size) {
|
|
|
|
|
list_data.clear()
|
|
|
|
|
list_data.addAll(tmp_list)
|
2018-07-16 20:19:19 +02:00
|
|
|
|
fireShowContent(reason = "removeUser")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-21 06:18:19 +01:00
|
|
|
|
// ステータスが削除された時に呼ばれる
|
2018-08-18 12:58:14 +02:00
|
|
|
|
fun onStatusRemoved(tl_host : String, status_id : EntityId) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-03-25 15:27:58 +02:00
|
|
|
|
if(is_dispose.get() || bInitialLoading || bRefreshLoading) return
|
|
|
|
|
|
2018-03-21 06:18:19 +01:00
|
|
|
|
if(tl_host.equals(access_info.host, ignoreCase = true)) {
|
|
|
|
|
val tmp_list = ArrayList<TimelineItem>(list_data.size)
|
|
|
|
|
for(o in list_data) {
|
|
|
|
|
if(o is TootStatus) {
|
|
|
|
|
if(status_id == o.id) continue
|
|
|
|
|
if(status_id == (o.reblog?.id ?: - 1L)) continue
|
2018-03-25 15:27:58 +02:00
|
|
|
|
} else if(o is TootNotification) {
|
2018-03-21 06:18:19 +01:00
|
|
|
|
val s = o.status
|
|
|
|
|
if(s != null) {
|
|
|
|
|
if(status_id == s.id) continue
|
|
|
|
|
if(status_id == (s.reblog?.id ?: - 1L)) continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp_list.add(o)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-03-21 06:18:19 +01:00
|
|
|
|
if(tmp_list.size != list_data.size) {
|
|
|
|
|
list_data.clear()
|
|
|
|
|
list_data.addAll(tmp_list)
|
|
|
|
|
fireShowContent(reason = "removeStatus")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2018-03-21 06:18:19 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun removeNotifications() {
|
|
|
|
|
cancelLastTask()
|
|
|
|
|
|
2018-12-01 00:02:18 +01:00
|
|
|
|
mRefreshLoadingErrorPopupState = 0
|
2018-01-04 19:52:25 +01:00
|
|
|
|
mRefreshLoadingError = ""
|
|
|
|
|
bRefreshLoading = false
|
|
|
|
|
mInitialLoadingError = ""
|
|
|
|
|
bInitialLoading = false
|
2018-08-18 12:58:14 +02:00
|
|
|
|
idOld = null
|
|
|
|
|
idRecent = null
|
2018-08-25 02:29:24 +02:00
|
|
|
|
offsetNext = 0
|
|
|
|
|
pagingType = PagingType.Default
|
2018-01-20 17:59:38 +01:00
|
|
|
|
|
2018-01-20 07:51:14 +01:00
|
|
|
|
list_data.clear()
|
|
|
|
|
duplicate_map.clear()
|
2018-01-20 17:59:38 +01:00
|
|
|
|
fireShowContent(reason = "removeNotifications", reset = true)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
PollingWorker.queueNotificationCleared(context, access_info.db_id)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun removeNotificationOne(target_account : SavedAccount, notification : TootNotification) {
|
|
|
|
|
if(column_type != TYPE_NOTIFICATIONS) return
|
|
|
|
|
if(access_info.acct != target_account.acct) return
|
|
|
|
|
|
2018-01-20 07:51:14 +01:00
|
|
|
|
val tmp_list = ArrayList<TimelineItem>(list_data.size)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
for(o in list_data) {
|
|
|
|
|
if(o is TootNotification) {
|
|
|
|
|
if(o.id == notification.id) continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tmp_list.add(o)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(tmp_list.size != list_data.size) {
|
|
|
|
|
list_data.clear()
|
|
|
|
|
list_data.addAll(tmp_list)
|
2018-01-20 17:59:38 +01:00
|
|
|
|
fireShowContent(reason = "removeNotificationOne")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-17 20:06:15 +02:00
|
|
|
|
fun onMuteUpdated() {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-09-17 20:06:15 +02:00
|
|
|
|
val checker = { status : TootStatus? -> status?.checkMuted() ?: false }
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-09-17 20:06:15 +02:00
|
|
|
|
val tmp_list = ArrayList<TimelineItem>(list_data.size)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
for(o in list_data) {
|
|
|
|
|
if(o is TootStatus) {
|
|
|
|
|
if(checker(o)) continue
|
|
|
|
|
}
|
|
|
|
|
if(o is TootNotification) {
|
|
|
|
|
if(checker(o.status)) continue
|
|
|
|
|
}
|
|
|
|
|
tmp_list.add(o)
|
|
|
|
|
}
|
|
|
|
|
if(tmp_list.size != list_data.size) {
|
|
|
|
|
list_data.clear()
|
|
|
|
|
list_data.addAll(tmp_list)
|
2018-09-17 20:06:15 +02:00
|
|
|
|
fireShowContent(reason = "onMuteUpdated")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-15 17:23:43 +01:00
|
|
|
|
fun onHideFavouriteNotification(acct : String) {
|
2018-03-21 06:18:19 +01:00
|
|
|
|
if(column_type != TYPE_NOTIFICATIONS) return
|
2018-03-15 17:23:43 +01:00
|
|
|
|
|
|
|
|
|
val tmp_list = ArrayList<TimelineItem>(list_data.size)
|
|
|
|
|
|
|
|
|
|
for(o in list_data) {
|
2018-03-21 06:18:19 +01:00
|
|
|
|
if(o is TootNotification && o.type != TootNotification.TYPE_MENTION) {
|
2018-03-15 17:23:43 +01:00
|
|
|
|
val a = o.account
|
2018-03-21 06:18:19 +01:00
|
|
|
|
if(a != null) {
|
2018-03-15 17:23:43 +01:00
|
|
|
|
val a_acct = access_info.getFullAcct(a)
|
2018-03-21 06:18:19 +01:00
|
|
|
|
if(a_acct == acct) continue
|
2018-03-15 17:23:43 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
tmp_list.add(o)
|
|
|
|
|
}
|
|
|
|
|
if(tmp_list.size != list_data.size) {
|
|
|
|
|
list_data.clear()
|
|
|
|
|
list_data.addAll(tmp_list)
|
|
|
|
|
fireShowContent(reason = "onHideFavouriteNotification")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
fun onDomainBlockChanged(target_account : SavedAccount, domain : String, bBlocked : Boolean) {
|
|
|
|
|
if(target_account.host != access_info.host) return
|
|
|
|
|
if(access_info.isPseudo) return
|
|
|
|
|
|
|
|
|
|
if(column_type == TYPE_DOMAIN_BLOCKS) {
|
|
|
|
|
// ドメインブロック一覧を読み直す
|
|
|
|
|
startLoading()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(bBlocked) {
|
|
|
|
|
// ブロックしたのとドメイン部分が一致するアカウントからのステータスと通知をすべて除去する
|
|
|
|
|
val reDomain = Pattern.compile("[^@]+@\\Q$domain\\E\\z", Pattern.CASE_INSENSITIVE)
|
2018-01-19 10:27:35 +01:00
|
|
|
|
val checker =
|
|
|
|
|
{ acct : String? -> if(acct == null) false else reDomain.matcher(acct).find() }
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-20 07:51:14 +01:00
|
|
|
|
val tmp_list = ArrayList<TimelineItem>(list_data.size)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
for(o in list_data) {
|
|
|
|
|
if(o is TootStatus) {
|
|
|
|
|
if(checker(o.account.acct)) continue
|
|
|
|
|
if(checker(o.reblog?.account?.acct)) continue
|
|
|
|
|
} else if(o is TootNotification) {
|
|
|
|
|
if(checker(o.account?.acct)) continue
|
|
|
|
|
if(checker(o.status?.account?.acct)) continue
|
|
|
|
|
if(checker(o.status?.reblog?.account?.acct)) continue
|
|
|
|
|
}
|
|
|
|
|
tmp_list.add(o)
|
|
|
|
|
}
|
|
|
|
|
if(tmp_list.size != list_data.size) {
|
|
|
|
|
list_data.clear()
|
|
|
|
|
list_data.addAll(tmp_list)
|
2018-01-20 17:59:38 +01:00
|
|
|
|
fireShowContent(reason = "onDomainBlockChanged")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun onListListUpdated(account : SavedAccount) {
|
|
|
|
|
if(column_type == TYPE_LIST_LIST && access_info.acct == account.acct) {
|
|
|
|
|
startLoading()
|
|
|
|
|
val vh = viewHolder
|
|
|
|
|
vh?.onListListUpdated()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-04 18:55:42 +01:00
|
|
|
|
fun onListNameUpdated(account : SavedAccount, item : TootList) {
|
|
|
|
|
if(access_info.acct != account.acct) return
|
|
|
|
|
when(column_type) {
|
|
|
|
|
TYPE_LIST_LIST -> {
|
|
|
|
|
startLoading()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TYPE_LIST_TL, TYPE_LIST_MEMBER -> {
|
2018-03-21 06:18:19 +01:00
|
|
|
|
if(item.id == profile_id) {
|
2018-03-11 04:53:34 +01:00
|
|
|
|
this.list_info = item
|
|
|
|
|
fireShowColumnHeader()
|
|
|
|
|
}
|
2018-02-04 18:55:42 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
fun onListMemberUpdated(
|
|
|
|
|
account : SavedAccount,
|
2018-08-18 12:58:14 +02:00
|
|
|
|
list_id : EntityId,
|
2018-01-19 10:27:35 +01:00
|
|
|
|
who : TootAccount,
|
|
|
|
|
bAdd : Boolean
|
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(column_type == TYPE_LIST_TL && access_info.acct == account.acct && list_id == profile_id) {
|
|
|
|
|
if(! bAdd) {
|
|
|
|
|
removeAccountInTimeline(account, who.id)
|
|
|
|
|
}
|
|
|
|
|
} else if(column_type == TYPE_LIST_MEMBER && access_info.acct == account.acct && list_id == profile_id) {
|
|
|
|
|
if(! bAdd) {
|
|
|
|
|
removeAccountInTimeline(account, who.id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun addColumnViewHolder(cvh : ColumnViewHolder) {
|
|
|
|
|
|
|
|
|
|
// 現在のリストにあるなら削除する
|
|
|
|
|
removeColumnViewHolder(cvh)
|
|
|
|
|
|
|
|
|
|
// 最後に追加されたものが先頭にくるようにする
|
|
|
|
|
// 呼び出しの後に必ず追加されているようにする
|
|
|
|
|
_holder_list.addFirst(cvh)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun removeColumnViewHolder(cvh : ColumnViewHolder) {
|
|
|
|
|
val it = _holder_list.iterator()
|
|
|
|
|
while(it.hasNext()) {
|
|
|
|
|
if(cvh == it.next()) it.remove()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun removeColumnViewHolderByActivity(activity : ActMain) {
|
|
|
|
|
val it = _holder_list.iterator()
|
|
|
|
|
while(it.hasNext()) {
|
|
|
|
|
val cvh = it.next()
|
|
|
|
|
if(cvh.activity == activity) {
|
|
|
|
|
it.remove()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun hasMultipleViewHolder() : Boolean {
|
|
|
|
|
return _holder_list.size > 1
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-20 07:51:14 +01:00
|
|
|
|
internal fun fireShowContent(
|
2018-01-20 17:59:38 +01:00
|
|
|
|
reason : String,
|
2018-01-20 07:51:14 +01:00
|
|
|
|
changeList : List<AdapterChange>? = null,
|
|
|
|
|
reset : Boolean = false
|
|
|
|
|
) {
|
2018-01-21 13:46:36 +01:00
|
|
|
|
if(! isMainThread) {
|
2018-01-20 07:51:14 +01:00
|
|
|
|
throw RuntimeException("fireShowContent: not on main thread.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-01-20 17:59:38 +01:00
|
|
|
|
viewHolder?.showContent(reason, changeList, reset)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun fireShowColumnHeader() {
|
2018-01-21 13:46:36 +01:00
|
|
|
|
if(! isMainThread) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
throw RuntimeException("fireShowColumnHeader: not on main thread.")
|
|
|
|
|
}
|
2018-01-20 07:51:14 +01:00
|
|
|
|
viewHolder?.showColumnHeader()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 15:22:21 +02:00
|
|
|
|
internal fun fireShowColumnStatus() {
|
|
|
|
|
if(! isMainThread) {
|
|
|
|
|
throw RuntimeException("fireShowColumnStatus: not on main thread.")
|
|
|
|
|
}
|
|
|
|
|
viewHolder?.showColumnStatus()
|
2018-11-18 03:38:52 +01:00
|
|
|
|
|
2018-04-20 15:22:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
internal fun fireColumnColor() {
|
2018-01-21 13:46:36 +01:00
|
|
|
|
if(! isMainThread) {
|
2018-01-20 07:51:14 +01:00
|
|
|
|
throw RuntimeException("fireColumnColor: not on main thread.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-01-20 07:51:14 +01:00
|
|
|
|
viewHolder?.showColumnColor()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-20 07:51:14 +01:00
|
|
|
|
fun fireRelativeTime() {
|
2018-01-21 13:46:36 +01:00
|
|
|
|
if(! isMainThread) {
|
2018-01-20 07:51:14 +01:00
|
|
|
|
throw RuntimeException("fireRelativeTime: not on main thread.")
|
|
|
|
|
}
|
|
|
|
|
viewHolder?.updateRelativeTime()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun fireRebindAdapterItems() {
|
2018-01-21 13:46:36 +01:00
|
|
|
|
if(! isMainThread) {
|
2018-01-20 07:51:14 +01:00
|
|
|
|
throw RuntimeException("fireRelativeTime: not on main thread.")
|
|
|
|
|
}
|
|
|
|
|
viewHolder?.rebindAdapterItems()
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
private fun cancelLastTask() {
|
2018-04-21 01:16:44 +02:00
|
|
|
|
if(lastTask != null) {
|
|
|
|
|
lastTask?.cancel(true)
|
|
|
|
|
lastTask = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
//
|
|
|
|
|
bInitialLoading = false
|
|
|
|
|
bRefreshLoading = false
|
|
|
|
|
mInitialLoadingError = context.getString(R.string.cancelled)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun initFilter() {
|
|
|
|
|
column_regex_filter = COLUMN_REGEX_FILTER_DEFAULT
|
|
|
|
|
val regex_text = this.regex_text
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(regex_text.isNotEmpty()) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
try {
|
|
|
|
|
val re = Pattern.compile(regex_text)
|
2018-01-19 10:27:35 +01:00
|
|
|
|
column_regex_filter =
|
2018-07-03 06:37:07 +02:00
|
|
|
|
{ text : CharSequence? ->
|
|
|
|
|
if(text?.isEmpty() != false) false else re.matcher(
|
|
|
|
|
text
|
|
|
|
|
).find()
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-15 17:23:43 +01:00
|
|
|
|
favMuteSet = FavMute.acctSet
|
2018-01-04 19:52:25 +01:00
|
|
|
|
highlight_trie = HighlightWord.nameSet
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun isFilteredByAttachment(status : TootStatus) : Boolean {
|
|
|
|
|
// オプションがどれも設定されていないならフィルタしない(false)
|
|
|
|
|
if(! (with_attachment || with_highlight)) return false
|
|
|
|
|
|
|
|
|
|
val matchMedia = with_attachment && status.reblog?.hasMedia() ?: status.hasMedia()
|
|
|
|
|
val matchHighlight = with_highlight && status.reblog?.hasHighlight ?: status.hasHighlight
|
|
|
|
|
|
|
|
|
|
// どれかの条件を満たすならフィルタしない(false)、どれも満たさないならフィルタする(true)
|
|
|
|
|
return ! (matchMedia || matchHighlight)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun isFiltered(status : TootStatus) : Boolean {
|
|
|
|
|
|
2018-07-06 17:22:22 +02:00
|
|
|
|
// word mute2
|
|
|
|
|
status.updateFiltered(muted_word2)
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(isFilteredByAttachment(status)) return true
|
|
|
|
|
|
|
|
|
|
if(dont_show_boost) {
|
|
|
|
|
if(status.reblog != null) return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dont_show_reply) {
|
2018-08-20 02:07:55 +02:00
|
|
|
|
if(status.in_reply_to_id != null) return true
|
|
|
|
|
if(status.reblog?.in_reply_to_id != null) return true
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-10 01:36:27 +01:00
|
|
|
|
if(dont_show_normal_toot) {
|
2018-08-20 02:07:55 +02:00
|
|
|
|
if(status.in_reply_to_id == null && status.reblog == null) return true
|
2018-02-10 01:36:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(column_regex_filter(status.decoded_content)) return true
|
|
|
|
|
if(column_regex_filter(status.reblog?.decoded_content)) return true
|
2018-05-18 23:58:33 +02:00
|
|
|
|
if(column_regex_filter(status.decoded_spoiler_text)) return true
|
|
|
|
|
if(column_regex_filter(status.reblog?.decoded_spoiler_text)) return true
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-09-17 20:06:15 +02:00
|
|
|
|
return status.checkMuted()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-20 07:51:14 +01:00
|
|
|
|
private inline fun <reified T : TimelineItem> addAll(
|
|
|
|
|
dstArg : ArrayList<TimelineItem>?,
|
2018-08-18 12:58:14 +02:00
|
|
|
|
src : List<T>
|
2018-01-20 07:51:14 +01:00
|
|
|
|
) : ArrayList<TimelineItem> {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val dst = dstArg ?: ArrayList(src.size)
|
|
|
|
|
dst.addAll(src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
return dst
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-20 07:51:14 +01:00
|
|
|
|
private fun addOne(
|
|
|
|
|
dstArg : ArrayList<TimelineItem>?,
|
2018-08-18 12:58:14 +02:00
|
|
|
|
item : TimelineItem?
|
2018-01-20 07:51:14 +01:00
|
|
|
|
) : ArrayList<TimelineItem> {
|
2018-01-10 16:47:35 +01:00
|
|
|
|
val dst = dstArg ?: ArrayList()
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(item != null) dst.add(item)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
return dst
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
private fun addOneFirst(
|
2018-01-20 07:51:14 +01:00
|
|
|
|
dstArg : ArrayList<TimelineItem>?,
|
2018-08-18 12:58:14 +02:00
|
|
|
|
item : TimelineItem?
|
2018-01-20 07:51:14 +01:00
|
|
|
|
) : ArrayList<TimelineItem> {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val dst = dstArg ?: ArrayList()
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(item != null) dst.add(0, item)
|
|
|
|
|
return dst
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun addWithFilterStatus(
|
|
|
|
|
dstArg : ArrayList<TimelineItem>?,
|
|
|
|
|
src : List<TootStatus>
|
|
|
|
|
) : ArrayList<TimelineItem> {
|
|
|
|
|
val dst = dstArg ?: ArrayList(src.size)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
for(status in src) {
|
|
|
|
|
if(! isFiltered(status)) {
|
|
|
|
|
dst.add(status)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return dst
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
private fun addWithFilterConversationSummary(
|
|
|
|
|
dstArg : ArrayList<TimelineItem>?,
|
|
|
|
|
src : List<TootConversationSummary>
|
|
|
|
|
) : ArrayList<TimelineItem> {
|
|
|
|
|
val dst = dstArg ?: ArrayList(src.size)
|
|
|
|
|
for(cs in src) {
|
|
|
|
|
if(! isFiltered(cs.last_status)) {
|
|
|
|
|
dst.add(cs)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return dst
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
private fun addWithFilterNotification(
|
2018-01-20 07:51:14 +01:00
|
|
|
|
dstArg : ArrayList<TimelineItem>?,
|
2018-08-18 12:58:14 +02:00
|
|
|
|
src : List<TootNotification>
|
2018-01-20 07:51:14 +01:00
|
|
|
|
) : ArrayList<TimelineItem> {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val dst = dstArg ?: ArrayList(src.size)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
for(item in src) {
|
|
|
|
|
if(! isFiltered(item)) dst.add(item)
|
|
|
|
|
}
|
|
|
|
|
return dst
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun isFiltered(item : TootNotification) : Boolean {
|
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
when(item.type) {
|
|
|
|
|
TootNotification.TYPE_FAVOURITE -> if(dont_show_favourite) {
|
2018-08-21 12:19:02 +02:00
|
|
|
|
log.d("isFiltered: favourite notification filtered.")
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
TootNotification.TYPE_REBLOG,
|
|
|
|
|
TootNotification.TYPE_RENOTE,
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TootNotification.TYPE_QUOTE -> if(dont_show_boost) {
|
2018-08-21 12:19:02 +02:00
|
|
|
|
log.d("isFiltered: reblog notification filtered.")
|
|
|
|
|
return true
|
|
|
|
|
}
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TootNotification.TYPE_FOLLOW -> if(dont_show_follow) {
|
2018-08-21 12:19:02 +02:00
|
|
|
|
log.d("isFiltered: follow notification filtered.")
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
TootNotification.TYPE_MENTION,
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TootNotification.TYPE_REPLY -> if(dont_show_reply) {
|
2018-08-21 12:19:02 +02:00
|
|
|
|
log.d("isFiltered: mention notification filtered.")
|
|
|
|
|
return true
|
|
|
|
|
}
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TootNotification.TYPE_REACTION -> if(dont_show_reaction) {
|
2018-08-21 12:19:02 +02:00
|
|
|
|
log.d("isFiltered: reaction notification filtered.")
|
|
|
|
|
return true
|
|
|
|
|
}
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TootNotification.TYPE_VOTE -> if(dont_show_vote) {
|
2018-08-21 12:19:02 +02:00
|
|
|
|
log.d("isFiltered: vote notification filtered.")
|
|
|
|
|
return true
|
|
|
|
|
}
|
2018-02-06 21:41:30 +01:00
|
|
|
|
}
|
2018-08-24 10:03:20 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val status = item.status
|
2018-09-17 20:06:15 +02:00
|
|
|
|
|
|
|
|
|
status?.updateFiltered(muted_word2)
|
|
|
|
|
|
|
|
|
|
if(status?.checkMuted() == true) {
|
|
|
|
|
log.d("isFiltered: status muted.")
|
|
|
|
|
return true
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-03-15 17:23:43 +01:00
|
|
|
|
|
|
|
|
|
// ふぁぼ魔ミュート
|
2018-03-21 06:18:19 +01:00
|
|
|
|
when(item.type) {
|
2018-08-21 12:19:02 +02:00
|
|
|
|
TootNotification.TYPE_REBLOG,
|
|
|
|
|
TootNotification.TYPE_RENOTE,
|
|
|
|
|
TootNotification.TYPE_QUOTE,
|
|
|
|
|
TootNotification.TYPE_FAVOURITE,
|
|
|
|
|
TootNotification.TYPE_REACTION,
|
|
|
|
|
TootNotification.TYPE_VOTE,
|
|
|
|
|
TootNotification.TYPE_FOLLOW -> {
|
2018-03-15 17:23:43 +01:00
|
|
|
|
val who = item.account
|
2018-03-21 06:18:19 +01:00
|
|
|
|
if(who != null && favMuteSet?.contains(access_info.getFullAcct(who)) == true) {
|
|
|
|
|
PollingWorker.log.d("%s is in favMuteSet.", access_info.getFullAcct(who))
|
2018-03-15 17:23:43 +01:00
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// @Nullable String parseMaxId( TootApiResult result ){
|
|
|
|
|
// if( result != null && result.link_older != null ){
|
|
|
|
|
// Matcher m = reMaxId.matcher( result.link_older );
|
2018-05-18 19:08:46 +02:00
|
|
|
|
// if( m.get() ) return m.group( 1 );
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// }
|
|
|
|
|
// return null;
|
|
|
|
|
// }
|
|
|
|
|
|
2018-05-23 03:57:34 +02:00
|
|
|
|
internal fun loadProfileAccount(
|
|
|
|
|
client : TootApiClient,
|
2018-10-30 20:29:00 +01:00
|
|
|
|
parser : TootParser,
|
2018-05-23 03:57:34 +02:00
|
|
|
|
bForceReload : Boolean
|
|
|
|
|
) : TootApiResult? {
|
2018-10-30 20:29:00 +01:00
|
|
|
|
|
|
|
|
|
return if(this.who_account != null && ! bForceReload) {
|
|
|
|
|
// リロード不要なら何もしない
|
|
|
|
|
null
|
|
|
|
|
} else if(isMisskey) {
|
|
|
|
|
val params = access_info.putMisskeyApiToken(JSONObject())
|
|
|
|
|
.put("userId", profile_id)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-10-30 20:29:00 +01:00
|
|
|
|
val result = client.request(PATH_MISSKEY_PROFILE, params.toPostRequestBuilder())
|
|
|
|
|
|
|
|
|
|
// ユーザリレーションの取り扱いのため、別のparserを作ってはいけない
|
|
|
|
|
parser.misskeyDecodeProfilePin = true
|
|
|
|
|
try {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val a = TootAccountRef.mayNull(parser, parser.account(result?.jsonObject))
|
|
|
|
|
if(a != null) {
|
|
|
|
|
this.who_account = a
|
|
|
|
|
client.publishApiProgress("") // カラムヘッダの再表示
|
|
|
|
|
}
|
2018-10-30 20:29:00 +01:00
|
|
|
|
} finally {
|
|
|
|
|
parser.misskeyDecodeProfilePin = false
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-10-30 20:29:00 +01:00
|
|
|
|
result
|
|
|
|
|
|
2018-05-23 03:57:34 +02:00
|
|
|
|
} else {
|
2018-10-30 20:29:00 +01:00
|
|
|
|
val result = client.request(String.format(Locale.JAPAN, PATH_ACCOUNT, profile_id))
|
|
|
|
|
val a = TootAccountRef.mayNull(parser, parser.account(result?.jsonObject))
|
|
|
|
|
if(a != null) {
|
|
|
|
|
this.who_account = a
|
|
|
|
|
client.publishApiProgress("") // カラムヘッダの再表示
|
|
|
|
|
}
|
|
|
|
|
result
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-10-30 20:29:00 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun loadListInfo(client : TootApiClient, bForceReload : Boolean) {
|
2018-10-01 02:10:16 +02:00
|
|
|
|
val parser = TootParser(context, access_info)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(bForceReload || this.list_info == null) {
|
2018-10-01 02:10:16 +02:00
|
|
|
|
val result = if(isMisskey) {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
val params = makeMisskeyBaseParameter(parser)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
.put("listId", profile_id)
|
|
|
|
|
client.request("/api/users/lists/show", params.toPostRequestBuilder())
|
|
|
|
|
} else {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
client.request(String.format(Locale.JAPAN, PATH_LIST_INFO, profile_id))
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val jsonObject = result?.jsonObject
|
|
|
|
|
if(jsonObject != null) {
|
2018-10-01 02:10:16 +02:00
|
|
|
|
val data = parseItem(::TootList, parser, jsonObject)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(data != null) {
|
|
|
|
|
this.list_info = data
|
|
|
|
|
client.publishApiProgress("") // カラムヘッダの再表示
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private inner class UpdateRelationEnv {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
internal val who_set = HashSet<EntityId>()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
internal val acct_set = HashSet<String>()
|
|
|
|
|
internal val tag_set = HashSet<String>()
|
|
|
|
|
|
2018-05-08 10:25:02 +02:00
|
|
|
|
internal fun add(whoRef : TootAccountRef?) {
|
2018-05-18 19:08:46 +02:00
|
|
|
|
add(whoRef?.get())
|
2018-05-08 10:25:02 +02:00
|
|
|
|
}
|
2018-05-18 19:08:46 +02:00
|
|
|
|
|
2018-05-08 10:25:02 +02:00
|
|
|
|
internal fun add(who : TootAccount?) {
|
2018-05-18 19:08:46 +02:00
|
|
|
|
who ?: return
|
2018-05-08 10:25:02 +02:00
|
|
|
|
who_set.add(who.id)
|
|
|
|
|
acct_set.add("@" + access_info.getFullAcct(who))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
//
|
2018-05-08 10:25:02 +02:00
|
|
|
|
add(who.movedRef)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-05-18 19:08:46 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
internal fun add(s : TootStatus?) {
|
|
|
|
|
if(s == null) return
|
2018-05-08 10:25:02 +02:00
|
|
|
|
add(s.accountRef)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
add(s.reblog)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
s.tags?.forEach { tag_set.add(it.name) }
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun add(n : TootNotification?) {
|
|
|
|
|
if(n == null) return
|
2018-05-08 10:25:02 +02:00
|
|
|
|
add(n.accountRef)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
add(n.status)
|
|
|
|
|
}
|
2018-11-03 17:14:14 +01:00
|
|
|
|
|
2018-08-21 12:19:02 +02:00
|
|
|
|
internal fun update(client : TootApiClient, parser : TootParser) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
var n : Int
|
|
|
|
|
var size : Int
|
|
|
|
|
|
2018-08-21 12:19:02 +02:00
|
|
|
|
if(isMisskey) {
|
2018-11-01 13:48:54 +01:00
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
// parser内部にアカウントIDとRelationのマップが生成されるので、それをデータベースに記録する
|
2018-11-01 13:48:54 +01:00
|
|
|
|
run {
|
|
|
|
|
val now = System.currentTimeMillis()
|
|
|
|
|
val who_list = parser.misskeyUserRelationMap.entries.toMutableList()
|
|
|
|
|
var start = 0
|
|
|
|
|
val end = who_list.size
|
|
|
|
|
while(start < end) {
|
|
|
|
|
var step = end - start
|
|
|
|
|
if(step > RELATIONSHIP_LOAD_STEP) step = RELATIONSHIP_LOAD_STEP
|
|
|
|
|
UserRelationMisskey.saveList(now, access_info.db_id, who_list, start, step)
|
|
|
|
|
start += step
|
|
|
|
|
}
|
|
|
|
|
log.d("updateRelation: update %d relations.", end)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2018/11/1 Misskeyにもリレーション取得APIができた
|
|
|
|
|
// アカウントIDの集合からRelationshipを取得してデータベースに記録する
|
|
|
|
|
|
|
|
|
|
size = who_set.size
|
|
|
|
|
if(size > 0) {
|
|
|
|
|
val who_list = ArrayList<EntityId>(size)
|
|
|
|
|
who_list.addAll(who_set)
|
|
|
|
|
|
|
|
|
|
val now = System.currentTimeMillis()
|
|
|
|
|
|
|
|
|
|
n = 0
|
|
|
|
|
while(n < who_list.size) {
|
|
|
|
|
val userIdList = ArrayList<EntityId>(RELATIONSHIP_LOAD_STEP)
|
|
|
|
|
for(i in 0 until RELATIONSHIP_LOAD_STEP) {
|
|
|
|
|
if(n >= size) break
|
|
|
|
|
if(! parser.misskeyUserRelationMap.containsKey(who_list[n])) {
|
|
|
|
|
userIdList.add(who_list[n])
|
|
|
|
|
}
|
|
|
|
|
++ n
|
|
|
|
|
}
|
|
|
|
|
if(userIdList.isEmpty()) continue
|
|
|
|
|
|
|
|
|
|
val params = access_info.putMisskeyApiToken()
|
|
|
|
|
.put("userId", JSONArray().apply {
|
|
|
|
|
for(id in userIdList) put(id.toString())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
val result =
|
|
|
|
|
client.request("/api/users/relation", params.toPostRequestBuilder())
|
2018-11-03 17:14:14 +01:00
|
|
|
|
|
|
|
|
|
if(result == null || result.response?.code() in 400 until 500) break
|
2018-11-02 08:55:54 +01:00
|
|
|
|
|
2018-11-01 13:48:54 +01:00
|
|
|
|
val list = parseList(::TootRelationShip, parser, result.jsonArray)
|
|
|
|
|
if(list.size == userIdList.size) {
|
|
|
|
|
for(i in 0 until list.size) {
|
|
|
|
|
list[i].id = userIdList[i]
|
|
|
|
|
}
|
|
|
|
|
UserRelationMisskey.saveList2(now, access_info.db_id, list)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
log.d("updateRelation: update %d relations.", n)
|
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
}
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
2018-08-21 12:19:02 +02:00
|
|
|
|
} else {
|
2018-08-20 02:07:55 +02:00
|
|
|
|
// アカウントIDの集合からRelationshipを取得してデータベースに記録する
|
|
|
|
|
size = who_set.size
|
2018-08-21 12:19:02 +02:00
|
|
|
|
if(size > 0) {
|
2018-08-20 02:07:55 +02:00
|
|
|
|
val who_list = ArrayList<EntityId>(size)
|
|
|
|
|
who_list.addAll(who_set)
|
|
|
|
|
|
|
|
|
|
val now = System.currentTimeMillis()
|
|
|
|
|
|
|
|
|
|
n = 0
|
|
|
|
|
while(n < who_list.size) {
|
|
|
|
|
val sb = StringBuilder()
|
|
|
|
|
sb.append("/api/v1/accounts/relationships")
|
|
|
|
|
for(i in 0 until RELATIONSHIP_LOAD_STEP) {
|
|
|
|
|
if(n >= size) break
|
|
|
|
|
sb.append(if(i == 0) '?' else '&')
|
|
|
|
|
sb.append("id[]=")
|
|
|
|
|
sb.append(who_list[n ++].toString())
|
|
|
|
|
}
|
|
|
|
|
val result = client.request(sb.toString()) ?: break // cancelled.
|
2018-11-01 13:48:54 +01:00
|
|
|
|
val list = parseList(::TootRelationShip, parser, result.jsonArray)
|
2018-08-20 02:07:55 +02:00
|
|
|
|
if(list.size > 0) UserRelation.saveList(now, access_info.db_id, list)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-20 02:07:55 +02:00
|
|
|
|
log.d("updateRelation: update %d relations.", n)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 出現したacctをデータベースに記録する
|
|
|
|
|
size = acct_set.size
|
|
|
|
|
if(size > 0) {
|
|
|
|
|
val acct_list = ArrayList<String?>(size)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
acct_list.addAll(acct_set)
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val now = System.currentTimeMillis()
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
|
|
|
|
n = 0
|
|
|
|
|
while(n < acct_list.size) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
var length = size - n
|
|
|
|
|
if(length > ACCT_DB_STEP) length = ACCT_DB_STEP
|
|
|
|
|
AcctSet.saveList(now, acct_list, n, length)
|
|
|
|
|
n += length
|
|
|
|
|
}
|
|
|
|
|
log.d("updateRelation: update %d acct.", n)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 出現したタグをデータベースに記録する
|
|
|
|
|
size = tag_set.size
|
|
|
|
|
if(size > 0) {
|
|
|
|
|
val tag_list = ArrayList<String?>(size)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
tag_list.addAll(tag_set)
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val now = System.currentTimeMillis()
|
2018-01-10 16:47:35 +01:00
|
|
|
|
|
|
|
|
|
n = 0
|
|
|
|
|
while(n < tag_list.size) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
var length = size - n
|
|
|
|
|
if(length > ACCT_DB_STEP) length = ACCT_DB_STEP
|
|
|
|
|
TagSet.saveList(now, tag_list, n, length)
|
|
|
|
|
n += length
|
|
|
|
|
}
|
|
|
|
|
log.d("updateRelation: update %d tag.", n)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
private fun updateRelation(
|
|
|
|
|
client : TootApiClient,
|
2018-01-20 07:51:14 +01:00
|
|
|
|
list : ArrayList<TimelineItem>?,
|
2018-08-20 02:07:55 +02:00
|
|
|
|
whoRef : TootAccountRef?,
|
2018-08-21 12:19:02 +02:00
|
|
|
|
parser : TootParser
|
2018-01-04 19:52:25 +01:00
|
|
|
|
) {
|
|
|
|
|
if(access_info.isPseudo) return
|
|
|
|
|
|
|
|
|
|
val env = UpdateRelationEnv()
|
|
|
|
|
|
2018-05-08 10:25:02 +02:00
|
|
|
|
env.add(whoRef)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
list?.forEach {
|
|
|
|
|
when(it) {
|
2018-05-08 10:25:02 +02:00
|
|
|
|
is TootAccountRef -> env.add(it)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
is TootStatus -> env.add(it)
|
|
|
|
|
is TootNotification -> env.add(it)
|
2018-11-01 13:48:54 +01:00
|
|
|
|
is TootConversationSummary -> env.add(it.last_status)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-21 12:19:02 +02:00
|
|
|
|
env.update(client, parser)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-10 22:43:10 +02:00
|
|
|
|
// DMカラム更新時に新APIの利用に成功したなら真
|
|
|
|
|
internal var useConversationSummarys = false
|
2018-10-30 20:29:00 +01:00
|
|
|
|
|
2018-10-10 22:43:10 +02:00
|
|
|
|
// DMカラムのストリーミングイベントで新形式のイベントを利用できたなら真
|
|
|
|
|
internal var useConversationSummaryStreaming = false
|
2018-10-09 01:20:43 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
internal fun startLoading() {
|
|
|
|
|
cancelLastTask()
|
|
|
|
|
|
|
|
|
|
stopStreaming()
|
|
|
|
|
|
|
|
|
|
initFilter()
|
2018-12-26 05:47:16 +01:00
|
|
|
|
|
|
|
|
|
useInstanceTicker = Pref.bpInstanceTicker(app_state.pref)
|
|
|
|
|
|
2018-12-01 00:02:18 +01:00
|
|
|
|
mRefreshLoadingErrorPopupState = 0
|
2018-01-04 19:52:25 +01:00
|
|
|
|
mRefreshLoadingError = ""
|
|
|
|
|
mInitialLoadingError = ""
|
|
|
|
|
bFirstInitialized = true
|
|
|
|
|
bInitialLoading = true
|
|
|
|
|
bRefreshLoading = false
|
2018-08-18 12:58:14 +02:00
|
|
|
|
idOld = null
|
|
|
|
|
idRecent = null
|
2018-08-25 02:29:24 +02:00
|
|
|
|
offsetNext = 0
|
|
|
|
|
pagingType = PagingType.Default
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
duplicate_map.clear()
|
|
|
|
|
list_data.clear()
|
2018-01-20 17:59:38 +01:00
|
|
|
|
fireShowContent(reason = "loading start", reset = true)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
val task = @SuppressLint("StaticFieldLeak")
|
2018-05-04 15:35:01 +02:00
|
|
|
|
object : ColumnTask(ColumnTaskType.LOADING) {
|
2018-07-05 14:38:48 +02:00
|
|
|
|
var parser = TootParser(context, access_info, highlightTrie = highlight_trie)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
var instance_tmp : TootInstance? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
var list_pinned : ArrayList<TimelineItem>? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
var list_tmp : ArrayList<TimelineItem>? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun getInstanceInformation(
|
2018-01-19 10:27:35 +01:00
|
|
|
|
client : TootApiClient,
|
|
|
|
|
instance_name : String?
|
|
|
|
|
) : TootApiResult? {
|
2018-01-17 02:16:26 +01:00
|
|
|
|
if(instance_name != null) {
|
2018-01-12 10:01:25 +01:00
|
|
|
|
// 「インスタンス情報」カラムをNAアカウントで開く場合
|
|
|
|
|
client.instance = instance_name
|
2018-01-17 02:16:26 +01:00
|
|
|
|
} else {
|
2018-01-12 10:01:25 +01:00
|
|
|
|
// カラムに紐付けられたアカウントのタンスのインスタンス情報
|
|
|
|
|
}
|
2018-08-20 19:37:42 +02:00
|
|
|
|
val result = client.parseInstanceInformation(client.getInstanceInformation())
|
2018-08-20 07:10:56 +02:00
|
|
|
|
instance_tmp = result?.data as? TootInstance
|
2018-01-04 19:52:25 +01:00
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun getStatusesPinned(client : TootApiClient, path_base : String) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val result = client.request(path_base)
|
|
|
|
|
val jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray != null) {
|
|
|
|
|
//
|
2018-01-13 07:15:52 +01:00
|
|
|
|
val src = TootParser(
|
|
|
|
|
context,
|
|
|
|
|
access_info,
|
|
|
|
|
pinned = true,
|
|
|
|
|
highlightTrie = highlight_trie
|
|
|
|
|
).statusList(jsonArray)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
this.list_pinned = addWithFilterStatus(null, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
// pinned tootにはページングの概念はない
|
|
|
|
|
}
|
2018-01-10 16:47:35 +01:00
|
|
|
|
log.d("getStatusesPinned: list size=%s", list_pinned?.size ?: - 1)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-16 21:58:30 +02:00
|
|
|
|
fun getStatuses(
|
|
|
|
|
client : TootApiClient,
|
|
|
|
|
path_base : String,
|
2018-10-01 02:10:16 +02:00
|
|
|
|
aroundMin : Boolean = false,
|
|
|
|
|
aroundMax : Boolean = false,
|
|
|
|
|
|
|
|
|
|
misskeyParams : JSONObject? = null,
|
2018-08-24 16:38:32 +02:00
|
|
|
|
misskeyCustomParser : (parser : TootParser, jsonArray : JSONArray) -> ArrayList<TootStatus> =
|
2018-11-18 04:51:03 +01:00
|
|
|
|
{ parser, jsonArray -> parser.statusList(jsonArray) },
|
|
|
|
|
initialUntilDate : Boolean = false
|
2018-08-16 21:58:30 +02:00
|
|
|
|
) : TootApiResult? {
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val params = misskeyParams ?: makeMisskeyTimelineParameter(parser)
|
|
|
|
|
|
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
// 初回の取得
|
2018-10-01 02:10:16 +02:00
|
|
|
|
val result = when {
|
2018-11-18 04:51:03 +01:00
|
|
|
|
isMisskey -> {
|
2018-11-18 15:29:35 +01:00
|
|
|
|
if(initialUntilDate) {
|
|
|
|
|
params.put("untilDate", System.currentTimeMillis() + (86400000L * 365))
|
2018-11-18 04:51:03 +01:00
|
|
|
|
}
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
}
|
2018-11-18 15:29:35 +01:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
aroundMin -> client.request("$path_base&min_id=$status_id")
|
|
|
|
|
aroundMax -> client.request("$path_base&max_id=$status_id")
|
|
|
|
|
else -> client.request(path_base)
|
2018-08-16 21:58:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
var jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray != null) {
|
2018-08-16 21:58:30 +02:00
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
var src = misskeyCustomParser(parser, jsonArray)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(list_tmp == null) {
|
|
|
|
|
list_tmp = ArrayList(src.size)
|
|
|
|
|
}
|
|
|
|
|
this.list_tmp = addWithFilterStatus(list_tmp, src)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
saveRange(true, true, result, src)
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(aroundMin) {
|
|
|
|
|
while(true) {
|
|
|
|
|
if(client.isApiCancelled) {
|
|
|
|
|
log.d("loading-statuses: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(! isFilterEnabled) {
|
|
|
|
|
log.d("loading-statuses: isFiltered is false.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(idRecent == null) {
|
|
|
|
|
log.d("loading-statuses: idRecent is empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
|
|
|
|
log.d("loading-statuses: read enough data.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("loading-statuses: previous response is empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("loading-statuses: timeout.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// フィルタなどが有効な場合は2回目以降の取得
|
|
|
|
|
val result2 = if(isMisskey) {
|
|
|
|
|
idOld?.putMisskeyUntil(params)
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
} else {
|
|
|
|
|
val path = "$path_base${delimiter}min_id=${idRecent}"
|
|
|
|
|
client.request(path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jsonArray = result2?.jsonArray
|
|
|
|
|
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("loading-statuses: error or cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = misskeyCustomParser(parser, jsonArray)
|
|
|
|
|
|
|
|
|
|
addWithFilterStatus(list_tmp, src)
|
|
|
|
|
|
|
|
|
|
if(! saveRangeStart(result = result2, list = src)) {
|
|
|
|
|
log.d("loading-statuses: missing range info.")
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
} else {
|
|
|
|
|
while(true) {
|
|
|
|
|
|
|
|
|
|
if(client.isApiCancelled) {
|
|
|
|
|
log.d("loading-statuses: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(! isFilterEnabled) {
|
|
|
|
|
log.d("loading-statuses: isFiltered is false.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(idOld == null) {
|
|
|
|
|
log.d("loading-statuses: idOld is empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
|
|
|
|
log.d("loading-statuses: read enough data.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("loading-statuses: previous response is empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("loading-statuses: timeout.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// フィルタなどが有効な場合は2回目以降の取得
|
|
|
|
|
val result2 = if(isMisskey) {
|
|
|
|
|
idOld?.putMisskeyUntil(params)
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
} else {
|
|
|
|
|
val path = "$path_base${delimiter}max_id=${idOld}"
|
|
|
|
|
client.request(path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jsonArray = result2?.jsonArray
|
|
|
|
|
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("loading-statuses: error or cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = misskeyCustomParser(parser, jsonArray)
|
|
|
|
|
|
|
|
|
|
addWithFilterStatus(list_tmp, src)
|
|
|
|
|
|
|
|
|
|
if(! saveRangeEnd(result = result2, list = src)) {
|
|
|
|
|
log.d("loading-statuses: missing range info.")
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
fun getConversationSummary(
|
|
|
|
|
client : TootApiClient,
|
|
|
|
|
path_base : String,
|
|
|
|
|
aroundMin : Boolean = false,
|
|
|
|
|
aroundMax : Boolean = false,
|
|
|
|
|
misskeyParams : JSONObject? = null,
|
|
|
|
|
misskeyCustomParser : (parser : TootParser, jsonArray : JSONArray) -> ArrayList<TootConversationSummary> =
|
|
|
|
|
{ parser, jsonArray -> parseList(::TootConversationSummary, parser, jsonArray) }
|
|
|
|
|
) : TootApiResult? {
|
|
|
|
|
|
|
|
|
|
val params = misskeyParams ?: makeMisskeyTimelineParameter(parser)
|
|
|
|
|
|
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
|
|
|
|
|
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
|
|
|
|
|
|
|
|
|
// 初回の取得
|
|
|
|
|
val result = when {
|
|
|
|
|
isMisskey -> client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
aroundMin -> client.request("$path_base&min_id=$status_id")
|
|
|
|
|
aroundMax -> client.request("$path_base&max_id=$status_id")
|
|
|
|
|
else -> client.request(path_base)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray != null) {
|
|
|
|
|
|
|
|
|
|
var src = misskeyCustomParser(parser, jsonArray !!)
|
|
|
|
|
|
|
|
|
|
if(list_tmp == null) {
|
|
|
|
|
list_tmp = ArrayList(src.size)
|
|
|
|
|
}
|
|
|
|
|
this.list_tmp = addWithFilterConversationSummary(list_tmp, src)
|
|
|
|
|
|
|
|
|
|
saveRange(true, true, result, src)
|
|
|
|
|
|
|
|
|
|
if(aroundMin) {
|
|
|
|
|
while(true) {
|
|
|
|
|
if(client.isApiCancelled) {
|
|
|
|
|
log.d("loading-ConversationSummary: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(! isFilterEnabled) {
|
|
|
|
|
log.d("loading-ConversationSummary: isFiltered is false.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(idRecent == null) {
|
|
|
|
|
log.d("loading-ConversationSummary: idRecent is empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
|
|
|
|
log.d("loading-ConversationSummary: read enough data.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("loading-ConversationSummary: previous response is empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("loading-ConversationSummary: timeout.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// フィルタなどが有効な場合は2回目以降の取得
|
|
|
|
|
val result2 = if(isMisskey) {
|
|
|
|
|
idOld?.putMisskeyUntil(params)
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
} else {
|
|
|
|
|
val path = "$path_base${delimiter}min_id=${idRecent}"
|
|
|
|
|
client.request(path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jsonArray = result2?.jsonArray
|
|
|
|
|
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("loading-ConversationSummary: error or cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = misskeyCustomParser(parser, jsonArray !!)
|
|
|
|
|
|
|
|
|
|
addWithFilterConversationSummary(list_tmp, src)
|
|
|
|
|
|
|
|
|
|
if(! saveRangeStart(result = result2, list = src)) {
|
|
|
|
|
log.d("loading-ConversationSummary: missing range info.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
while(true) {
|
|
|
|
|
|
|
|
|
|
if(client.isApiCancelled) {
|
|
|
|
|
log.d("loading-ConversationSummary: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(! isFilterEnabled) {
|
|
|
|
|
log.d("loading-ConversationSummary: isFiltered is false.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(idOld == null) {
|
|
|
|
|
log.d("loading-ConversationSummary: idOld is empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
|
|
|
|
log.d("loading-ConversationSummary: read enough data.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("loading-ConversationSummary: previous response is empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("loading-ConversationSummary: timeout.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// フィルタなどが有効な場合は2回目以降の取得
|
|
|
|
|
val result2 = if(isMisskey) {
|
|
|
|
|
idOld?.putMisskeyUntil(params)
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
} else {
|
|
|
|
|
val path = "$path_base${delimiter}max_id=${idOld}"
|
|
|
|
|
client.request(path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
jsonArray = result2?.jsonArray
|
|
|
|
|
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("loading-ConversationSummary: error or cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = misskeyCustomParser(parser, jsonArray !!)
|
|
|
|
|
|
|
|
|
|
addWithFilterConversationSummary(list_tmp, src)
|
|
|
|
|
|
|
|
|
|
if(! saveRangeEnd(result = result2, list = src)) {
|
|
|
|
|
log.d("loading-ConversationSummary: missing range info.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun parseAccountList(
|
2018-10-01 02:10:16 +02:00
|
|
|
|
client : TootApiClient,
|
|
|
|
|
path_base : String,
|
|
|
|
|
emptyMessage : String? = null,
|
|
|
|
|
misskeyParams : JSONObject? = null,
|
|
|
|
|
misskeyArrayFinder : (JSONObject) -> JSONArray? = { null },
|
2018-08-24 10:03:20 +02:00
|
|
|
|
misskeyCustomParser : (parser : TootParser, jsonArray : JSONArray) -> ArrayList<TootAccountRef> =
|
|
|
|
|
{ parser, jsonArray -> parser.accountList(jsonArray) }
|
2018-01-19 10:27:35 +01:00
|
|
|
|
) : TootApiResult? {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
val result = if(misskeyParams != null) {
|
|
|
|
|
client.request(path_base, misskeyParams.toPostRequestBuilder())
|
|
|
|
|
} else {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
client.request(path_base)
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-21 12:19:02 +02:00
|
|
|
|
if(result != null && result.error == null) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val jsonObject = result.jsonObject
|
2018-08-24 10:03:20 +02:00
|
|
|
|
if(jsonObject != null) {
|
2018-08-25 02:29:24 +02:00
|
|
|
|
if(pagingType == PagingType.Cursor) {
|
|
|
|
|
idOld = EntityId.mayNull(jsonObject.parseString("next"))
|
|
|
|
|
}
|
2018-08-23 07:45:12 +02:00
|
|
|
|
result.data = misskeyArrayFinder(jsonObject)
|
|
|
|
|
}
|
|
|
|
|
val jsonArray = result.jsonArray
|
2018-08-24 10:03:20 +02:00
|
|
|
|
?: return result.setError("missing JSON data.")
|
2018-08-21 12:19:02 +02:00
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
val src = misskeyCustomParser(parser, jsonArray)
|
2018-08-25 02:29:24 +02:00
|
|
|
|
when(pagingType) {
|
|
|
|
|
PagingType.Default -> {
|
|
|
|
|
saveRange(true, true, result, src)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PagingType.Offset -> {
|
|
|
|
|
offsetNext += src.size
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else -> {
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-23 03:57:34 +02:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val tmp = ArrayList<TimelineItem>()
|
2018-05-23 03:57:34 +02:00
|
|
|
|
|
|
|
|
|
if(emptyMessage != null) {
|
|
|
|
|
// フォロー/フォロワー一覧には警告の表示が必要だった
|
|
|
|
|
val who = who_account?.get()
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(! access_info.isMe(who)) {
|
|
|
|
|
if(who != null && access_info.isRemoteUser(who)) tmp.add(
|
2018-05-23 03:57:34 +02:00
|
|
|
|
TootMessageHolder(
|
2018-08-18 12:58:14 +02:00
|
|
|
|
context.getString(R.string.follow_follower_list_may_restrict)
|
2018-05-23 03:57:34 +02:00
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
tmp.add(TootMessageHolder(emptyMessage))
|
2018-05-30 21:50:07 +02:00
|
|
|
|
|
2018-05-23 03:57:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-20 00:31:59 +02:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
tmp.addAll(src)
|
|
|
|
|
list_tmp = addAll(null, tmp)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-07 07:15:16 +02:00
|
|
|
|
fun parseFilterList(
|
|
|
|
|
client : TootApiClient,
|
|
|
|
|
path_base : String
|
|
|
|
|
) : TootApiResult? {
|
|
|
|
|
val result = client.request(path_base)
|
|
|
|
|
if(result != null) {
|
|
|
|
|
val src = TootFilter.parseList(result.jsonArray)
|
|
|
|
|
this.list_tmp = addAll(null, src)
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun parseDomainList(
|
2018-01-19 10:27:35 +01:00
|
|
|
|
client : TootApiClient,
|
|
|
|
|
path_base : String
|
|
|
|
|
) : TootApiResult? {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val result = client.request(path_base)
|
|
|
|
|
if(result != null) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val src = TootDomainBlock.parseList(result.jsonArray)
|
|
|
|
|
saveRange(true, true, result, src)
|
|
|
|
|
this.list_tmp = addAll(null, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun parseReports(client : TootApiClient, path_base : String) : TootApiResult? {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val result = client.request(path_base)
|
|
|
|
|
if(result != null) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val src = parseList(::TootReport, result.jsonArray)
|
|
|
|
|
saveRange(true, true, result, src)
|
|
|
|
|
list_tmp = addAll(null, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-31 14:47:40 +02:00
|
|
|
|
fun parseListList(
|
|
|
|
|
client : TootApiClient,
|
|
|
|
|
path_base : String,
|
2018-10-01 02:10:16 +02:00
|
|
|
|
misskeyParams : JSONObject? = null
|
|
|
|
|
) : TootApiResult? {
|
|
|
|
|
val result = if(misskeyParams != null) {
|
|
|
|
|
client.request(path_base, misskeyParams.toPostRequestBuilder())
|
|
|
|
|
} else {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
client.request(path_base)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(result != null) {
|
2018-10-01 02:10:16 +02:00
|
|
|
|
val src = parseList(::TootList, parser, result.jsonArray)
|
2018-01-11 10:31:25 +01:00
|
|
|
|
src.sort()
|
2018-08-18 12:58:14 +02:00
|
|
|
|
saveRange(true, true, result, src)
|
2018-01-11 10:31:25 +01:00
|
|
|
|
this.list_tmp = addAll(null, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun parseNotifications(client : TootApiClient) : TootApiResult? {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
val params = makeMisskeyBaseParameter(parser)
|
|
|
|
|
|
2018-03-21 06:18:19 +01:00
|
|
|
|
val path_base = makeNotificationUrl()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val result = if(isMisskey) {
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
} else {
|
|
|
|
|
client.request(path_base)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
var jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray != null) {
|
|
|
|
|
var src = parser.notificationList(jsonArray)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
saveRange(true, true, result, src)
|
|
|
|
|
this.list_tmp = addWithFilterNotification(null, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
//
|
|
|
|
|
if(! src.isEmpty()) {
|
2018-08-25 02:54:17 +02:00
|
|
|
|
PollingWorker.injectData(context, access_info, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
//
|
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
|
|
|
|
while(true) {
|
2018-01-12 10:01:25 +01:00
|
|
|
|
if(client.isApiCancelled) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
log.d("loading-notifications: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if(! isFilterEnabled) {
|
|
|
|
|
log.d("loading-notifications: isFiltered is false.")
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(idOld == null) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
log.d("loading-notifications: max_id is empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
log.d("loading-notifications: read enough data.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("loading-notifications: previous response is empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("loading-notifications: timeout.")
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
val result2 = if(isMisskey) {
|
|
|
|
|
idOld?.putMisskeyUntil(params)
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
} else {
|
|
|
|
|
val path = "$path_base${delimiter}max_id=$idOld"
|
|
|
|
|
client.request(path)
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
jsonArray = result2?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("loading-notifications: error or cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = parser.notificationList(jsonArray)
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
addWithFilterNotification(list_tmp, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(! saveRangeEnd(result2, src)) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
log.d("loading-notifications: missing range info.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
fun getPublicAroundStatuses(client : TootApiClient, url : String) : TootApiResult? {
|
|
|
|
|
// (Mastodonのみ対応)
|
|
|
|
|
|
2018-10-31 04:40:27 +01:00
|
|
|
|
var instance = access_info.instance
|
|
|
|
|
if(instance == null) {
|
|
|
|
|
getInstanceInformation(client, null)
|
|
|
|
|
if(instance_tmp != null) {
|
|
|
|
|
instance = instance_tmp
|
|
|
|
|
access_info.instance = instance
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
// ステータスIDに該当するトゥート
|
|
|
|
|
// タンスをまたいだりすると存在しないかもしれないが、エラーは出さない
|
|
|
|
|
var result : TootApiResult? =
|
|
|
|
|
client.request(String.format(Locale.JAPAN, PATH_STATUSES, status_id))
|
|
|
|
|
val target_status = parser.status(result?.jsonObject)
|
|
|
|
|
if(target_status != null) {
|
|
|
|
|
list_tmp = addOne(list_tmp, target_status)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idOld = null
|
|
|
|
|
idRecent = null
|
|
|
|
|
|
2018-10-31 04:40:27 +01:00
|
|
|
|
var bInstanceTooOld = false
|
2018-11-01 13:48:54 +01:00
|
|
|
|
if(instance?.versionGE(TootInstance.VERSION_2_6_0) == true) {
|
2018-10-31 04:40:27 +01:00
|
|
|
|
// 指定より新しいトゥート
|
|
|
|
|
result = getStatuses(client, url, aroundMin = true)
|
|
|
|
|
if(result == null || result.error != null) return result
|
2018-11-01 13:48:54 +01:00
|
|
|
|
} else {
|
2018-10-31 04:40:27 +01:00
|
|
|
|
bInstanceTooOld = true
|
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
// 指定位置より古いトゥート
|
|
|
|
|
result = getStatuses(client, url, aroundMax = true)
|
|
|
|
|
if(result == null || result.error != null) return result
|
|
|
|
|
|
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
|
|
|
|
list_tmp?.reverse()
|
2018-11-01 13:48:54 +01:00
|
|
|
|
if(bInstanceTooOld) {
|
|
|
|
|
list_tmp?.add(
|
|
|
|
|
0,
|
|
|
|
|
TootMessageHolder(context.getString(R.string.around_toot_limitation_warning))
|
|
|
|
|
)
|
2018-10-31 04:40:27 +01:00
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun getAccountAroundStatuses(client : TootApiClient) : TootApiResult? {
|
|
|
|
|
// (Mastodonのみ対応)
|
|
|
|
|
|
2018-10-31 04:40:27 +01:00
|
|
|
|
var instance = access_info.instance
|
|
|
|
|
if(instance == null) {
|
|
|
|
|
getInstanceInformation(client, null)
|
|
|
|
|
if(instance_tmp != null) {
|
|
|
|
|
instance = instance_tmp
|
|
|
|
|
access_info.instance = instance
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
// ステータスIDに該当するトゥート
|
|
|
|
|
// タンスをまたいだりすると存在しないかもしれない
|
|
|
|
|
var result : TootApiResult? =
|
|
|
|
|
client.request(String.format(Locale.JAPAN, PATH_STATUSES, status_id))
|
|
|
|
|
val target_status = parser.status(result?.jsonObject) ?: return result
|
|
|
|
|
list_tmp = addOne(list_tmp, target_status)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
// ↑のトゥートのアカウントのID
|
|
|
|
|
profile_id = target_status.account.id
|
|
|
|
|
|
|
|
|
|
var path = String.format(
|
|
|
|
|
Locale.JAPAN,
|
|
|
|
|
PATH_ACCOUNT_STATUSES,
|
|
|
|
|
profile_id
|
|
|
|
|
)
|
|
|
|
|
if(with_attachment && ! with_highlight) path += "&only_media=1"
|
2018-10-09 01:20:43 +02:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
idOld = null
|
|
|
|
|
idRecent = null
|
|
|
|
|
|
2018-10-31 04:40:27 +01:00
|
|
|
|
var bInstanceTooOld = false
|
2018-11-01 13:48:54 +01:00
|
|
|
|
if(instance?.versionGE(TootInstance.VERSION_2_6_0) == true) {
|
2018-10-31 04:40:27 +01:00
|
|
|
|
// 指定より新しいトゥート
|
|
|
|
|
result = getStatuses(client, path, aroundMin = true)
|
|
|
|
|
if(result == null || result?.error != null) return result
|
2018-11-01 13:48:54 +01:00
|
|
|
|
} else {
|
2018-10-31 04:40:27 +01:00
|
|
|
|
bInstanceTooOld = true
|
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
// 指定位置より古いトゥート
|
|
|
|
|
result = getStatuses(client, path, aroundMax = true)
|
|
|
|
|
if(result == null || result?.error != null) return result
|
|
|
|
|
|
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
|
|
|
|
list_tmp?.reverse()
|
2018-11-01 13:48:54 +01:00
|
|
|
|
if(bInstanceTooOld) {
|
|
|
|
|
list_tmp?.add(
|
|
|
|
|
0,
|
|
|
|
|
TootMessageHolder(context.getString(R.string.around_toot_limitation_warning))
|
|
|
|
|
)
|
2018-10-31 04:40:27 +01:00
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-30 08:57:41 +02:00
|
|
|
|
override fun doInBackground(vararg unused : Void) : TootApiResult? {
|
2018-04-21 01:16:44 +02:00
|
|
|
|
ctStarted.set(true)
|
2018-04-20 15:22:21 +02:00
|
|
|
|
|
2018-12-26 05:47:16 +01:00
|
|
|
|
if(Pref.bpInstanceTicker(app_state.pref)) {
|
2018-12-08 16:29:07 +01:00
|
|
|
|
InstanceTicker.load()
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-17 02:16:26 +01:00
|
|
|
|
val client = TootApiClient(context, callback = object : TootApiCallback {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
override val isApiCancelled : Boolean
|
|
|
|
|
get() = isCancelled || is_dispose.get()
|
|
|
|
|
|
|
|
|
|
override fun publishApiProgress(s : String) {
|
2018-01-21 13:46:36 +01:00
|
|
|
|
runOnMainLooper {
|
|
|
|
|
if(isCancelled) return@runOnMainLooper
|
2018-01-04 19:52:25 +01:00
|
|
|
|
task_progress = s
|
2018-01-20 17:59:38 +01:00
|
|
|
|
fireShowContent(reason = "loading progress", changeList = ArrayList())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2018-01-17 02:16:26 +01:00
|
|
|
|
client.account = access_info
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-12-25 12:54:41 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
try {
|
2018-12-26 05:47:16 +01:00
|
|
|
|
var result : TootApiResult? = access_info.checkConfirmed(context, client)
|
|
|
|
|
if(result == null || result?.error != null) return result
|
2018-12-25 12:54:41 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val q : String
|
|
|
|
|
|
2018-07-06 17:22:22 +02:00
|
|
|
|
muted_word2 = encodeFilterTree(loadFilter2(client))
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
when(column_type) {
|
2018-04-18 14:38:17 +02:00
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
TYPE_DIRECT_MESSAGES -> {
|
|
|
|
|
|
2018-10-10 22:43:10 +02:00
|
|
|
|
useConversationSummarys = false
|
2018-10-30 20:29:00 +01:00
|
|
|
|
if(! use_old_api) {
|
|
|
|
|
|
2018-10-10 22:43:10 +02:00
|
|
|
|
// try 2.6.0 new API https://github.com/tootsuite/mastodon/pull/8832
|
2018-10-28 14:08:10 +01:00
|
|
|
|
val resultCS = getConversationSummary(client, PATH_DIRECT_MESSAGES2)
|
2018-10-30 20:29:00 +01:00
|
|
|
|
|
|
|
|
|
when {
|
2018-10-10 22:43:10 +02:00
|
|
|
|
// cancelled
|
2018-10-28 14:08:10 +01:00
|
|
|
|
resultCS == null -> return null
|
2018-10-10 22:43:10 +02:00
|
|
|
|
|
|
|
|
|
// not error
|
2018-10-28 14:08:10 +01:00
|
|
|
|
resultCS.error.isNullOrBlank() -> {
|
2018-10-10 22:43:10 +02:00
|
|
|
|
useConversationSummarys = true
|
2018-10-28 14:08:10 +01:00
|
|
|
|
return resultCS
|
2018-10-10 22:43:10 +02:00
|
|
|
|
}
|
2018-10-09 01:20:43 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-10 22:43:10 +02:00
|
|
|
|
|
|
|
|
|
// fallback to old api
|
|
|
|
|
return getStatuses(client, PATH_DIRECT_MESSAGES)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
TYPE_LOCAL -> return getStatuses(client, makePublicLocalUrl())
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
TYPE_LOCAL_AROUND -> return getPublicAroundStatuses(
|
|
|
|
|
client,
|
|
|
|
|
makePublicLocalUrl()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
TYPE_FEDERATED_AROUND -> return getPublicAroundStatuses(
|
|
|
|
|
client,
|
|
|
|
|
makePublicFederateUrl()
|
|
|
|
|
)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
TYPE_ACCOUNT_AROUND -> return getAccountAroundStatuses(
|
2018-10-01 02:10:16 +02:00
|
|
|
|
client
|
2018-10-09 01:20:43 +02:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
)
|
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_MISSKEY_HYBRID -> return getStatuses(client, makeMisskeyHybridTlUrl())
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
TYPE_FEDERATE -> return getStatuses(client, makePublicFederateUrl())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_PROFILE -> {
|
|
|
|
|
|
2018-10-30 20:29:00 +01:00
|
|
|
|
val who_result = loadProfileAccount(client, parser, true)
|
2018-05-23 03:57:34 +02:00
|
|
|
|
if(client.isApiCancelled || who_account == null) return who_result
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
when(profile_tab) {
|
|
|
|
|
|
2018-12-26 05:47:16 +01:00
|
|
|
|
TAB_FOLLOWING -> return when {
|
|
|
|
|
access_info.isPseudo -> {
|
|
|
|
|
idRecent = null
|
|
|
|
|
idOld = null
|
|
|
|
|
list_tmp = addOne(
|
|
|
|
|
list_tmp,
|
|
|
|
|
TootMessageHolder(context.getString(R.string.pseudo_account_cant_get_follow_list))
|
|
|
|
|
)
|
|
|
|
|
TootApiResult()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isMisskey -> {
|
|
|
|
|
pagingType = PagingType.Cursor
|
|
|
|
|
parseAccountList(
|
|
|
|
|
client,
|
|
|
|
|
PATH_MISSKEY_PROFILE_FOLLOWING,
|
|
|
|
|
emptyMessage = context.getString(R.string.none_or_hidden_following),
|
|
|
|
|
misskeyParams = makeMisskeyParamsUserId(parser),
|
|
|
|
|
misskeyArrayFinder = misskeyArrayFinderUsers
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else -> {
|
|
|
|
|
parseAccountList(
|
|
|
|
|
client,
|
|
|
|
|
String.format(
|
|
|
|
|
Locale.JAPAN,
|
|
|
|
|
PATH_ACCOUNT_FOLLOWING,
|
|
|
|
|
profile_id
|
|
|
|
|
),
|
|
|
|
|
emptyMessage = context.getString(R.string.none_or_hidden_following)
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-12-26 05:47:16 +01:00
|
|
|
|
TAB_FOLLOWERS -> return when {
|
|
|
|
|
access_info.isPseudo -> {
|
|
|
|
|
idRecent = null
|
|
|
|
|
idOld = null
|
|
|
|
|
list_tmp = addOne(
|
|
|
|
|
list_tmp,
|
|
|
|
|
TootMessageHolder(context.getString(R.string.pseudo_account_cant_get_follow_list))
|
|
|
|
|
)
|
|
|
|
|
TootApiResult()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
isMisskey -> {
|
|
|
|
|
pagingType = PagingType.Cursor
|
|
|
|
|
parseAccountList(
|
|
|
|
|
client,
|
|
|
|
|
PATH_MISSKEY_PROFILE_FOLLOWERS,
|
|
|
|
|
emptyMessage = context.getString(R.string.none_or_hidden_followers),
|
|
|
|
|
misskeyParams = makeMisskeyParamsUserId(parser),
|
|
|
|
|
misskeyArrayFinder = misskeyArrayFinderUsers
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else -> {
|
|
|
|
|
parseAccountList(
|
|
|
|
|
client,
|
|
|
|
|
String.format(
|
|
|
|
|
Locale.JAPAN,
|
|
|
|
|
PATH_ACCOUNT_FOLLOWERS,
|
|
|
|
|
profile_id
|
|
|
|
|
),
|
|
|
|
|
emptyMessage = context.getString(R.string.none_or_hidden_followers)
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-11-18 04:51:03 +01:00
|
|
|
|
else -> {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
var instance = access_info.instance
|
2018-08-20 02:07:55 +02:00
|
|
|
|
|
2018-12-26 05:47:16 +01:00
|
|
|
|
return if(isMisskey) {
|
|
|
|
|
// 固定トゥートの取得
|
|
|
|
|
val pinnedNotes = who_account?.get()?.pinnedNotes
|
|
|
|
|
if(pinnedNotes != null) {
|
|
|
|
|
this.list_pinned =
|
|
|
|
|
addWithFilterStatus(null, pinnedNotes)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 通常トゥートの取得
|
|
|
|
|
getStatuses(
|
|
|
|
|
client,
|
|
|
|
|
PATH_MISSKEY_PROFILE_STATUSES,
|
|
|
|
|
misskeyParams = makeMisskeyParamsProfileStatuses(parser),
|
|
|
|
|
initialUntilDate = true
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// まだ取得してない
|
|
|
|
|
// 疑似アカウントの場合は過去のデータが別タンスかもしれない?
|
|
|
|
|
if(instance == null || access_info.isPseudo) {
|
2018-12-26 05:47:16 +01:00
|
|
|
|
getInstanceInformation(client, null)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(instance_tmp != null) {
|
|
|
|
|
instance = instance_tmp
|
|
|
|
|
access_info.instance = instance
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
var path = String.format(
|
|
|
|
|
Locale.JAPAN,
|
|
|
|
|
PATH_ACCOUNT_STATUSES,
|
|
|
|
|
profile_id
|
|
|
|
|
)
|
|
|
|
|
if(with_attachment && ! with_highlight) path += "&only_media=1"
|
|
|
|
|
|
2018-11-01 13:48:54 +01:00
|
|
|
|
if(instance?.versionGE(TootInstance.VERSION_1_6) == true
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// 将来的に正しく判定できる見込みがないので、Pleroma条件でのフィルタは行わない
|
|
|
|
|
// && instance.instanceType != TootInstance.InstanceType.Pleroma
|
|
|
|
|
) {
|
|
|
|
|
getStatusesPinned(client, "$path&pinned=true")
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-26 05:47:16 +01:00
|
|
|
|
getStatuses(client, path)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-25 02:29:24 +02:00
|
|
|
|
TYPE_MUTES -> return if(isMisskey) {
|
|
|
|
|
pagingType = PagingType.Cursor
|
2018-08-24 18:56:04 +02:00
|
|
|
|
parseAccountList(
|
|
|
|
|
client
|
2018-08-25 02:29:24 +02:00
|
|
|
|
, PATH_MISSKEY_MUTES
|
2018-08-24 18:56:04 +02:00
|
|
|
|
, misskeyParams = access_info.putMisskeyApiToken(JSONObject())
|
2018-08-25 02:29:24 +02:00
|
|
|
|
, misskeyArrayFinder = misskeyArrayFinderUsers
|
2018-08-24 18:56:04 +02:00
|
|
|
|
)
|
2018-08-25 02:29:24 +02:00
|
|
|
|
} else {
|
2018-08-24 18:56:04 +02:00
|
|
|
|
parseAccountList(client, PATH_MUTES)
|
|
|
|
|
}
|
2018-07-07 07:15:16 +02:00
|
|
|
|
TYPE_KEYWORD_FILTER -> return parseFilterList(client, PATH_FILTERS)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-11-01 13:48:54 +01:00
|
|
|
|
TYPE_BLOCKS -> return if(isMisskey) {
|
|
|
|
|
pagingType = PagingType.Default
|
|
|
|
|
val params = access_info.putMisskeyApiToken(JSONObject())
|
|
|
|
|
parseAccountList(
|
|
|
|
|
client,
|
|
|
|
|
"/api/blocking/list",
|
|
|
|
|
misskeyParams = params,
|
|
|
|
|
misskeyCustomParser = misskeyCustomParserBlocks
|
|
|
|
|
)
|
|
|
|
|
} else {
|
2018-10-30 20:29:00 +01:00
|
|
|
|
parseAccountList(client, PATH_BLOCKS)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_DOMAIN_BLOCKS -> return parseDomainList(client, PATH_DOMAIN_BLOCK)
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
TYPE_LIST_LIST -> return if(isMisskey) {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
val params = makeMisskeyBaseParameter(parser)
|
2018-09-07 13:34:54 +02:00
|
|
|
|
parseListList(
|
2018-08-31 14:47:40 +02:00
|
|
|
|
client,
|
|
|
|
|
"/api/users/lists/list",
|
|
|
|
|
misskeyParams = params
|
|
|
|
|
)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
} else {
|
2018-09-07 13:34:54 +02:00
|
|
|
|
parseListList(client, PATH_LIST_LIST)
|
2018-08-31 14:47:40 +02:00
|
|
|
|
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_LIST_TL -> {
|
|
|
|
|
loadListInfo(client, true)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
return if(isMisskey) {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
val params = makeMisskeyTimelineParameter(parser)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
.put("listId", profile_id)
|
|
|
|
|
getStatuses(client, makeListTlUrl(), misskeyParams = params)
|
|
|
|
|
} else {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
getStatuses(client, makeListTlUrl())
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TYPE_LIST_MEMBER -> {
|
|
|
|
|
loadListInfo(client, true)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
return if(isMisskey) {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
pagingType = PagingType.None
|
|
|
|
|
val params = access_info.putMisskeyApiToken(JSONObject())
|
2018-10-01 02:10:16 +02:00
|
|
|
|
.put("userIds", JSONArray().apply {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
list_info?.userIds?.forEach {
|
2018-10-01 02:10:16 +02:00
|
|
|
|
this.put(it.toString())
|
2018-08-31 14:47:40 +02:00
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
parseAccountList(
|
|
|
|
|
client,
|
|
|
|
|
"/api/users/show",
|
|
|
|
|
misskeyParams = params
|
|
|
|
|
)
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
} else {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
parseAccountList(
|
|
|
|
|
client,
|
|
|
|
|
String.format(Locale.JAPAN, PATH_LIST_MEMBER, profile_id)
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-21 12:19:02 +02:00
|
|
|
|
TYPE_FOLLOW_REQUESTS -> return if(isMisskey) {
|
2018-08-25 02:29:24 +02:00
|
|
|
|
pagingType = PagingType.None
|
2018-08-21 12:19:02 +02:00
|
|
|
|
parseAccountList(
|
|
|
|
|
client
|
2018-08-25 02:29:24 +02:00
|
|
|
|
, PATH_MISSKEY_FOLLOW_REQUESTS
|
2018-08-21 12:19:02 +02:00
|
|
|
|
, misskeyParams = access_info.putMisskeyApiToken(JSONObject())
|
2018-08-24 10:03:20 +02:00
|
|
|
|
, misskeyCustomParser = misskeyCustomParserFollowRequest
|
2018-08-21 12:19:02 +02:00
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
parseAccountList(
|
|
|
|
|
client,
|
|
|
|
|
PATH_FOLLOW_REQUESTS
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-07-06 17:22:22 +02:00
|
|
|
|
|
2018-08-25 02:29:24 +02:00
|
|
|
|
TYPE_FOLLOW_SUGGESTION -> return if(isMisskey) {
|
|
|
|
|
pagingType = PagingType.Offset
|
|
|
|
|
parseAccountList(
|
|
|
|
|
client
|
|
|
|
|
, PATH_MISSKEY_FOLLOW_SUGGESTION
|
|
|
|
|
, misskeyParams = access_info.putMisskeyApiToken(JSONObject())
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
parseAccountList(
|
|
|
|
|
client,
|
|
|
|
|
PATH_FOLLOW_SUGGESTION
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-28 14:25:45 +02:00
|
|
|
|
TYPE_ENDORSEMENT -> return parseAccountList(
|
|
|
|
|
client,
|
|
|
|
|
PATH_ENDORSEMENT
|
|
|
|
|
)
|
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
TYPE_FAVOURITES -> return if(isMisskey) {
|
|
|
|
|
getStatuses(
|
|
|
|
|
client
|
2018-08-25 02:29:24 +02:00
|
|
|
|
, PATH_MISSKEY_FAVORITES
|
2018-08-24 16:38:32 +02:00
|
|
|
|
, misskeyParams = makeMisskeyTimelineParameter(parser)
|
|
|
|
|
, misskeyCustomParser = misskeyCustomParserFavorites
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
getStatuses(client, PATH_FAVOURITES)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_HASHTAG -> return if(isMisskey) {
|
|
|
|
|
getStatuses(
|
|
|
|
|
client
|
|
|
|
|
, makeHashtagUrl(hashtag)
|
|
|
|
|
, misskeyParams = makeMisskeyTimelineParameter(parser)
|
|
|
|
|
.put("tag", hashtag)
|
2018-08-24 16:38:32 +02:00
|
|
|
|
.put("limit", MISSKEY_HASHTAG_LIMIT)
|
2018-11-18 15:29:35 +01:00
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
getStatuses(client, makeHashtagUrl(hashtag))
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_REPORTS -> return parseReports(client, PATH_REPORTS)
|
|
|
|
|
|
2018-03-01 07:41:07 +01:00
|
|
|
|
TYPE_NOTIFICATIONS -> return parseNotifications(client)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
TYPE_BOOSTED_BY -> return parseAccountList(
|
|
|
|
|
client,
|
|
|
|
|
String.format(Locale.JAPAN, PATH_BOOSTED_BY, status_id)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
TYPE_FAVOURITED_BY -> return parseAccountList(
|
|
|
|
|
client,
|
|
|
|
|
String.format(Locale.JAPAN, PATH_FAVOURITED_BY, status_id)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_CONVERSATION -> {
|
2018-08-21 12:19:02 +02:00
|
|
|
|
if(isMisskey) {
|
2018-08-20 07:10:56 +02:00
|
|
|
|
// 指定された発言そのもの
|
|
|
|
|
val queryParams = makeMisskeyBaseParameter(parser)
|
2018-08-21 12:19:02 +02:00
|
|
|
|
.put("noteId", status_id)
|
2018-08-20 07:10:56 +02:00
|
|
|
|
result = client.request(
|
|
|
|
|
"/api/notes/show"
|
2018-08-21 12:19:02 +02:00
|
|
|
|
, queryParams.toPostRequestBuilder()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
)
|
2018-08-21 12:19:02 +02:00
|
|
|
|
val jsonObject = result?.jsonObject ?: return result
|
2018-08-20 07:10:56 +02:00
|
|
|
|
val target_status = parser.status(jsonObject)
|
|
|
|
|
?: return TootApiResult("TootStatus parse failed.")
|
|
|
|
|
target_status.conversation_main = true
|
2018-08-21 12:19:02 +02:00
|
|
|
|
|
2018-08-20 07:10:56 +02:00
|
|
|
|
// 祖先
|
|
|
|
|
val list_asc = ArrayList<TootStatus>()
|
2018-08-21 12:19:02 +02:00
|
|
|
|
while(true) {
|
|
|
|
|
if(client.isApiCancelled) return null
|
|
|
|
|
queryParams.put("offset", list_asc.size)
|
2018-08-20 07:10:56 +02:00
|
|
|
|
result = client.request(
|
|
|
|
|
"/api/notes/conversation"
|
2018-08-21 12:19:02 +02:00
|
|
|
|
, queryParams.toPostRequestBuilder()
|
2018-01-19 10:27:35 +01:00
|
|
|
|
)
|
2018-08-20 07:10:56 +02:00
|
|
|
|
val jsonArray = result?.jsonArray ?: return result
|
2018-08-21 12:19:02 +02:00
|
|
|
|
val src = parser.statusList(jsonArray)
|
2018-08-20 07:10:56 +02:00
|
|
|
|
if(src.isEmpty()) break
|
|
|
|
|
list_asc.addAll(src)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 直接の子リプライ。(子孫をたどることまではしない)
|
|
|
|
|
val list_desc = ArrayList<TootStatus>()
|
2018-08-21 12:19:02 +02:00
|
|
|
|
while(true) {
|
|
|
|
|
if(client.isApiCancelled) return null
|
|
|
|
|
queryParams.put("offset", list_desc.size)
|
2018-08-20 07:10:56 +02:00
|
|
|
|
result = client.request(
|
|
|
|
|
"/api/notes/replies"
|
2018-08-21 12:19:02 +02:00
|
|
|
|
, queryParams.toPostRequestBuilder()
|
2018-01-19 10:27:35 +01:00
|
|
|
|
)
|
2018-08-20 07:10:56 +02:00
|
|
|
|
val jsonArray = result?.jsonArray ?: return result
|
2018-08-21 12:19:02 +02:00
|
|
|
|
val src = parser.statusList(jsonArray)
|
2018-08-20 07:10:56 +02:00
|
|
|
|
if(src.isEmpty()) break
|
|
|
|
|
list_desc.addAll(src)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 一つのリストにまとめる
|
|
|
|
|
this.list_tmp = ArrayList<TimelineItem>(
|
2018-08-21 12:19:02 +02:00
|
|
|
|
list_asc.size + list_desc.size + 2
|
|
|
|
|
).apply {
|
|
|
|
|
addAll(list_asc.sortedBy { it.time_created_at })
|
|
|
|
|
add(target_status)
|
|
|
|
|
addAll(list_desc.sortedBy { it.time_created_at })
|
|
|
|
|
add(TootMessageHolder(context.getString(R.string.misskey_cant_show_all_descendants)))
|
2018-08-20 07:10:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
//
|
2018-08-20 07:10:56 +02:00
|
|
|
|
return result
|
|
|
|
|
|
2018-08-21 12:19:02 +02:00
|
|
|
|
} else {
|
2018-08-20 07:10:56 +02:00
|
|
|
|
// 指定された発言そのもの
|
|
|
|
|
result = client.request(
|
|
|
|
|
String.format(Locale.JAPAN, PATH_STATUSES, status_id)
|
2018-08-16 21:58:30 +02:00
|
|
|
|
)
|
2018-08-20 07:10:56 +02:00
|
|
|
|
var jsonObject = result?.jsonObject ?: return result
|
|
|
|
|
val target_status = parser.status(jsonObject)
|
|
|
|
|
?: return TootApiResult("TootStatus parse failed.")
|
|
|
|
|
|
|
|
|
|
// 前後の会話
|
|
|
|
|
result = client.request(
|
|
|
|
|
String.format(Locale.JAPAN, PATH_STATUSES_CONTEXT, status_id)
|
|
|
|
|
)
|
|
|
|
|
jsonObject = result?.jsonObject ?: return result
|
2018-08-21 12:19:02 +02:00
|
|
|
|
val conversation_context =
|
|
|
|
|
parseItem(::TootContext, parser, jsonObject)
|
2018-08-20 07:10:56 +02:00
|
|
|
|
|
|
|
|
|
// 一つのリストにまとめる
|
|
|
|
|
target_status.conversation_main = true
|
|
|
|
|
if(conversation_context != null) {
|
|
|
|
|
|
|
|
|
|
this.list_tmp = ArrayList(
|
|
|
|
|
1
|
|
|
|
|
+ (conversation_context.ancestors?.size ?: 0)
|
|
|
|
|
+ (conversation_context.descendants?.size ?: 0)
|
|
|
|
|
)
|
|
|
|
|
//
|
|
|
|
|
if(conversation_context.ancestors != null)
|
|
|
|
|
addWithFilterStatus(
|
|
|
|
|
this.list_tmp,
|
|
|
|
|
conversation_context.ancestors
|
2018-04-30 16:55:27 +02:00
|
|
|
|
)
|
2018-08-20 07:10:56 +02:00
|
|
|
|
//
|
|
|
|
|
addOne(list_tmp, target_status)
|
|
|
|
|
//
|
|
|
|
|
if(conversation_context.descendants != null)
|
|
|
|
|
addWithFilterStatus(
|
|
|
|
|
this.list_tmp,
|
|
|
|
|
conversation_context.descendants
|
|
|
|
|
)
|
|
|
|
|
//
|
|
|
|
|
} 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))
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-20 01:33:57 +01:00
|
|
|
|
// マストドン2.6でTLにカード情報がレンダリングされたことにより、
|
|
|
|
|
// 個別にカードを取得する機能は ST3.0.7で廃止される
|
|
|
|
|
// この機能はRate limitの問題を引き起こすことが多かった
|
|
|
|
|
// if(! Pref.bpDontRetrievePreviewCard(context)) {
|
|
|
|
|
// this.list_tmp?.forEach { o ->
|
|
|
|
|
// // カードを取得する
|
|
|
|
|
// if(o is TootStatus && o.card == null)
|
|
|
|
|
// o.card = parseItem(
|
|
|
|
|
// ::TootCard,
|
|
|
|
|
// client.request("/api/v1/statuses/" + o.id + "/card")?.jsonObject
|
|
|
|
|
// )
|
|
|
|
|
// }
|
|
|
|
|
// }
|
2018-08-20 07:10:56 +02:00
|
|
|
|
|
|
|
|
|
return result
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-30 21:50:07 +02:00
|
|
|
|
TYPE_TREND_TAG -> {
|
|
|
|
|
result = client.request("/api/v1/trends")
|
2018-05-30 07:18:45 +02:00
|
|
|
|
val src = parser.trendTagList(result?.jsonArray)
|
2018-07-03 06:37:07 +02:00
|
|
|
|
|
2018-05-30 07:18:45 +02:00
|
|
|
|
this.list_tmp = addAll(this.list_tmp, src)
|
2018-06-11 02:33:01 +02:00
|
|
|
|
this.list_tmp = addOne(
|
|
|
|
|
this.list_tmp, TootMessageHolder(
|
2018-07-03 06:37:07 +02:00
|
|
|
|
context.getString(
|
|
|
|
|
R.string.trend_tag_desc,
|
|
|
|
|
getResetTimeString()
|
|
|
|
|
),
|
2018-06-11 02:33:01 +02:00
|
|
|
|
gravity = Gravity.END
|
|
|
|
|
)
|
|
|
|
|
)
|
2018-05-30 07:18:45 +02:00
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
TYPE_SEARCH -> if(isMisskey) {
|
2018-08-30 08:57:41 +02:00
|
|
|
|
result = TootApiResult()
|
2018-10-01 02:10:16 +02:00
|
|
|
|
val parser = TootParser(context, access_info)
|
|
|
|
|
var params : JSONObject
|
2018-08-28 18:24:36 +02:00
|
|
|
|
|
|
|
|
|
list_tmp = ArrayList()
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
val queryAccount = search_query.trim().replace("^@".toRegex(), "")
|
|
|
|
|
if(queryAccount.isNotEmpty()) {
|
|
|
|
|
|
2018-08-28 18:24:36 +02:00
|
|
|
|
params = access_info.putMisskeyApiToken(JSONObject())
|
2018-10-01 02:10:16 +02:00
|
|
|
|
.put("query", queryAccount)
|
|
|
|
|
.put("localOnly", ! search_resolve)
|
|
|
|
|
|
|
|
|
|
result = client.request(
|
|
|
|
|
"/api/users/search",
|
|
|
|
|
params.toPostRequestBuilder()
|
|
|
|
|
)
|
2018-08-28 18:24:36 +02:00
|
|
|
|
val jsonArray = result?.jsonArray
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(jsonArray != null) {
|
|
|
|
|
val src =
|
|
|
|
|
TootParser(context, access_info).accountList(jsonArray)
|
2018-08-28 18:24:36 +02:00
|
|
|
|
list_tmp = addAll(list_tmp, src)
|
|
|
|
|
}
|
2018-08-28 18:31:18 +02:00
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
val queryTag = search_query.trim().replace("^#".toRegex(), "")
|
|
|
|
|
if(queryTag.isNotEmpty()) {
|
2018-08-28 18:31:18 +02:00
|
|
|
|
params = access_info.putMisskeyApiToken(JSONObject())
|
2018-10-01 02:10:16 +02:00
|
|
|
|
.put("query", queryTag)
|
|
|
|
|
result = client.request(
|
|
|
|
|
"/api/hashtags/search",
|
|
|
|
|
params.toPostRequestBuilder()
|
|
|
|
|
)
|
2018-08-28 18:31:18 +02:00
|
|
|
|
val jsonArray = result?.jsonArray
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(jsonArray != null) {
|
|
|
|
|
val src = TootTag.parseTootTagList(parser, jsonArray)
|
2018-08-28 18:31:18 +02:00
|
|
|
|
list_tmp = addAll(list_tmp, src)
|
|
|
|
|
}
|
2018-08-28 18:24:36 +02:00
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(search_query.isNotEmpty()) {
|
2018-08-28 18:24:36 +02:00
|
|
|
|
params = access_info.putMisskeyApiToken(JSONObject())
|
2018-10-01 02:10:16 +02:00
|
|
|
|
.put("query", search_query)
|
|
|
|
|
result = client.request(
|
|
|
|
|
"/api/notes/search",
|
|
|
|
|
params.toPostRequestBuilder()
|
|
|
|
|
)
|
2018-08-28 18:24:36 +02:00
|
|
|
|
val jsonArray = result?.jsonArray
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(jsonArray != null) {
|
2018-08-28 18:24:36 +02:00
|
|
|
|
val src = parser.statusList(jsonArray)
|
|
|
|
|
list_tmp = addWithFilterStatus(list_tmp, src)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-13 17:27:38 +01:00
|
|
|
|
// 検索機能が無効だとsearch_query が 400を返すが、他のAPIがデータを返したら成功したことにする
|
2018-11-18 03:38:52 +01:00
|
|
|
|
return if(list_tmp?.isNotEmpty() == true) {
|
2018-11-13 17:27:38 +01:00
|
|
|
|
TootApiResult()
|
2018-11-18 03:38:52 +01:00
|
|
|
|
} else {
|
2018-11-13 17:27:38 +01:00
|
|
|
|
result
|
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
} else {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(access_info.isPseudo) {
|
|
|
|
|
// 1.5.0rc からマストドンの検索APIは認証を要求するようになった
|
|
|
|
|
return TootApiResult(context.getString(R.string.search_is_not_available_on_pseudo_account))
|
|
|
|
|
}
|
2018-05-30 07:18:45 +02:00
|
|
|
|
|
|
|
|
|
var instance = access_info.instance
|
2018-05-30 21:50:07 +02:00
|
|
|
|
if(instance == null) {
|
|
|
|
|
getInstanceInformation(client, null)
|
2018-05-30 07:18:45 +02:00
|
|
|
|
if(instance_tmp != null) {
|
|
|
|
|
instance = instance_tmp
|
|
|
|
|
access_info.instance = instance
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-05-30 21:50:07 +02:00
|
|
|
|
|
2018-10-31 04:40:27 +01:00
|
|
|
|
if(instance?.versionGE(TootInstance.VERSION_2_4_0) == true) {
|
2018-05-30 07:18:45 +02:00
|
|
|
|
// v2 api を試す
|
|
|
|
|
var path = String.format(
|
2018-05-30 21:50:07 +02:00
|
|
|
|
Locale.JAPAN,
|
|
|
|
|
PATH_SEARCH_V2,
|
|
|
|
|
search_query.encodePercent()
|
|
|
|
|
)
|
2018-05-30 07:18:45 +02:00
|
|
|
|
if(search_resolve) path += "&resolve=1"
|
|
|
|
|
|
|
|
|
|
result = client.request(path)
|
|
|
|
|
val jsonObject = result?.jsonObject
|
2018-05-30 21:50:07 +02:00
|
|
|
|
if(jsonObject != null) {
|
2018-05-30 07:18:45 +02:00
|
|
|
|
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)
|
2018-05-30 21:50:07 +02:00
|
|
|
|
return result
|
2018-05-30 07:18:45 +02:00
|
|
|
|
}
|
2018-05-30 21:50:07 +02:00
|
|
|
|
}
|
2018-11-01 13:48:54 +01:00
|
|
|
|
if(instance?.versionGE(TootInstance.VERSION_2_4_1_rc1) == true) {
|
2018-05-30 21:50:07 +02:00
|
|
|
|
// 2.4.1rc1以降はv2が確実に存在するはずなので、v1へのフォールバックを行わない
|
2018-05-30 07:18:45 +02:00
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
var path =
|
2018-01-21 13:46:36 +01:00
|
|
|
|
String.format(
|
|
|
|
|
Locale.JAPAN,
|
|
|
|
|
PATH_SEARCH,
|
|
|
|
|
search_query.encodePercent()
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(search_resolve) path += "&resolve=1"
|
|
|
|
|
|
|
|
|
|
result = client.request(path)
|
|
|
|
|
val jsonObject = result?.jsonObject
|
|
|
|
|
if(result == null || jsonObject == null) return result
|
|
|
|
|
|
|
|
|
|
val tmp = parser.results(jsonObject)
|
|
|
|
|
if(tmp != null) {
|
|
|
|
|
list_tmp = ArrayList()
|
|
|
|
|
addAll(list_tmp, tmp.hashtags)
|
|
|
|
|
addAll(list_tmp, tmp.accounts)
|
|
|
|
|
addAll(list_tmp, tmp.statuses)
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TYPE_SEARCH_MSP -> {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
idOld = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
q = search_query.trim { it <= ' ' }
|
|
|
|
|
if(q.isEmpty()) {
|
|
|
|
|
list_tmp = ArrayList()
|
|
|
|
|
result = TootApiResult()
|
|
|
|
|
} else {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
result = client.searchMsp(search_query, idOld?.toString())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray != null) {
|
|
|
|
|
// max_id の更新
|
2018-08-18 12:58:14 +02:00
|
|
|
|
idOld = EntityId.mayNull(
|
|
|
|
|
TootApiClient.getMspMaxId(
|
|
|
|
|
jsonArray,
|
|
|
|
|
idOld?.toString()
|
|
|
|
|
)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// リストデータの用意
|
2018-01-21 17:47:13 +01:00
|
|
|
|
parser.serviceType = ServiceType.MSP
|
2018-02-04 18:55:42 +01:00
|
|
|
|
list_tmp =
|
|
|
|
|
addWithFilterStatus(null, parser.statusList(jsonArray))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TYPE_SEARCH_TS -> {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
idOld = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
q = search_query.trim { it <= ' ' }
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if(q.isEmpty()) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
list_tmp = ArrayList()
|
|
|
|
|
result = TootApiResult()
|
|
|
|
|
} else {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
result = client.searchTootsearch(search_query, idOld?.toLong())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val jsonObject = result?.jsonObject
|
|
|
|
|
if(jsonObject != null) {
|
|
|
|
|
// max_id の更新
|
2018-08-18 12:58:14 +02:00
|
|
|
|
idOld = EntityId.mayNull(
|
|
|
|
|
TootApiClient.getTootsearchMaxId(
|
|
|
|
|
jsonObject,
|
|
|
|
|
idOld?.toLong()
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// リストデータの用意
|
2018-01-19 10:27:35 +01:00
|
|
|
|
val search_result =
|
|
|
|
|
TootStatus.parseListTootsearch(parser, jsonObject)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
this.list_tmp = addWithFilterStatus(null, search_result)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(search_result.isEmpty()) {
|
|
|
|
|
log.d("search result is empty. %s", result?.bodyString)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TYPE_INSTANCE_INFORMATION -> {
|
|
|
|
|
result = getInstanceInformation(client, instance_uri)
|
|
|
|
|
if(instance_tmp != null) {
|
|
|
|
|
instance_information = instance_tmp
|
2018-12-07 01:50:11 +01:00
|
|
|
|
handshake = result?.response?.handshake()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
else -> return getStatuses(client, makeHomeTlUrl())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
try {
|
2018-08-21 12:19:02 +02:00
|
|
|
|
updateRelation(client, list_tmp, who_account, parser)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
}
|
2018-04-21 01:16:44 +02:00
|
|
|
|
ctClosed.set(true)
|
2018-05-04 15:35:01 +02:00
|
|
|
|
runOnMainLooperDelayed(333L) {
|
|
|
|
|
if(! isCancelled) fireShowColumnStatus()
|
2018-04-21 01:16:44 +02:00
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
override fun onCancelled(result : TootApiResult?) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
onPostExecute(null)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onPostExecute(result : TootApiResult?) {
|
|
|
|
|
if(is_dispose.get()) return
|
|
|
|
|
|
|
|
|
|
if(isCancelled || result == null) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-21 01:16:44 +02:00
|
|
|
|
bInitialLoading = false
|
|
|
|
|
lastTask = null
|
|
|
|
|
|
|
|
|
|
if(result.error != null) {
|
|
|
|
|
this@Column.mInitialLoadingError = result.error ?: ""
|
|
|
|
|
} else {
|
|
|
|
|
duplicate_map.clear()
|
|
|
|
|
list_data.clear()
|
|
|
|
|
val list_tmp = this.list_tmp
|
|
|
|
|
if(list_tmp != null) {
|
|
|
|
|
val list_pinned = this.list_pinned
|
|
|
|
|
if(list_pinned?.isNotEmpty() == true) {
|
|
|
|
|
val list_new = duplicate_map.filterDuplicate(list_pinned)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
list_data.addAll(list_new)
|
|
|
|
|
}
|
2018-04-21 01:16:44 +02:00
|
|
|
|
val list_new = duplicate_map.filterDuplicate(list_tmp)
|
|
|
|
|
list_data.addAll(list_new)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-21 01:16:44 +02:00
|
|
|
|
resumeStreaming(false)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-04-21 01:16:44 +02:00
|
|
|
|
fireShowContent(reason = "loading updated", reset = true)
|
|
|
|
|
|
|
|
|
|
// 初期ロードの直後は先頭に移動する
|
|
|
|
|
viewHolder?.scrollToTop()
|
2018-11-03 15:21:42 +01:00
|
|
|
|
|
|
|
|
|
updateMisskeyCapture()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-21 01:16:44 +02:00
|
|
|
|
this.lastTask = task
|
2018-01-04 19:52:25 +01:00
|
|
|
|
task.executeOnExecutor(App1.task_executor)
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-07 19:57:19 +02:00
|
|
|
|
private var bMinIdMatched : Boolean = false
|
2018-09-30 23:33:58 +02:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
private fun parseRange(
|
|
|
|
|
result : TootApiResult?,
|
|
|
|
|
list : List<TimelineItem>?
|
|
|
|
|
) : Pair<EntityId?, EntityId?> {
|
|
|
|
|
var idMin : EntityId? = null
|
|
|
|
|
var idMax : EntityId? = null
|
|
|
|
|
|
|
|
|
|
if(isMisskey && list != null) {
|
|
|
|
|
// MisskeyはLinkヘッダがないので、常にデータからIDを読む
|
|
|
|
|
for(item in list) {
|
|
|
|
|
val id = item.getOrderId()
|
|
|
|
|
if(idMin == null || id < idMin) idMin = id
|
|
|
|
|
if(idMax == null || id > idMax) idMax = id
|
|
|
|
|
}
|
|
|
|
|
} else if(result != null) {
|
|
|
|
|
// Linkヘッダを読む
|
|
|
|
|
idMin = if(result.link_older == null) {
|
|
|
|
|
null
|
|
|
|
|
} else {
|
|
|
|
|
val m = reMaxId.matcher(result.link_older)
|
|
|
|
|
if(m.find()) {
|
2018-08-25 10:05:50 +02:00
|
|
|
|
EntityIdLong(m.group(1).toLong())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} else {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
idMax = if(result.link_newer == null) {
|
|
|
|
|
null
|
|
|
|
|
} else {
|
2018-09-30 23:33:58 +02:00
|
|
|
|
var m = reMinId.matcher(result.link_newer)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(m.find()) {
|
2018-09-30 23:33:58 +02:00
|
|
|
|
bMinIdMatched = true
|
2018-08-25 10:05:50 +02:00
|
|
|
|
EntityIdLong(m.group(1).toLong())
|
2018-08-18 12:58:14 +02:00
|
|
|
|
} else {
|
2018-09-30 23:33:58 +02:00
|
|
|
|
m = reSinceId.matcher(result.link_newer)
|
|
|
|
|
if(m.find()) {
|
|
|
|
|
bMinIdMatched = false
|
|
|
|
|
EntityIdLong(m.group(1).toLong())
|
|
|
|
|
} else {
|
|
|
|
|
null
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
return Pair(idMin, idMax)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// int scroll_hack;
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// return true if list bottom may have unread remain
|
|
|
|
|
private fun saveRange(
|
|
|
|
|
bBottom : Boolean,
|
|
|
|
|
bTop : Boolean,
|
|
|
|
|
result : TootApiResult?,
|
|
|
|
|
list : List<TimelineItem>?
|
|
|
|
|
) : Boolean {
|
|
|
|
|
val (idMin, idMax) = parseRange(result, list)
|
|
|
|
|
|
|
|
|
|
var hasBottomRemain = false
|
|
|
|
|
|
2018-08-16 21:58:30 +02:00
|
|
|
|
if(bBottom) {
|
2018-08-25 10:05:50 +02:00
|
|
|
|
when {
|
|
|
|
|
idMin == null -> idOld = null // リストの終端
|
|
|
|
|
idMin is EntityIdString && ! isMisskey -> {
|
|
|
|
|
log.e("EntityId should be Long for non-misskey column! columnType=$column_type")
|
|
|
|
|
}
|
2018-08-26 04:09:34 +02:00
|
|
|
|
|
2018-08-25 10:05:50 +02:00
|
|
|
|
else -> {
|
|
|
|
|
val i = idOld?.compareTo(idMin)
|
|
|
|
|
if(i == null || i > 0) {
|
|
|
|
|
idOld = idMin
|
|
|
|
|
hasBottomRemain = true
|
|
|
|
|
}
|
2018-08-16 21:58:30 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
if(bTop) {
|
2018-08-25 10:05:50 +02:00
|
|
|
|
when {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// リロードを許容するため、取得内容がカラでもidRecentを変更しない
|
2018-08-25 10:05:50 +02:00
|
|
|
|
idMax == null -> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idMax is EntityIdString && ! isMisskey -> {
|
|
|
|
|
log.e("EntityId should be Long for non-misskey column! columnType=$column_type")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else -> {
|
|
|
|
|
val i = idRecent?.compareTo(idMax)
|
|
|
|
|
if(i == null || i < 0) {
|
|
|
|
|
idRecent = idMax
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
return hasBottomRemain
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// return true if list bottom may have unread remain
|
|
|
|
|
private fun saveRangeEnd(result : TootApiResult?, list : List<TimelineItem>?) =
|
|
|
|
|
saveRange(true, false, result = result, list = list)
|
2018-08-16 21:58:30 +02:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
// return true if list bottom may have unread remain
|
|
|
|
|
private fun saveRangeStart(result : TootApiResult?, list : List<TimelineItem>?) =
|
|
|
|
|
saveRange(false, true, result = result, list = list)
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
private fun addRange(bBottom : Boolean, path : String) : String {
|
|
|
|
|
val delimiter = if(- 1 != path.indexOf('?')) '&' else '?'
|
|
|
|
|
if(bBottom) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(idOld != null) return "$path${delimiter}max_id=${idOld}"
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} else {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(idRecent != null) return "$path${delimiter}since_id=${idRecent}"
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
return path
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
private fun addRangeMin(path : String) : String {
|
|
|
|
|
val delimiter = if(- 1 != path.indexOf('?')) '&' else '?'
|
|
|
|
|
if(idRecent != null) return "$path${delimiter}min_id=${idRecent}"
|
|
|
|
|
return path
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
private fun addRangeMisskey(bBottom : Boolean, params : JSONObject) : JSONObject {
|
|
|
|
|
if(bBottom) {
|
|
|
|
|
idOld?.putMisskeyUntil(params)
|
|
|
|
|
} else {
|
|
|
|
|
idRecent?.putMisskeySince(params)
|
|
|
|
|
}
|
|
|
|
|
return params
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-08 05:14:46 +01:00
|
|
|
|
internal fun startRefreshForPost(
|
|
|
|
|
refresh_after_post : Int,
|
2018-08-18 12:58:14 +02:00
|
|
|
|
posted_status_id : EntityId,
|
|
|
|
|
posted_reply_id : EntityId?
|
2018-02-08 05:14:46 +01:00
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
when(column_type) {
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_HOME, TYPE_LOCAL, TYPE_FEDERATE, TYPE_DIRECT_MESSAGES, TYPE_MISSKEY_HYBRID -> {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
startRefresh(
|
|
|
|
|
true,
|
|
|
|
|
false,
|
|
|
|
|
posted_status_id,
|
|
|
|
|
refresh_after_post
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
TYPE_PROFILE -> {
|
|
|
|
|
if(profile_tab == TAB_STATUS
|
|
|
|
|
&& profile_id == access_info.loginAccount?.id
|
|
|
|
|
) {
|
|
|
|
|
startRefresh(
|
|
|
|
|
true,
|
|
|
|
|
false,
|
|
|
|
|
posted_status_id,
|
|
|
|
|
refresh_after_post
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-08 05:14:46 +01:00
|
|
|
|
TYPE_CONVERSATION -> {
|
|
|
|
|
// 会話への返信が行われたなら会話を更新する
|
|
|
|
|
try {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(posted_reply_id != null) {
|
2018-02-08 05:14:46 +01:00
|
|
|
|
for(item in list_data) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(item is TootStatus && item.id == posted_reply_id) {
|
2018-02-08 05:14:46 +01:00
|
|
|
|
startLoading()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
} catch(_ : Throwable) {
|
2018-02-08 05:14:46 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
internal fun startRefresh(
|
|
|
|
|
bSilent : Boolean,
|
|
|
|
|
bBottom : Boolean,
|
2018-08-18 12:58:14 +02:00
|
|
|
|
posted_status_id : EntityId? = null,
|
|
|
|
|
refresh_after_toot : Int = - 1
|
2018-01-19 10:27:35 +01:00
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-04-21 01:16:44 +02:00
|
|
|
|
if(lastTask != null) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(! bSilent) {
|
2018-01-21 13:46:36 +01:00
|
|
|
|
showToast(context, true, R.string.column_is_busy)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val holder = viewHolder
|
|
|
|
|
if(holder != null) holder.refreshLayout.isRefreshing = false
|
|
|
|
|
}
|
|
|
|
|
return
|
2018-08-25 02:29:24 +02:00
|
|
|
|
} else if(bBottom && ! canRefreshBottom()) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(! bSilent) {
|
2018-01-21 13:46:36 +01:00
|
|
|
|
showToast(context, true, R.string.end_of_list)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val holder = viewHolder
|
|
|
|
|
if(holder != null) holder.refreshLayout.isRefreshing = false
|
|
|
|
|
}
|
|
|
|
|
return
|
2018-08-25 02:29:24 +02:00
|
|
|
|
} else if(! bBottom && ! canRefreshTop()) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val holder = viewHolder
|
|
|
|
|
if(holder != null) holder.refreshLayout.isRefreshing = false
|
|
|
|
|
startLoading()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(bSilent) {
|
|
|
|
|
val holder = viewHolder
|
|
|
|
|
if(holder != null) {
|
|
|
|
|
holder.refreshLayout.isRefreshing = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(! bBottom) {
|
|
|
|
|
bRefreshingTop = true
|
|
|
|
|
stopStreaming()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bRefreshLoading = true
|
|
|
|
|
mRefreshLoadingError = ""
|
|
|
|
|
|
|
|
|
|
val task = @SuppressLint("StaticFieldLeak")
|
2018-05-04 15:35:01 +02:00
|
|
|
|
object : ColumnTask(
|
|
|
|
|
when {
|
|
|
|
|
bBottom -> ColumnTaskType.REFRESH_BOTTOM
|
|
|
|
|
else -> ColumnTaskType.REFRESH_TOP
|
|
|
|
|
}
|
|
|
|
|
) {
|
2018-07-05 14:38:48 +02:00
|
|
|
|
var parser = TootParser(context, access_info, highlightTrie = highlight_trie)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
var list_tmp : ArrayList<TimelineItem>? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun getAccountList(
|
2018-01-19 10:27:35 +01:00
|
|
|
|
client : TootApiClient,
|
2018-08-18 12:58:14 +02:00
|
|
|
|
path_base : String,
|
|
|
|
|
misskeyParams : JSONObject? = null,
|
2018-08-23 07:45:12 +02:00
|
|
|
|
misskeyArrayFinder : (JSONObject) -> JSONArray? = { null },
|
2018-08-24 10:03:20 +02:00
|
|
|
|
misskeyCustomParser : (parser : TootParser, jsonArray : JSONArray) -> ArrayList<TootAccountRef> =
|
|
|
|
|
{ parser, jsonArray -> parser.accountList(jsonArray) }
|
2018-01-19 10:27:35 +01:00
|
|
|
|
) : TootApiResult? {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-08-25 02:29:24 +02:00
|
|
|
|
@Suppress("NON_EXHAUSTIVE_WHEN")
|
|
|
|
|
when(bBottom) {
|
|
|
|
|
false -> when(pagingType) {
|
|
|
|
|
PagingType.Cursor,
|
|
|
|
|
PagingType.None,
|
|
|
|
|
PagingType.Offset -> {
|
|
|
|
|
return TootApiResult("can't refresh top.")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
true -> when(pagingType) {
|
|
|
|
|
PagingType.Cursor -> if(idOld == null) {
|
|
|
|
|
return TootApiResult(context.getString(R.string.end_of_list))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PagingType.None -> {
|
|
|
|
|
return TootApiResult(context.getString(R.string.end_of_list))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val params = misskeyParams ?: makeMisskeyBaseParameter(parser)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val last_since_id = idRecent
|
|
|
|
|
|
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
|
|
|
|
|
|
|
|
|
var result = if(isMisskey) {
|
2018-08-25 02:29:24 +02:00
|
|
|
|
@Suppress("NON_EXHAUSTIVE_WHEN")
|
|
|
|
|
when(pagingType) {
|
|
|
|
|
PagingType.Default -> addRangeMisskey(bBottom, params)
|
|
|
|
|
PagingType.Offset -> params.put("offset", offsetNext)
|
|
|
|
|
PagingType.Cursor -> params.put("cursor", idOld)
|
|
|
|
|
}
|
2018-08-20 02:07:55 +02:00
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
2018-08-18 12:58:14 +02:00
|
|
|
|
} else {
|
|
|
|
|
client.request(addRange(bBottom, path_base))
|
|
|
|
|
}
|
|
|
|
|
val firstResult = result
|
2018-08-20 02:07:55 +02:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
var jsonObject = result?.jsonObject
|
2018-08-24 10:03:20 +02:00
|
|
|
|
if(jsonObject != null) {
|
2018-08-25 02:29:24 +02:00
|
|
|
|
if(pagingType == PagingType.Cursor) {
|
|
|
|
|
idOld = EntityId.mayNull(jsonObject.parseString("next"))
|
|
|
|
|
}
|
2018-08-24 10:03:20 +02:00
|
|
|
|
result !!.data = misskeyArrayFinder(jsonObject)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
}
|
2018-08-20 02:07:55 +02:00
|
|
|
|
|
2018-08-25 02:29:24 +02:00
|
|
|
|
var array = result?.jsonArray
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(array != null) {
|
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
var src = misskeyCustomParser(parser, array)
|
2018-08-25 02:29:24 +02:00
|
|
|
|
@Suppress("NON_EXHAUSTIVE_WHEN")
|
|
|
|
|
when(pagingType) {
|
|
|
|
|
PagingType.Default -> {
|
|
|
|
|
saveRange(bBottom, ! bBottom, firstResult, src)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PagingType.Offset -> {
|
|
|
|
|
offsetNext += src.size
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
list_tmp = addAll(null, src)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(! bBottom) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
if(isMisskey) {
|
|
|
|
|
var bHeadGap = false
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// misskeyの場合、sinceIdを指定したら未読範囲の古い方から読んでしまう
|
|
|
|
|
// 最新まで読めるとは限らない
|
|
|
|
|
// 先頭にギャップを置くかもしれない
|
|
|
|
|
while(true) {
|
|
|
|
|
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("refresh-account-top: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
// 直前のデータが0個なら終了とみなす
|
|
|
|
|
log.d("refresh-account-top: previous size == 0.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("refresh-account-top: timeout.")
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idRecent?.putMisskeySince(params)
|
2018-08-25 02:29:24 +02:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
result = client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
|
|
|
|
|
jsonObject = result?.jsonObject
|
2018-08-24 10:03:20 +02:00
|
|
|
|
if(jsonObject != null) {
|
2018-08-26 04:09:34 +02:00
|
|
|
|
// pagingType is always default.
|
2018-08-25 02:29:24 +02:00
|
|
|
|
result !!.data = misskeyArrayFinder(jsonObject)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
}
|
2018-08-24 10:03:20 +02:00
|
|
|
|
|
2018-08-23 07:45:12 +02:00
|
|
|
|
array = result?.jsonArray
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(array == null) {
|
|
|
|
|
log.d("refresh-account-top: error or cancelled.")
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
src = misskeyCustomParser(parser, array)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
addAll(list_tmp, src)
|
2018-08-26 04:09:34 +02:00
|
|
|
|
|
|
|
|
|
// pagingType is always default.
|
2018-08-18 12:58:14 +02:00
|
|
|
|
saveRange(false, true, result, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-26 04:09:34 +02:00
|
|
|
|
// pagingType is always default.
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(isMisskey && ! bBottom) {
|
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
2018-08-26 04:09:34 +02:00
|
|
|
|
list_tmp?.reverse()
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(! isCancelled
|
|
|
|
|
&& list_tmp?.isNotEmpty() == true
|
|
|
|
|
&& (bHeadGap || Pref.bpForceGap(context))
|
|
|
|
|
) {
|
|
|
|
|
addOneFirst(list_tmp, TootGap.mayNull(null, idRecent))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
} else {
|
|
|
|
|
var bGapAdded = false
|
|
|
|
|
var max_id : EntityId? = null
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("refresh-account-top: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// max_id だけを指定した場合、必ずlimit個のデータが帰ってくるとは限らない
|
|
|
|
|
// 直前のデータが0個なら終了とみなすしかなさそう
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("refresh-account-top: previous size == 0.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
max_id = parseRange(result, src).first
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("refresh-account-top: timeout. make gap.")
|
|
|
|
|
// タイムアウト
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
bGapAdded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val path =
|
|
|
|
|
"$path_base${delimiter}max_id=$max_id&since_id=$last_since_id"
|
|
|
|
|
result = client.request(path)
|
|
|
|
|
|
2018-08-23 07:45:12 +02:00
|
|
|
|
jsonObject = result?.jsonObject
|
2018-08-24 10:03:20 +02:00
|
|
|
|
if(jsonObject != null) {
|
2018-08-23 07:45:12 +02:00
|
|
|
|
result?.data = misskeyArrayFinder(jsonObject)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val jsonArray = result?.jsonArray
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("refresh-account-top: error or cancelled. make gap.")
|
|
|
|
|
// エラー
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
bGapAdded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
src = misskeyCustomParser(parser, jsonArray)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
addAll(list_tmp, src)
|
|
|
|
|
}
|
|
|
|
|
if(Pref.bpForceGap(context) && ! isCancelled && ! bGapAdded && list_tmp?.isNotEmpty() == true) {
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// フィルタがないので下端更新の繰り返しは発生しない
|
|
|
|
|
}
|
|
|
|
|
return firstResult
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun getDomainList(
|
2018-01-19 10:27:35 +01:00
|
|
|
|
client : TootApiClient,
|
|
|
|
|
path_base : String
|
|
|
|
|
) : TootApiResult? {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
if(isMisskey) return TootApiResult("misskey support is not yet implemented.")
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val last_since_id = idRecent
|
|
|
|
|
|
|
|
|
|
var result = client.request(addRange(bBottom, path_base))
|
|
|
|
|
val firstResult = result
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
var jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray != null) {
|
|
|
|
|
var src = TootDomainBlock.parseList(jsonArray)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// ページネーションはサーバ側の内部パラメータで行われる
|
|
|
|
|
saveRange(bBottom, ! bBottom, result, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
list_tmp = addAll(null, src)
|
|
|
|
|
if(! bBottom) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(isMisskey) {
|
|
|
|
|
// Misskey非対応
|
|
|
|
|
} else {
|
|
|
|
|
var bGapAdded = false
|
|
|
|
|
var max_id : EntityId? = null
|
|
|
|
|
while(true) {
|
|
|
|
|
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("refresh-domain-top: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// max_id だけを指定した場合、必ずlimit個のデータが帰ってくるとは限らない
|
|
|
|
|
// 直前のデータが0個なら終了とみなすしかなさそう
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("refresh-domain-top: previous size == 0.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 直前に読んだ範囲のmaxIdを調べる
|
|
|
|
|
max_id = parseRange(result, src).first
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("refresh-domain-top: timeout.")
|
|
|
|
|
|
|
|
|
|
// タイムアウト
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
bGapAdded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val path =
|
|
|
|
|
"$path_base${delimiter}max_id=$max_id&since_id=$last_since_id"
|
|
|
|
|
result = client.request(path)
|
|
|
|
|
jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("refresh-domain-top: error or cancelled.")
|
|
|
|
|
// エラー
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
bGapAdded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = TootDomainBlock.parseList(jsonArray)
|
|
|
|
|
addAll(list_tmp, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(Pref.bpForceGap(context) && ! isCancelled && ! bGapAdded && list_tmp?.isNotEmpty() == true) {
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// フィルタがないので下端更新の繰り返しはない
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
return firstResult
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// fun getListList(client : TootApiClient, path_base : String) : TootApiResult? {
|
|
|
|
|
//
|
|
|
|
|
// if(isMisskey) return TootApiResult("misskey support is not yet implemented.")
|
|
|
|
|
//
|
|
|
|
|
// return TootApiResult("Mastodon's /api/v1/lists has no pagination.")
|
|
|
|
|
// }
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun getReportList(
|
2018-01-19 10:27:35 +01:00
|
|
|
|
client : TootApiClient,
|
|
|
|
|
path_base : String
|
|
|
|
|
) : TootApiResult? {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
if(isMisskey) return TootApiResult("misskey support is not yet implemented.")
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val last_since_id = idRecent
|
|
|
|
|
var result = client.request(addRange(bBottom, path_base))
|
|
|
|
|
val firstResult = result
|
2018-01-04 19:52:25 +01:00
|
|
|
|
var jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray != null) {
|
2018-01-10 16:47:35 +01:00
|
|
|
|
var src = parseList(::TootReport, jsonArray)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
list_tmp = addAll(null, src)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
saveRange(bBottom, ! bBottom, result, src)
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(! bBottom) {
|
2018-01-17 18:39:16 +01:00
|
|
|
|
var bGapAdded = false
|
2018-08-18 12:58:14 +02:00
|
|
|
|
var max_id : EntityId? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
log.d("refresh-report-top: cancelled.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// max_id だけを指定した場合、必ずlimit個のデータが帰ってくるとは限らない
|
|
|
|
|
// 直前のデータが0個なら終了とみなすしかなさそう
|
|
|
|
|
if(src.isEmpty()) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
log.d("refresh-report-top: previous size == 0.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// 直前に読んだ範囲のmaxIdを調べる
|
|
|
|
|
max_id = parseRange(result, src).first
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
log.d("refresh-report-top: timeout. make gap.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// タイムアウト
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
2018-08-18 12:58:14 +02:00
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
2018-01-17 18:39:16 +01:00
|
|
|
|
bGapAdded = true
|
2018-01-04 19:52:25 +01:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
val path =
|
2018-08-18 12:58:14 +02:00
|
|
|
|
"$path_base${delimiter}max_id=$max_id&since_id=$last_since_id"
|
|
|
|
|
result = client.request(path)
|
|
|
|
|
jsonArray = result?.jsonArray
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(jsonArray == null) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
log.d("refresh-report-top: timeout. error or retry. make gap.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// エラー
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
2018-08-18 12:58:14 +02:00
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
2018-01-17 18:39:16 +01:00
|
|
|
|
bGapAdded = true
|
2018-01-04 19:52:25 +01:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
src = parseList(::TootReport, jsonArray)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
addAll(list_tmp, src)
|
|
|
|
|
}
|
2018-01-19 10:27:35 +01:00
|
|
|
|
if(Pref.bpForceGap(context) && ! isCancelled && ! bGapAdded && list_tmp?.isNotEmpty() == true) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
2018-01-17 18:39:16 +01:00
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// レポートにはフィルタがないので下端更新は繰り返さない
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
return firstResult
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun getNotificationList(client : TootApiClient) : TootApiResult? {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-03-21 06:18:19 +01:00
|
|
|
|
val path_base = makeNotificationUrl()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val last_since_id = idRecent
|
|
|
|
|
|
|
|
|
|
val params = makeMisskeyBaseParameter(parser)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
|
|
|
|
|
|
|
|
|
var result = if(isMisskey) {
|
|
|
|
|
addRangeMisskey(bBottom, params)
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
} else {
|
|
|
|
|
client.request(addRange(bBottom, path_base))
|
|
|
|
|
}
|
|
|
|
|
val firstResult = result
|
2018-01-04 19:52:25 +01:00
|
|
|
|
var jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray != null) {
|
|
|
|
|
var src = parser.notificationList(jsonArray)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
list_tmp = addWithFilterNotification(null, src)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
saveRange(bBottom, ! bBottom, result, src)
|
|
|
|
|
|
|
|
|
|
if(! src.isEmpty()) {
|
2018-08-25 02:54:17 +02:00
|
|
|
|
PollingWorker.injectData(context, access_info, src)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
if(! bBottom) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// 頭の方を読む時は隙間を減らすため、フィルタの有無に関係なく繰り返しを行う
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(isMisskey) {
|
|
|
|
|
// misskey ではsinceIdを指定すると古い方から読める
|
|
|
|
|
// 先頭にギャップを追加するかもしれない
|
|
|
|
|
var bHeadGap = false
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
while(true) {
|
|
|
|
|
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("refresh-notification-top: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// max_id だけを指定した場合、必ずlimit個のデータが帰ってくるとは限らない
|
|
|
|
|
// 直前のデータが0個なら終了とみなすしかなさそう
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("refresh-notification-top: previous size == 0.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("refresh-notification-top: timeout. make gap.")
|
|
|
|
|
// タイムアウト
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idRecent?.putMisskeySince(params)
|
|
|
|
|
result = client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("refresh-notification-top: error or cancelled. make gap.")
|
|
|
|
|
// エラー
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = parser.notificationList(jsonArray)
|
|
|
|
|
|
|
|
|
|
saveRange(false, true, result, src)
|
|
|
|
|
if(! src.isEmpty()) {
|
|
|
|
|
addWithFilterNotification(list_tmp, src)
|
2018-08-25 02:54:17 +02:00
|
|
|
|
PollingWorker.injectData(context, access_info, src)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(isMisskey && ! bBottom) {
|
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
2018-08-26 04:09:34 +02:00
|
|
|
|
list_tmp?.reverse()
|
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(! isCancelled
|
|
|
|
|
&& list_tmp?.isNotEmpty() == true
|
|
|
|
|
&& (bHeadGap || Pref.bpForceGap(context))
|
|
|
|
|
) {
|
|
|
|
|
addOneFirst(list_tmp, TootGap.mayNull(null, idRecent))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
} else {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
var bGapAdded = false
|
|
|
|
|
var max_id : EntityId? = null
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("refresh-notification-offset: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// max_id だけを指定した場合、必ずlimit個のデータが帰ってくるとは限らない
|
|
|
|
|
// 直前のデータが0個なら終了とみなすしかなさそう
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("refresh-notification-offset: previous size == 0.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
max_id = parseRange(result, src).first
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("refresh-notification-offset: timeout. make gap.")
|
|
|
|
|
// タイムアウト
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
bGapAdded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val path =
|
|
|
|
|
"$path_base${delimiter}max_id=$max_id&since_id=$last_since_id"
|
|
|
|
|
result = client.request(path)
|
|
|
|
|
jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("refresh-notification-offset: error or cancelled. make gap.")
|
|
|
|
|
// エラー
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
bGapAdded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = parser.notificationList(jsonArray)
|
|
|
|
|
if(! src.isEmpty()) {
|
|
|
|
|
addWithFilterNotification(list_tmp, src)
|
2018-08-25 02:54:17 +02:00
|
|
|
|
PollingWorker.injectData(context, access_info, src)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(Pref.bpForceGap(context) && ! isCancelled && ! bGapAdded && list_tmp?.isNotEmpty() == true) {
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} else {
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("refresh-notification-bottom: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// bottomの場合、フィルタなしなら繰り返さない
|
|
|
|
|
if(! isFilterEnabled) {
|
|
|
|
|
log.d("refresh-notification-bottom: isFiltered is false.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// max_id だけを指定した場合、必ずlimit個のデータが帰ってくるとは限らない
|
|
|
|
|
// 直前のデータが0個なら終了とみなすしかなさそう
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("refresh-notification-bottom: previous size == 0.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 十分読んだらそれで終了
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
log.d("refresh-notification-bottom: read enough data.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
// タイムアウト
|
|
|
|
|
log.d("refresh-notification-bottom: loop timeout.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
result = if(isMisskey) {
|
|
|
|
|
idOld?.putMisskeyUntil(params)
|
|
|
|
|
client.request(path_base)
|
|
|
|
|
} else {
|
|
|
|
|
val path = path_base + delimiter + "max_id=" + idOld
|
|
|
|
|
client.request(path)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
jsonArray = result?.jsonArray
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("refresh-notification-bottom: error or cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = parser.notificationList(jsonArray)
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
addWithFilterNotification(list_tmp, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(! saveRangeEnd(result, src)) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
log.d("refresh-notification-bottom: saveRangeEnd failed.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
return firstResult
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
fun getConversationSummaryList(
|
2018-01-19 10:27:35 +01:00
|
|
|
|
client : TootApiClient,
|
2018-08-16 21:58:30 +02:00
|
|
|
|
path_base : String,
|
2018-10-01 02:10:16 +02:00
|
|
|
|
aroundMin : Boolean = false,
|
2018-08-26 04:09:34 +02:00
|
|
|
|
misskeyParams : JSONObject? = null,
|
2018-10-09 01:20:43 +02:00
|
|
|
|
misskeyCustomParser : (parser : TootParser, jsonArray : JSONArray) -> ArrayList<TootConversationSummary> =
|
2018-10-30 20:29:00 +01:00
|
|
|
|
{ parser, jsonArray -> parseList(::TootConversationSummary, parser, jsonArray) }
|
2018-01-19 10:27:35 +01:00
|
|
|
|
) : TootApiResult? {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val isMisskey = access_info.isMisskey
|
|
|
|
|
|
|
|
|
|
val params = misskeyParams ?: makeMisskeyTimelineParameter(parser)
|
2018-08-16 21:58:30 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
|
|
|
|
|
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val last_since_id = idRecent
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
var result = when {
|
|
|
|
|
isMisskey -> {
|
|
|
|
|
addRangeMisskey(bBottom, params)
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
aroundMin -> client.request(addRangeMin(path_base))
|
|
|
|
|
else -> client.request(addRange(bBottom, path_base))
|
2018-08-16 21:58:30 +02:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val firstResult = result
|
2018-08-16 21:58:30 +02:00
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
val jsonArray = result?.jsonArray
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(jsonArray != null) {
|
2018-08-24 16:38:32 +02:00
|
|
|
|
var src = misskeyCustomParser(parser, jsonArray)
|
2018-08-26 04:09:34 +02:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
saveRange(bBottom, ! bBottom, result, src)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
list_tmp = addWithFilterConversationSummary(null, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(! bBottom) {
|
|
|
|
|
if(isMisskey) {
|
|
|
|
|
// Misskeyの場合はsinceIdを指定しても取得できるのは未読のうち古い範囲に偏る
|
|
|
|
|
var bHeadGap = false
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-top: cancelled.")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 頭の方を読む時は隙間を減らすため、フィルタの有無に関係なく繰り返しを行う
|
|
|
|
|
|
|
|
|
|
// 直前のデータが0個なら終了とみなす
|
|
|
|
|
if(src.isEmpty()) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-top: previous size == 0.")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-top: read enough. make gap.")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-top: timeout. make gap.")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idRecent?.putMisskeySince(params)
|
|
|
|
|
result =
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
val jsonArray2 = result?.jsonArray
|
|
|
|
|
if(jsonArray2 == null) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-top: error or cancelled. make gap.")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
src = misskeyCustomParser(parser, jsonArray2)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
saveRange(false, true, result, src)
|
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
addWithFilterConversationSummary(list_tmp, src)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
if(isMisskey && ! bBottom) {
|
2018-08-26 04:09:34 +02:00
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
|
|
|
|
list_tmp?.reverse()
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
if(! isCancelled
|
|
|
|
|
&& list_tmp?.isNotEmpty() == true
|
|
|
|
|
&& (bHeadGap || Pref.bpForceGap(context))
|
|
|
|
|
) {
|
|
|
|
|
addOneFirst(list_tmp, TootGap.mayNull(null, idRecent))
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
} else if(aroundMin) {
|
|
|
|
|
while(true) {
|
|
|
|
|
|
|
|
|
|
saveRangeStart(result, src)
|
|
|
|
|
|
|
|
|
|
if(isCancelled) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-aroundMin: cancelled.")
|
2018-10-01 02:10:16 +02:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 頭の方を読む時は隙間を減らすため、フィルタの有無に関係なく繰り返しを行う
|
|
|
|
|
|
|
|
|
|
// 直前のデータが0個なら終了とみなすしかなさそう
|
|
|
|
|
if(src.isEmpty()) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-aroundMin: previous size == 0.")
|
2018-10-01 02:10:16 +02:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-aroundMin: read enough.")
|
2018-10-01 02:10:16 +02:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-aroundMin: timeout.")
|
2018-10-01 02:10:16 +02:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val path = "$path_base${delimiter}min_id=$idRecent"
|
|
|
|
|
result = client.request(path)
|
|
|
|
|
|
|
|
|
|
val jsonArray2 = result?.jsonArray
|
|
|
|
|
if(jsonArray2 == null) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-aroundMin: error or cancelled.")
|
2018-10-01 02:10:16 +02:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = misskeyCustomParser(parser, jsonArray2)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
addWithFilterConversationSummary(list_tmp, src)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
} else {
|
|
|
|
|
var bGapAdded = false
|
|
|
|
|
var max_id : EntityId? = null
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-top: cancelled.")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 頭の方を読む時は隙間を減らすため、フィルタの有無に関係なく繰り返しを行う
|
|
|
|
|
|
|
|
|
|
// max_id だけを指定した場合、必ずlimit個のデータが帰ってくるとは限らない
|
|
|
|
|
// 直前のデータが0個なら終了とみなすしかなさそう
|
|
|
|
|
if(src.isEmpty()) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-top: previous size == 0.")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
max_id = parseRange(result, src).first
|
|
|
|
|
|
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-top: read enough. make gap.")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
bGapAdded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-top: timeout. make gap.")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// タイムアウト
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
bGapAdded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val path =
|
|
|
|
|
"$path_base${delimiter}max_id=$max_id&since_id=$last_since_id"
|
|
|
|
|
result = client.request(path)
|
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
val jsonArray2 = result?.jsonArray
|
|
|
|
|
if(jsonArray2 == null) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-top: error or cancelled. make gap.")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// エラー
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
bGapAdded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
src = misskeyCustomParser(parser, jsonArray2)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
addWithFilterConversationSummary(list_tmp, src)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(Pref.bpForceGap(context) && ! isCancelled && ! bGapAdded && list_tmp?.isNotEmpty() == true) {
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-bottom: cancelled.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// bottomの場合、フィルタなしなら繰り返さない
|
|
|
|
|
if(! isFilterEnabled) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-bottom: isFiltered is false.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// max_id だけを指定した場合、必ずlimit個のデータが帰ってくるとは限らない
|
|
|
|
|
// 直前のデータが0個なら終了とみなすしかなさそう
|
|
|
|
|
if(src.isEmpty()) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-bottom: previous size == 0.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 十分読んだらそれで終了
|
2018-01-10 16:47:35 +01:00
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-bottom: read enough data.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
// タイムアウト
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-bottom: loop timeout.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
result = if(isMisskey) {
|
|
|
|
|
idOld?.putMisskeyUntil(params)
|
2018-08-16 21:58:30 +02:00
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
} else {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val path = "$path_base${delimiter}max_id=$idOld"
|
2018-08-16 21:58:30 +02:00
|
|
|
|
client.request(path)
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
val jsonArray2 = result?.jsonArray
|
|
|
|
|
if(jsonArray2 == null) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-bottom: error or cancelled.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
src = misskeyCustomParser(parser, jsonArray2)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
addWithFilterConversationSummary(list_tmp, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(! saveRangeEnd(result, src)) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
log.d("refresh-ConversationSummary-bottom: saveRangeEnd failed.")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
break
|
|
|
|
|
}
|
2018-01-17 18:39:16 +01:00
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
return firstResult
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
fun getStatusList(
|
|
|
|
|
client : TootApiClient,
|
|
|
|
|
path_base : String,
|
|
|
|
|
aroundMin : Boolean = false,
|
|
|
|
|
misskeyParams : JSONObject? = null,
|
|
|
|
|
misskeyCustomParser : (parser : TootParser, jsonArray : JSONArray) -> ArrayList<TootStatus> =
|
|
|
|
|
{ parser, jsonArray -> parser.statusList(jsonArray) }
|
|
|
|
|
) : TootApiResult? {
|
2018-04-20 15:22:21 +02:00
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
val isMisskey = access_info.isMisskey
|
|
|
|
|
|
|
|
|
|
val params = misskeyParams ?: makeMisskeyTimelineParameter(parser)
|
|
|
|
|
|
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
|
|
|
|
|
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
|
|
|
|
val last_since_id = idRecent
|
|
|
|
|
|
|
|
|
|
var result = when {
|
|
|
|
|
isMisskey -> {
|
|
|
|
|
addRangeMisskey(bBottom, params)
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
aroundMin -> client.request(addRangeMin(path_base))
|
|
|
|
|
else -> client.request(addRange(bBottom, path_base))
|
|
|
|
|
}
|
|
|
|
|
val firstResult = result
|
|
|
|
|
|
|
|
|
|
val jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray != null) {
|
|
|
|
|
var src = misskeyCustomParser(parser, jsonArray)
|
2018-07-06 17:22:22 +02:00
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
saveRange(bBottom, ! bBottom, result, src)
|
|
|
|
|
list_tmp = addWithFilterStatus(null, src)
|
|
|
|
|
|
|
|
|
|
if(! bBottom) {
|
|
|
|
|
if(isMisskey) {
|
|
|
|
|
// Misskeyの場合はsinceIdを指定しても取得できるのは未読のうち古い範囲に偏る
|
|
|
|
|
var bHeadGap = false
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("refresh-status-top: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 頭の方を読む時は隙間を減らすため、フィルタの有無に関係なく繰り返しを行う
|
|
|
|
|
|
|
|
|
|
// 直前のデータが0個なら終了とみなす
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("refresh-status-top: previous size == 0.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
|
|
|
|
log.d("refresh-status-top: read enough. make gap.")
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("refresh-status-top: timeout. make gap.")
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
idRecent?.putMisskeySince(params)
|
|
|
|
|
result =
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
|
|
|
|
|
val jsonArray2 = result?.jsonArray
|
|
|
|
|
if(jsonArray2 == null) {
|
|
|
|
|
log.d("refresh-status-top: error or cancelled. make gap.")
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = misskeyCustomParser(parser, jsonArray2)
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
&& (bHeadGap || Pref.bpForceGap(context))
|
|
|
|
|
) {
|
|
|
|
|
addOneFirst(list_tmp, TootGap.mayNull(null, idRecent))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if(aroundMin) {
|
|
|
|
|
while(true) {
|
|
|
|
|
|
|
|
|
|
saveRangeStart(result, src)
|
|
|
|
|
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("refresh-status-aroundMin: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 頭の方を読む時は隙間を減らすため、フィルタの有無に関係なく繰り返しを行う
|
|
|
|
|
|
|
|
|
|
// 直前のデータが0個なら終了とみなすしかなさそう
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("refresh-status-aroundMin: previous size == 0.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
|
|
|
|
log.d("refresh-status-aroundMin: read enough.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("refresh-status-aroundMin: timeout.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val path = "$path_base${delimiter}min_id=$idRecent"
|
|
|
|
|
result = client.request(path)
|
|
|
|
|
|
|
|
|
|
val jsonArray2 = result?.jsonArray
|
|
|
|
|
if(jsonArray2 == null) {
|
|
|
|
|
log.d("refresh-status-aroundMin: error or cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = misskeyCustomParser(parser, jsonArray2)
|
|
|
|
|
addWithFilterStatus(list_tmp, src)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
var bGapAdded = false
|
|
|
|
|
var max_id : EntityId? = null
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("refresh-status-top: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 頭の方を読む時は隙間を減らすため、フィルタの有無に関係なく繰り返しを行う
|
|
|
|
|
|
|
|
|
|
// max_id だけを指定した場合、必ずlimit個のデータが帰ってくるとは限らない
|
|
|
|
|
// 直前のデータが0個なら終了とみなすしかなさそう
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("refresh-status-top: previous size == 0.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
max_id = parseRange(result, src).first
|
|
|
|
|
|
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
|
|
|
|
log.d("refresh-status-top: read enough. make gap.")
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
bGapAdded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("refresh-status-top: timeout. make gap.")
|
|
|
|
|
// タイムアウト
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
bGapAdded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val path =
|
|
|
|
|
"$path_base${delimiter}max_id=$max_id&since_id=$last_since_id"
|
|
|
|
|
result = client.request(path)
|
|
|
|
|
|
|
|
|
|
val jsonArray2 = result?.jsonArray
|
|
|
|
|
if(jsonArray2 == null) {
|
|
|
|
|
log.d("refresh-status-top: error or cancelled. make gap.")
|
|
|
|
|
// エラー
|
|
|
|
|
// 隙間ができるかもしれない。後ほど手動で試してもらうしかない
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, last_since_id))
|
|
|
|
|
bGapAdded = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("refresh-status-bottom: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// bottomの場合、フィルタなしなら繰り返さない
|
|
|
|
|
if(! isFilterEnabled) {
|
|
|
|
|
log.d("refresh-status-bottom: isFiltered is false.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// max_id だけを指定した場合、必ずlimit個のデータが帰ってくるとは限らない
|
|
|
|
|
// 直前のデータが0個なら終了とみなすしかなさそう
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("refresh-status-bottom: previous size == 0.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 十分読んだらそれで終了
|
|
|
|
|
if((list_tmp?.size ?: 0) >= LOOP_READ_ENOUGH) {
|
|
|
|
|
log.d("refresh-status-bottom: read enough data.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
// タイムアウト
|
|
|
|
|
log.d("refresh-status-bottom: loop timeout.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = if(isMisskey) {
|
|
|
|
|
idOld?.putMisskeyUntil(params)
|
|
|
|
|
client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
} else {
|
|
|
|
|
val path = "$path_base${delimiter}max_id=$idOld"
|
|
|
|
|
client.request(path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val jsonArray2 = result?.jsonArray
|
|
|
|
|
if(jsonArray2 == null) {
|
|
|
|
|
log.d("refresh-status-bottom: error or cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
src = misskeyCustomParser(parser, jsonArray2)
|
|
|
|
|
addWithFilterStatus(list_tmp, src)
|
|
|
|
|
|
|
|
|
|
if(! saveRangeEnd(result, src)) {
|
|
|
|
|
log.d("refresh-status-bottom: saveRangeEnd failed.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return firstResult
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var filterUpdated = false
|
|
|
|
|
|
|
|
|
|
override fun doInBackground(vararg unused : Void) : TootApiResult? {
|
|
|
|
|
ctStarted.set(true)
|
|
|
|
|
|
|
|
|
|
val client = TootApiClient(context, callback = object : TootApiCallback {
|
|
|
|
|
override val isApiCancelled : Boolean
|
|
|
|
|
get() = isCancelled || is_dispose.get()
|
|
|
|
|
|
|
|
|
|
override fun publishApiProgress(s : String) {
|
|
|
|
|
runOnMainLooper {
|
|
|
|
|
if(isCancelled) return@runOnMainLooper
|
|
|
|
|
task_progress = s
|
|
|
|
|
fireShowContent(reason = "refresh progress", changeList = ArrayList())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
client.account = access_info
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
|
|
if(! bBottom) {
|
|
|
|
|
val filterList = loadFilter2(client)
|
|
|
|
|
if(filterList != null) {
|
|
|
|
|
muted_word2 = encodeFilterTree(filterList)
|
|
|
|
|
filterUpdated = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return when(column_type) {
|
|
|
|
|
|
|
|
|
|
TYPE_DIRECT_MESSAGES -> if(useConversationSummarys) {
|
|
|
|
|
// try 2.6.0 new API https://github.com/tootsuite/mastodon/pull/8832
|
|
|
|
|
getConversationSummaryList(client, PATH_DIRECT_MESSAGES2)
|
2018-10-30 20:29:00 +01:00
|
|
|
|
} else {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
// fallback to old api
|
|
|
|
|
getStatusList(client, PATH_DIRECT_MESSAGES)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TYPE_LOCAL -> getStatusList(client, makePublicLocalUrl())
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
TYPE_LOCAL_AROUND -> {
|
|
|
|
|
if(bBottom) {
|
|
|
|
|
// 通常と同じ
|
|
|
|
|
getStatusList(client, makePublicLocalUrl())
|
|
|
|
|
} else {
|
|
|
|
|
val rv =
|
|
|
|
|
getStatusList(client, makePublicLocalUrl(), aroundMin = true)
|
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
|
|
|
|
list_tmp?.reverse()
|
|
|
|
|
rv
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TYPE_FEDERATED_AROUND -> {
|
|
|
|
|
if(bBottom) {
|
|
|
|
|
// 通常と同じ
|
|
|
|
|
getStatusList(client, makePublicFederateUrl())
|
|
|
|
|
} else {
|
|
|
|
|
val rv =
|
|
|
|
|
getStatusList(client, makePublicFederateUrl(), aroundMin = true)
|
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
|
|
|
|
list_tmp?.reverse()
|
|
|
|
|
rv
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
TYPE_ACCOUNT_AROUND -> {
|
2018-10-01 02:10:16 +02:00
|
|
|
|
var s = String.format(
|
|
|
|
|
Locale.JAPAN,
|
|
|
|
|
PATH_ACCOUNT_STATUSES,
|
|
|
|
|
profile_id
|
|
|
|
|
)
|
|
|
|
|
if(with_attachment && ! with_highlight) s += "&only_media=1"
|
|
|
|
|
getStatusList(client, s)
|
|
|
|
|
|
|
|
|
|
if(bBottom) {
|
|
|
|
|
getStatusList(client, s)
|
|
|
|
|
} else {
|
|
|
|
|
val rv =
|
|
|
|
|
getStatusList(client, s, aroundMin = true)
|
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
|
|
|
|
list_tmp?.reverse()
|
|
|
|
|
rv
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_MISSKEY_HYBRID -> getStatusList(client, makeMisskeyHybridTlUrl())
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
TYPE_FEDERATE -> getStatusList(client, makePublicFederateUrl())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
TYPE_FAVOURITES -> if(isMisskey) {
|
|
|
|
|
getStatusList(
|
|
|
|
|
client
|
2018-08-25 02:29:24 +02:00
|
|
|
|
, PATH_MISSKEY_FAVORITES
|
2018-08-24 16:38:32 +02:00
|
|
|
|
, misskeyParams = makeMisskeyTimelineParameter(parser)
|
|
|
|
|
, misskeyCustomParser = misskeyCustomParserFavorites
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
getStatusList(client, PATH_FAVOURITES)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_REPORTS -> getReportList(client, PATH_REPORTS)
|
|
|
|
|
|
2018-03-01 07:41:07 +01:00
|
|
|
|
TYPE_NOTIFICATIONS -> getNotificationList(client)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
TYPE_BOOSTED_BY -> getAccountList(
|
|
|
|
|
client, String.format(
|
|
|
|
|
Locale.JAPAN, PATH_BOOSTED_BY,
|
|
|
|
|
posted_status_id
|
|
|
|
|
)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
TYPE_FAVOURITED_BY -> getAccountList(
|
|
|
|
|
client, String.format(
|
|
|
|
|
Locale.JAPAN, PATH_FAVOURITED_BY,
|
|
|
|
|
posted_status_id
|
|
|
|
|
)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_PROFILE -> {
|
2018-10-30 20:29:00 +01:00
|
|
|
|
loadProfileAccount(client, parser, false)
|
2018-05-20 00:31:59 +02:00
|
|
|
|
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
when(profile_tab) {
|
2018-08-20 02:07:55 +02:00
|
|
|
|
TAB_FOLLOWING -> if(isMisskey) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
getAccountList(
|
|
|
|
|
client,
|
|
|
|
|
PATH_MISSKEY_PROFILE_FOLLOWING,
|
|
|
|
|
misskeyParams = makeMisskeyParamsUserId(parser),
|
2018-08-24 18:56:04 +02:00
|
|
|
|
misskeyArrayFinder = misskeyArrayFinderUsers
|
2018-08-18 12:58:14 +02:00
|
|
|
|
)
|
2018-08-20 02:07:55 +02:00
|
|
|
|
} else {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
getAccountList(
|
|
|
|
|
client,
|
2018-08-20 02:07:55 +02:00
|
|
|
|
String.format(
|
|
|
|
|
Locale.JAPAN,
|
|
|
|
|
PATH_ACCOUNT_FOLLOWING,
|
|
|
|
|
profile_id
|
|
|
|
|
)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
TAB_FOLLOWERS -> if(isMisskey) {
|
|
|
|
|
getAccountList(
|
|
|
|
|
client,
|
|
|
|
|
PATH_MISSKEY_PROFILE_FOLLOWERS,
|
|
|
|
|
misskeyParams = makeMisskeyParamsUserId(parser),
|
2018-08-24 18:56:04 +02:00
|
|
|
|
misskeyArrayFinder = misskeyArrayFinderUsers
|
2018-08-20 02:07:55 +02:00
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
getAccountList(
|
|
|
|
|
client,
|
|
|
|
|
String.format(
|
|
|
|
|
Locale.JAPAN,
|
|
|
|
|
PATH_ACCOUNT_FOLLOWERS,
|
|
|
|
|
profile_id
|
2018-01-19 10:27:35 +01:00
|
|
|
|
)
|
2018-08-20 02:07:55 +02:00
|
|
|
|
)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
else -> if(isMisskey) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
getStatusList(
|
|
|
|
|
client,
|
|
|
|
|
PATH_MISSKEY_PROFILE_STATUSES,
|
|
|
|
|
misskeyParams = makeMisskeyParamsProfileStatuses(parser)
|
|
|
|
|
)
|
2018-08-20 02:07:55 +02:00
|
|
|
|
} else {
|
2018-11-20 10:33:13 +01:00
|
|
|
|
var s = String.format(
|
|
|
|
|
Locale.JAPAN,
|
|
|
|
|
PATH_ACCOUNT_STATUSES,
|
|
|
|
|
profile_id
|
|
|
|
|
)
|
|
|
|
|
if(with_attachment && ! with_highlight) s += "&only_media=1"
|
|
|
|
|
getStatusList(client, s)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
TYPE_LIST_LIST -> {
|
|
|
|
|
TootApiResult("list API does not support refresh loading.")
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_LIST_TL -> {
|
|
|
|
|
loadListInfo(client, false)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(isMisskey) {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
val params = makeMisskeyTimelineParameter(parser)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
.put("listId", profile_id)
|
|
|
|
|
getStatusList(client, makeListTlUrl(), misskeyParams = params)
|
|
|
|
|
} else {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
getStatusList(client, makeListTlUrl())
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TYPE_LIST_MEMBER -> {
|
|
|
|
|
loadListInfo(client, false)
|
2018-01-19 10:27:35 +01:00
|
|
|
|
getAccountList(
|
|
|
|
|
client,
|
|
|
|
|
String.format(Locale.JAPAN, PATH_LIST_MEMBER, profile_id)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-25 02:29:24 +02:00
|
|
|
|
TYPE_MUTES -> if(isMisskey) {
|
2018-08-24 18:56:04 +02:00
|
|
|
|
getAccountList(
|
|
|
|
|
client
|
2018-08-25 02:29:24 +02:00
|
|
|
|
, PATH_MISSKEY_MUTES
|
2018-08-24 18:56:04 +02:00
|
|
|
|
, misskeyParams = access_info.putMisskeyApiToken(JSONObject())
|
2018-08-25 02:29:24 +02:00
|
|
|
|
, misskeyArrayFinder = misskeyArrayFinderUsers
|
2018-08-24 18:56:04 +02:00
|
|
|
|
)
|
2018-08-25 02:29:24 +02:00
|
|
|
|
} else {
|
2018-08-24 18:56:04 +02:00
|
|
|
|
getAccountList(client, PATH_MUTES)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-11-01 13:48:54 +01:00
|
|
|
|
TYPE_BLOCKS -> if(isMisskey) {
|
|
|
|
|
pagingType = PagingType.Default
|
|
|
|
|
val params = access_info.putMisskeyApiToken(JSONObject())
|
|
|
|
|
getAccountList(
|
|
|
|
|
client,
|
|
|
|
|
"/api/blocking/list",
|
|
|
|
|
misskeyParams = params,
|
|
|
|
|
misskeyCustomParser = misskeyCustomParserBlocks
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
getAccountList(client, PATH_BLOCKS)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_DOMAIN_BLOCKS -> getDomainList(client, PATH_DOMAIN_BLOCK)
|
|
|
|
|
|
2018-08-21 12:19:02 +02:00
|
|
|
|
TYPE_FOLLOW_REQUESTS -> if(isMisskey) {
|
|
|
|
|
getAccountList(
|
|
|
|
|
client
|
2018-08-25 02:29:24 +02:00
|
|
|
|
, PATH_MISSKEY_FOLLOW_REQUESTS
|
2018-08-21 12:19:02 +02:00
|
|
|
|
, misskeyParams = access_info.putMisskeyApiToken(JSONObject())
|
2018-08-24 10:03:20 +02:00
|
|
|
|
, misskeyCustomParser = misskeyCustomParserFollowRequest
|
2018-08-21 12:19:02 +02:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
getAccountList(client, PATH_FOLLOW_REQUESTS)
|
|
|
|
|
}
|
2018-08-25 02:29:24 +02:00
|
|
|
|
TYPE_FOLLOW_SUGGESTION -> if(isMisskey) {
|
|
|
|
|
getAccountList(
|
|
|
|
|
client
|
|
|
|
|
, PATH_MISSKEY_FOLLOW_SUGGESTION
|
|
|
|
|
, misskeyParams = access_info.putMisskeyApiToken(JSONObject())
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
getAccountList(client, PATH_FOLLOW_SUGGESTION)
|
|
|
|
|
}
|
2018-08-28 14:25:45 +02:00
|
|
|
|
TYPE_ENDORSEMENT -> getAccountList(client, PATH_ENDORSEMENT)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_HASHTAG -> if(isMisskey) {
|
|
|
|
|
getStatusList(
|
|
|
|
|
client
|
|
|
|
|
, makeHashtagUrl(hashtag)
|
|
|
|
|
, misskeyParams = makeMisskeyTimelineParameter(parser)
|
|
|
|
|
.put("tag", hashtag)
|
2018-08-24 16:38:32 +02:00
|
|
|
|
.put("limit", MISSKEY_HASHTAG_LIMIT)
|
2018-08-24 10:03:20 +02:00
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
getStatusList(client, makeHashtagUrl(hashtag))
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_SEARCH_MSP ->
|
|
|
|
|
if(! bBottom) {
|
|
|
|
|
TootApiResult("head of list.")
|
|
|
|
|
} else {
|
|
|
|
|
val result : TootApiResult?
|
|
|
|
|
val q = search_query.trim { it <= ' ' }
|
|
|
|
|
if(q.isEmpty()) {
|
|
|
|
|
list_tmp = ArrayList()
|
|
|
|
|
result = TootApiResult(context.getString(R.string.end_of_list))
|
|
|
|
|
} else {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
result = client.searchMsp(search_query, idOld?.toString())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val jsonArray = result?.jsonArray
|
|
|
|
|
if(jsonArray != null) {
|
|
|
|
|
// max_id の更新
|
2018-08-18 12:58:14 +02:00
|
|
|
|
idOld = EntityId.mayNull(
|
|
|
|
|
TootApiClient.getMspMaxId(
|
|
|
|
|
jsonArray,
|
|
|
|
|
idOld?.toString()
|
|
|
|
|
)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// リストデータの用意
|
2018-01-21 17:47:13 +01:00
|
|
|
|
parser.serviceType = ServiceType.MSP
|
2018-02-04 18:55:42 +01:00
|
|
|
|
list_tmp = addWithFilterStatus(
|
|
|
|
|
list_tmp,
|
|
|
|
|
parser.statusList(jsonArray)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TYPE_SEARCH_TS -> if(! bBottom) {
|
|
|
|
|
TootApiResult("head of list.")
|
|
|
|
|
} else {
|
|
|
|
|
val result : TootApiResult?
|
|
|
|
|
val q = search_query.trim { it <= ' ' }
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(q.isEmpty() || idOld == null) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
list_tmp = ArrayList()
|
|
|
|
|
result = TootApiResult(context.getString(R.string.end_of_list))
|
|
|
|
|
} else {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
result = client.searchTootsearch(search_query, idOld?.toLong())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val jsonObject = result?.jsonObject
|
|
|
|
|
if(jsonObject != null) {
|
|
|
|
|
// max_id の更新
|
2018-08-18 12:58:14 +02:00
|
|
|
|
idOld = EntityId.mayNull(
|
|
|
|
|
TootApiClient.getTootsearchMaxId(
|
|
|
|
|
jsonObject,
|
|
|
|
|
idOld?.toLong()
|
|
|
|
|
)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// リストデータの用意
|
2018-01-19 10:27:35 +01:00
|
|
|
|
val search_result =
|
|
|
|
|
TootStatus.parseListTootsearch(parser, jsonObject)
|
2018-01-10 16:47:35 +01:00
|
|
|
|
list_tmp = addWithFilterStatus(list_tmp, search_result)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
else -> getStatusList(client, makeHomeTlUrl())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
try {
|
2018-08-21 12:19:02 +02:00
|
|
|
|
updateRelation(client, list_tmp, who_account, parser)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
}
|
2018-04-21 01:16:44 +02:00
|
|
|
|
ctClosed.set(true)
|
2018-05-04 15:35:01 +02:00
|
|
|
|
runOnMainLooperDelayed(333L) {
|
|
|
|
|
if(! isCancelled) fireShowColumnStatus()
|
2018-04-21 01:16:44 +02:00
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
override fun onCancelled(result : TootApiResult?) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
onPostExecute(null)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onPostExecute(result : TootApiResult?) {
|
|
|
|
|
if(is_dispose.get()) return
|
|
|
|
|
|
|
|
|
|
if(isCancelled || result == null) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
try {
|
2018-04-21 01:16:44 +02:00
|
|
|
|
lastTask = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
bRefreshLoading = false
|
|
|
|
|
|
2018-07-06 17:22:22 +02:00
|
|
|
|
if(filterUpdated) {
|
|
|
|
|
checkFiltersForListData(muted_word2)
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val error = result.error
|
|
|
|
|
if(error != null) {
|
|
|
|
|
mRefreshLoadingError = error
|
2018-08-04 20:07:55 +02:00
|
|
|
|
mRefreshLoadingErrorTime = SystemClock.elapsedRealtime()
|
2018-01-20 17:59:38 +01:00
|
|
|
|
fireShowContent(reason = "refresh error", changeList = ArrayList())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val list_new = duplicate_map.filterDuplicate(list_tmp)
|
2018-01-20 17:59:38 +01:00
|
|
|
|
if(list_new.isEmpty()) {
|
|
|
|
|
fireShowContent(
|
|
|
|
|
reason = "refresh list_new is empty",
|
|
|
|
|
changeList = ArrayList()
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 事前にスクロール位置を覚えておく
|
|
|
|
|
var sp : ScrollPosition? = null
|
|
|
|
|
val holder = viewHolder
|
|
|
|
|
if(holder != null) {
|
|
|
|
|
sp = holder.scrollPosition
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-11 13:17:36 +01:00
|
|
|
|
|
2018-01-20 07:51:14 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(bBottom) {
|
2018-01-20 17:59:38 +01:00
|
|
|
|
val changeList = listOf(
|
|
|
|
|
AdapterChange(
|
|
|
|
|
AdapterChangeType.RangeInsert,
|
|
|
|
|
list_data.size,
|
2018-11-11 13:17:36 +01:00
|
|
|
|
list_new.size
|
2018-01-20 17:59:38 +01:00
|
|
|
|
)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
list_data.addAll(list_new)
|
2018-01-20 17:59:38 +01:00
|
|
|
|
fireShowContent(reason = "refresh updated bottom", changeList = changeList)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
// 新着が少しだけ見えるようにスクロール位置を移動する
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(sp != null) {
|
2018-01-10 16:47:35 +01:00
|
|
|
|
holder?.setScrollPosition(sp, 20f)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val changeList = ArrayList<AdapterChange>()
|
|
|
|
|
|
|
|
|
|
if(list_data.isNotEmpty() && list_data[0] is TootGap) {
|
|
|
|
|
changeList.add(AdapterChange(AdapterChangeType.RangeRemove, 0, 1))
|
|
|
|
|
list_data.removeAt(0)
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
for(o in list_new) {
|
|
|
|
|
if(o is TootStatus) {
|
2018-01-10 16:47:35 +01:00
|
|
|
|
val highlight_sound = o.highlight_sound
|
|
|
|
|
if(highlight_sound != null) {
|
|
|
|
|
App1.sound(highlight_sound)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-30 20:29:00 +01:00
|
|
|
|
replaceConversationSummary(changeList, list_new, list_data)
|
2018-11-18 03:38:52 +01:00
|
|
|
|
|
2018-11-11 13:17:36 +01:00
|
|
|
|
val added = list_new.size // may 0
|
2018-10-28 14:08:10 +01:00
|
|
|
|
|
2018-01-20 07:51:14 +01:00
|
|
|
|
// 投稿後のリフレッシュなら当該投稿の位置を探す
|
2018-01-04 19:52:25 +01:00
|
|
|
|
var status_index = - 1
|
2018-01-20 17:59:38 +01:00
|
|
|
|
for(i in 0 until added) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val o = list_new[i]
|
2018-01-20 07:51:14 +01:00
|
|
|
|
if(o is TootStatus && o.id == posted_status_id) {
|
|
|
|
|
status_index = i
|
|
|
|
|
break
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
changeList.add(AdapterChange(AdapterChangeType.RangeInsert, 0, added))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
list_data.addAll(0, list_new)
|
2018-01-20 17:59:38 +01:00
|
|
|
|
fireShowContent(reason = "refresh updated head", changeList = changeList)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
if(status_index >= 0 && refresh_after_toot == Pref.RAT_REFRESH_SCROLL) {
|
2018-01-19 10:27:35 +01:00
|
|
|
|
// 投稿後にその投稿にスクロールする
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(holder != null) {
|
2018-01-19 10:27:35 +01:00
|
|
|
|
holder.setScrollPosition(
|
2018-12-12 15:21:38 +01:00
|
|
|
|
ScrollPosition(toAdapterIndex(status_index)),
|
2018-01-19 10:27:35 +01:00
|
|
|
|
0f
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} else {
|
2018-12-12 15:21:38 +01:00
|
|
|
|
scroll_save = ScrollPosition(toAdapterIndex(status_index))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2018-01-19 10:27:35 +01:00
|
|
|
|
//
|
2018-01-10 16:47:35 +01:00
|
|
|
|
val scroll_save = this@Column.scroll_save
|
2018-01-04 19:52:25 +01:00
|
|
|
|
when {
|
2018-08-16 21:58:30 +02:00
|
|
|
|
// ViewHolderがある場合は増加件数分+deltaの位置にスクロールする
|
2018-01-04 19:52:25 +01:00
|
|
|
|
sp != null -> {
|
2018-01-19 10:27:35 +01:00
|
|
|
|
sp.adapterIndex += added
|
|
|
|
|
val delta = if(bSilent) 0f else - 20f
|
2018-01-04 19:52:25 +01:00
|
|
|
|
holder?.setScrollPosition(sp, delta)
|
|
|
|
|
}
|
2018-08-16 21:58:30 +02:00
|
|
|
|
// ViewHolderがなくて保存中の位置がある場合、増加件数分ずらす。deltaは難しいので反映しない
|
2018-01-19 10:27:35 +01:00
|
|
|
|
scroll_save != null -> scroll_save.adapterIndex += added
|
2018-08-16 21:58:30 +02:00
|
|
|
|
// 保存中の位置がない場合、保存中の位置を新しく作る
|
2018-01-19 10:27:35 +01:00
|
|
|
|
else -> this@Column.scroll_save =
|
2018-12-12 15:21:38 +01:00
|
|
|
|
ScrollPosition(toAdapterIndex(added))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-03 15:21:42 +01:00
|
|
|
|
|
|
|
|
|
updateMisskeyCapture()
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} finally {
|
2018-04-20 15:22:21 +02:00
|
|
|
|
fireShowColumnStatus()
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(! bBottom) {
|
|
|
|
|
bRefreshingTop = false
|
|
|
|
|
resumeStreaming(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-21 01:16:44 +02:00
|
|
|
|
this.lastTask = task
|
2018-01-04 19:52:25 +01:00
|
|
|
|
task.executeOnExecutor(App1.task_executor)
|
2018-04-21 01:16:44 +02:00
|
|
|
|
fireShowColumnStatus()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal fun startGap(gap : TootGap?) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(gap == null) {
|
2018-01-21 13:46:36 +01:00
|
|
|
|
showToast(context, true, "gap is null")
|
2018-01-04 19:52:25 +01:00
|
|
|
|
return
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-04-21 01:16:44 +02:00
|
|
|
|
if(lastTask != null) {
|
2018-01-21 13:46:36 +01:00
|
|
|
|
showToast(context, true, R.string.column_is_busy)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-03 15:21:42 +01:00
|
|
|
|
@Suppress("UNNECESSARY_SAFE_CALL")
|
2018-11-02 14:28:09 +01:00
|
|
|
|
viewHolder?.refreshLayout?.isRefreshing = true
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
bRefreshLoading = true
|
|
|
|
|
mRefreshLoadingError = ""
|
|
|
|
|
|
|
|
|
|
val task = @SuppressLint("StaticFieldLeak")
|
2018-05-04 15:35:01 +02:00
|
|
|
|
object : ColumnTask(ColumnTaskType.GAP) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
var max_id : EntityId? = gap.max_id
|
|
|
|
|
var since_id : EntityId? = gap.since_id
|
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
var list_tmp : ArrayList<TimelineItem>? = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
var parser = TootParser(context, access_info, highlightTrie = highlight_trie)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun getAccountList(
|
2018-11-01 13:48:54 +01:00
|
|
|
|
client : TootApiClient,
|
|
|
|
|
path_base : String,
|
|
|
|
|
misskeyParams : JSONObject? = null,
|
2018-08-25 02:29:24 +02:00
|
|
|
|
misskeyCustomParser : (parser : TootParser, jsonArray : JSONArray) -> ArrayList<TootAccountRef> =
|
2018-11-01 13:48:54 +01:00
|
|
|
|
{ parser, jsonArray -> parser.accountList(jsonArray) },
|
2018-08-25 02:29:24 +02:00
|
|
|
|
misskeyArrayFinder : (jsonObject : JSONObject) -> JSONArray? = { null }
|
2018-08-23 07:45:12 +02:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
) : TootApiResult? {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-08-25 02:29:24 +02:00
|
|
|
|
@Suppress("NON_EXHAUSTIVE_WHEN")
|
|
|
|
|
when(pagingType) {
|
|
|
|
|
PagingType.Offset,
|
|
|
|
|
PagingType.Cursor,
|
|
|
|
|
PagingType.None -> {
|
|
|
|
|
return TootApiResult("can't support gap")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
val params = misskeyParams ?: makeMisskeyBaseParameter(parser)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
|
|
|
|
list_tmp = ArrayList()
|
|
|
|
|
|
|
|
|
|
var result : TootApiResult? = null
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
if(isMisskey) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// missKeyではgapを下から読む
|
|
|
|
|
var bHeadGap = false
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
while(true) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("gap-account: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(result != null && SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("gap-account: timeout. make gap.")
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
since_id?.putMisskeySince(params)
|
|
|
|
|
val r2 = client.request(path_base, params.toPostRequestBuilder())
|
2018-08-23 07:45:12 +02:00
|
|
|
|
|
2018-08-24 18:56:04 +02:00
|
|
|
|
val jsonObject = r2?.jsonObject
|
2018-08-25 02:29:24 +02:00
|
|
|
|
if(jsonObject != null) {
|
2018-08-24 18:56:04 +02:00
|
|
|
|
r2.data = misskeyArrayFinder(jsonObject)
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val jsonArray = r2?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
2018-08-23 07:45:12 +02:00
|
|
|
|
log.d("gap-account: error. make gap.")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(result == null) result = r2
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
result = r2
|
2018-08-23 07:45:12 +02:00
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
val src = misskeyCustomParser(parser, jsonArray)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("gap-account: empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addAll(list_tmp, src)
|
|
|
|
|
since_id = parseRange(result, src).second
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(isMisskey) {
|
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
2018-08-26 04:09:34 +02:00
|
|
|
|
list_tmp?.reverse()
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(bHeadGap) {
|
|
|
|
|
addOneFirst(list_tmp, TootGap.mayNull(max_id, since_id))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
} else {
|
|
|
|
|
while(true) {
|
|
|
|
|
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("gap-account: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(result != null && SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("gap-account: timeout. make gap.")
|
|
|
|
|
// タイムアウト
|
|
|
|
|
// 隙間が残る
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, since_id))
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val path = "$path_base${delimiter}max_id=$max_id&since_id=$since_id"
|
|
|
|
|
val r2 = client.request(path)
|
|
|
|
|
|
|
|
|
|
val jsonArray = r2?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("gap-account: error timeout. make gap.")
|
|
|
|
|
|
|
|
|
|
if(result == null) result = r2
|
|
|
|
|
|
|
|
|
|
// 隙間が残る
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, since_id))
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
result = r2
|
2018-08-24 10:03:20 +02:00
|
|
|
|
val src = misskeyCustomParser(parser, jsonArray)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("gap-account: empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addAll(list_tmp, src)
|
|
|
|
|
max_id = parseRange(result, src).first
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun getReportList(
|
2018-01-19 10:27:35 +01:00
|
|
|
|
client : TootApiClient,
|
|
|
|
|
path_base : String
|
|
|
|
|
) : TootApiResult? {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val params = makeMisskeyBaseParameter(parser)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
|
|
|
|
list_tmp = ArrayList()
|
|
|
|
|
|
|
|
|
|
var result : TootApiResult? = null
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
if(isMisskey) {
|
|
|
|
|
var bHeadGap = false
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("gap-report: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(result != null && SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("gap-report: timeout. make gap.")
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
since_id?.putMisskeySince(params)
|
|
|
|
|
val r2 = client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
val jsonArray = r2?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("gap-report: error or cancelled. make gap.")
|
|
|
|
|
if(result == null) result = r2
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = r2
|
|
|
|
|
val src = parseList(::TootReport, jsonArray)
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("gap-report: empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addAll(list_tmp, src)
|
|
|
|
|
|
|
|
|
|
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである
|
|
|
|
|
since_id = parseRange(result, src).second
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-26 04:09:34 +02:00
|
|
|
|
|
|
|
|
|
// レポート一覧ってそもそもMisskey対応してないので、ここをどうするかは不明
|
|
|
|
|
// 多分 sinceIDによるページングではないと思う
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(isMisskey) {
|
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
2018-08-26 04:09:34 +02:00
|
|
|
|
list_tmp?.reverse()
|
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(bHeadGap) {
|
|
|
|
|
addOneFirst(list_tmp, TootGap.mayNull(max_id, since_id))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
} else {
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("gap-report: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(result != null && SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("gap-report: timeout. make gap.")
|
|
|
|
|
// タイムアウト
|
|
|
|
|
// 隙間が残る
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, since_id))
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val path =
|
|
|
|
|
path_base + delimiter + "max_id=" + max_id + "&since_id=" + since_id
|
|
|
|
|
val r2 = client.request(path)
|
|
|
|
|
val jsonArray = r2?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("gap-report: error or cancelled. make gap.")
|
|
|
|
|
if(result == null) result = r2
|
|
|
|
|
// 隙間が残る
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, since_id))
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = r2
|
|
|
|
|
val src = parseList(::TootReport, jsonArray)
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("gap-report: empty.")
|
|
|
|
|
// コレ以上取得する必要はない
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addAll(list_tmp, src)
|
|
|
|
|
|
|
|
|
|
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである
|
|
|
|
|
max_id = parseRange(result, src).first
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun getNotificationList(client : TootApiClient) : TootApiResult? {
|
2018-03-21 06:18:19 +01:00
|
|
|
|
val path_base = makeNotificationUrl()
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val params = makeMisskeyBaseParameter(parser)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
2018-03-01 07:41:07 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
list_tmp = ArrayList()
|
|
|
|
|
|
|
|
|
|
var result : TootApiResult? = null
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
if(isMisskey) {
|
|
|
|
|
var bHeadGap = false
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("gap-notification: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(result != null && SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("gap-notification: timeout. make gap.")
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
since_id?.putMisskeySince(params)
|
|
|
|
|
val r2 = client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
val jsonArray = r2?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
// エラー
|
|
|
|
|
log.d("gap-notification: error or response. make gap.")
|
|
|
|
|
if(result == null) result = r2
|
|
|
|
|
// 隙間が残る
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = r2
|
|
|
|
|
val src = parser.notificationList(jsonArray)
|
|
|
|
|
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("gap-notification: empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである
|
|
|
|
|
since_id = parseRange(result, src).second
|
|
|
|
|
|
|
|
|
|
addWithFilterNotification(list_tmp, src)
|
|
|
|
|
|
2018-08-25 02:54:17 +02:00
|
|
|
|
PollingWorker.injectData(context, access_info, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(isMisskey) {
|
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
2018-08-26 04:09:34 +02:00
|
|
|
|
list_tmp?.reverse()
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(bHeadGap) {
|
|
|
|
|
addOneFirst(list_tmp, TootGap.mayNull(max_id, since_id))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
} else {
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("gap-notification: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(result != null && SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("gap-notification: timeout. make gap.")
|
|
|
|
|
// タイムアウト
|
|
|
|
|
// 隙間が残る
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, since_id))
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
val path =
|
|
|
|
|
path_base + delimiter + "max_id=" + max_id + "&since_id=" + since_id
|
|
|
|
|
val r2 = client.request(path)
|
|
|
|
|
val jsonArray = r2?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
// エラー
|
|
|
|
|
log.d("gap-notification: error or response. make gap.")
|
|
|
|
|
|
|
|
|
|
if(result == null) result = r2
|
|
|
|
|
|
|
|
|
|
// 隙間が残る
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, since_id))
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = r2
|
|
|
|
|
val src = parser.notificationList(jsonArray)
|
|
|
|
|
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
log.d("gap-notification: empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである
|
|
|
|
|
max_id = parseRange(result, src).first
|
|
|
|
|
|
|
|
|
|
addWithFilterNotification(list_tmp, src)
|
|
|
|
|
|
2018-08-25 02:54:17 +02:00
|
|
|
|
PollingWorker.injectData(context, access_info, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-05 14:38:48 +02:00
|
|
|
|
fun getStatusList(
|
2018-01-19 10:27:35 +01:00
|
|
|
|
client : TootApiClient,
|
2018-08-20 02:07:55 +02:00
|
|
|
|
path_base : String,
|
|
|
|
|
misskeyParams : JSONObject? = null
|
2018-08-24 16:38:32 +02:00
|
|
|
|
,
|
|
|
|
|
misskeyCustomParser : (parser : TootParser, jsonArray : JSONArray) -> ArrayList<TootStatus> =
|
|
|
|
|
{ parser, jsonArray -> parser.statusList(jsonArray) }
|
2018-01-19 10:27:35 +01:00
|
|
|
|
) : TootApiResult? {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
val isMisskey = access_info.isMisskey
|
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
val params = misskeyParams ?: makeMisskeyTimelineParameter(parser)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
list_tmp = ArrayList()
|
|
|
|
|
|
|
|
|
|
var result : TootApiResult? = null
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(isMisskey) {
|
|
|
|
|
var bHeadGap = false
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("gap-statuses: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(result != null && SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("gap-statuses: timeout.")
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
since_id?.putMisskeySince(params)
|
|
|
|
|
val r2 = client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
|
|
|
|
|
val jsonArray = r2?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("gap-statuses: error or cancelled. make gap.")
|
|
|
|
|
|
|
|
|
|
// 成功データがない場合だけ、今回のエラーを返すようにする
|
|
|
|
|
if(result == null) result = r2
|
|
|
|
|
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 成功した場合はそれを返したい
|
|
|
|
|
result = r2
|
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
val src = misskeyCustomParser(parser, jsonArray)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
// 直前の取得でカラのデータが帰ってきたら終了
|
|
|
|
|
log.d("gap-statuses: empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-08-26 04:09:34 +02:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである
|
|
|
|
|
since_id = parseRange(result, src).second
|
|
|
|
|
|
|
|
|
|
addWithFilterStatus(list_tmp, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-10-01 02:10:16 +02:00
|
|
|
|
|
|
|
|
|
if(isMisskey) {
|
2018-08-26 04:09:34 +02:00
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
|
|
|
|
list_tmp?.reverse()
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(bHeadGap) {
|
|
|
|
|
addOneFirst(list_tmp, TootGap.mayNull(max_id, since_id))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
} else {
|
|
|
|
|
var bLastGap = false
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("gap-statuses: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(result != null && SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("gap-statuses: timeout.")
|
|
|
|
|
// タイムアウト
|
|
|
|
|
bLastGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val path = "${path_base}${delimiter}max_id=${max_id}&since_id=${since_id}"
|
|
|
|
|
val r2 = client.request(path)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val jsonArray = r2?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("gap-statuses: error or cancelled. make gap.")
|
|
|
|
|
|
|
|
|
|
// 成功データがない場合だけ、今回のエラーを返すようにする
|
|
|
|
|
if(result == null) result = r2
|
|
|
|
|
|
|
|
|
|
bLastGap = true
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 成功した場合はそれを返したい
|
|
|
|
|
result = r2
|
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
val src = misskeyCustomParser(parser, jsonArray)
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
// 直前の取得でカラのデータが帰ってきたら終了
|
|
|
|
|
log.d("gap-statuses: empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである
|
|
|
|
|
max_id = parseRange(result, src).first
|
|
|
|
|
|
|
|
|
|
addWithFilterStatus(list_tmp, src)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
if(bLastGap) {
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, since_id))
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
fun getConversationSummaryList(
|
|
|
|
|
client : TootApiClient,
|
|
|
|
|
path_base : String,
|
|
|
|
|
misskeyParams : JSONObject? = null
|
|
|
|
|
,
|
|
|
|
|
misskeyCustomParser : (parser : TootParser, jsonArray : JSONArray) -> ArrayList<TootConversationSummary> =
|
2018-10-30 20:29:00 +01:00
|
|
|
|
{ parser, jsonArray -> parseList(::TootConversationSummary, parser, jsonArray) }
|
2018-10-09 01:20:43 +02:00
|
|
|
|
) : TootApiResult? {
|
|
|
|
|
|
|
|
|
|
val isMisskey = access_info.isMisskey
|
|
|
|
|
|
|
|
|
|
val params = misskeyParams ?: makeMisskeyTimelineParameter(parser)
|
|
|
|
|
|
|
|
|
|
val time_start = SystemClock.elapsedRealtime()
|
|
|
|
|
|
|
|
|
|
val delimiter = if(- 1 != path_base.indexOf('?')) '&' else '?'
|
|
|
|
|
|
|
|
|
|
list_tmp = ArrayList()
|
|
|
|
|
|
|
|
|
|
var result : TootApiResult? = null
|
|
|
|
|
if(isMisskey) {
|
|
|
|
|
var bHeadGap = false
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("gap-statuses: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(result != null && SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("gap-statuses: timeout.")
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
since_id?.putMisskeySince(params)
|
|
|
|
|
val r2 = client.request(path_base, params.toPostRequestBuilder())
|
|
|
|
|
|
|
|
|
|
val jsonArray = r2?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("gap-statuses: error or cancelled. make gap.")
|
|
|
|
|
|
|
|
|
|
// 成功データがない場合だけ、今回のエラーを返すようにする
|
|
|
|
|
if(result == null) result = r2
|
|
|
|
|
|
|
|
|
|
bHeadGap = true
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 成功した場合はそれを返したい
|
|
|
|
|
result = r2
|
|
|
|
|
|
|
|
|
|
val src = misskeyCustomParser(parser, jsonArray)
|
|
|
|
|
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
// 直前の取得でカラのデータが帰ってきたら終了
|
|
|
|
|
log.d("gap-statuses: empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである
|
|
|
|
|
since_id = parseRange(result, src).second
|
|
|
|
|
|
|
|
|
|
addWithFilterConversationSummary(list_tmp, src)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(isMisskey) {
|
|
|
|
|
list_tmp?.sortBy { it.getOrderId() }
|
|
|
|
|
list_tmp?.reverse()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(bHeadGap) {
|
|
|
|
|
addOneFirst(list_tmp, TootGap.mayNull(max_id, since_id))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
var bLastGap = false
|
|
|
|
|
while(true) {
|
|
|
|
|
if(isCancelled) {
|
|
|
|
|
log.d("gap-statuses: cancelled.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(result != null && SystemClock.elapsedRealtime() - time_start > LOOP_TIMEOUT) {
|
|
|
|
|
log.d("gap-statuses: timeout.")
|
|
|
|
|
// タイムアウト
|
|
|
|
|
bLastGap = true
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val path = "${path_base}${delimiter}max_id=${max_id}&since_id=${since_id}"
|
|
|
|
|
val r2 = client.request(path)
|
|
|
|
|
|
|
|
|
|
val jsonArray = r2?.jsonArray
|
|
|
|
|
if(jsonArray == null) {
|
|
|
|
|
log.d("gap-statuses: error or cancelled. make gap.")
|
|
|
|
|
|
|
|
|
|
// 成功データがない場合だけ、今回のエラーを返すようにする
|
|
|
|
|
if(result == null) result = r2
|
|
|
|
|
|
|
|
|
|
bLastGap = true
|
|
|
|
|
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 成功した場合はそれを返したい
|
|
|
|
|
result = r2
|
|
|
|
|
|
|
|
|
|
val src = misskeyCustomParser(parser, jsonArray)
|
|
|
|
|
|
|
|
|
|
if(src.isEmpty()) {
|
|
|
|
|
// 直前の取得でカラのデータが帰ってきたら終了
|
|
|
|
|
log.d("gap-statuses: empty.")
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 隙間の最新のステータスIDは取得データ末尾のステータスIDである
|
|
|
|
|
max_id = parseRange(result, src).first
|
|
|
|
|
|
|
|
|
|
addWithFilterConversationSummary(list_tmp, src)
|
|
|
|
|
}
|
|
|
|
|
if(bLastGap) {
|
|
|
|
|
addOne(list_tmp, TootGap.mayNull(max_id, since_id))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-07 07:51:59 +02:00
|
|
|
|
override fun doInBackground(vararg unused : Void) : TootApiResult? {
|
2018-04-21 01:16:44 +02:00
|
|
|
|
ctStarted.set(true)
|
2018-04-20 15:22:21 +02:00
|
|
|
|
|
2018-01-17 02:16:26 +01:00
|
|
|
|
val client = TootApiClient(context, callback = object : TootApiCallback {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
override val isApiCancelled : Boolean
|
|
|
|
|
get() = isCancelled || is_dispose.get()
|
|
|
|
|
|
|
|
|
|
override fun publishApiProgress(s : String) {
|
2018-01-21 13:46:36 +01:00
|
|
|
|
runOnMainLooper {
|
|
|
|
|
if(isCancelled) return@runOnMainLooper
|
2018-01-04 19:52:25 +01:00
|
|
|
|
task_progress = s
|
2018-01-20 17:59:38 +01:00
|
|
|
|
fireShowContent(reason = "gap progress", changeList = ArrayList())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2018-01-12 10:01:25 +01:00
|
|
|
|
client.account = access_info
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
return when(column_type) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-03-10 01:28:53 +01:00
|
|
|
|
TYPE_LOCAL -> getStatusList(client, makePublicLocalUrl())
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_MISSKEY_HYBRID -> getStatusList(client, makeMisskeyHybridTlUrl())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-03-10 01:28:53 +01:00
|
|
|
|
TYPE_FEDERATE -> getStatusList(client, makePublicFederateUrl())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
TYPE_LIST_TL -> if(isMisskey) {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
val params = makeMisskeyTimelineParameter(parser)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
.put("listId", profile_id)
|
|
|
|
|
getStatusList(client, makeListTlUrl(), misskeyParams = params)
|
|
|
|
|
} else {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
getStatusList(client, makeListTlUrl())
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-24 16:38:32 +02:00
|
|
|
|
TYPE_FAVOURITES -> if(isMisskey) {
|
|
|
|
|
getStatusList(
|
|
|
|
|
client,
|
2018-08-25 02:29:24 +02:00
|
|
|
|
PATH_MISSKEY_FAVORITES
|
2018-08-24 16:38:32 +02:00
|
|
|
|
, misskeyParams = makeMisskeyTimelineParameter(parser)
|
|
|
|
|
, misskeyCustomParser = misskeyCustomParserFavorites
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
getStatusList(client, PATH_FAVOURITES)
|
|
|
|
|
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
TYPE_REPORTS -> getReportList(client, PATH_REPORTS)
|
|
|
|
|
|
2018-03-01 07:41:07 +01:00
|
|
|
|
TYPE_NOTIFICATIONS -> getNotificationList(client)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_HASHTAG -> if(isMisskey) {
|
|
|
|
|
getStatusList(
|
|
|
|
|
client
|
|
|
|
|
, makeHashtagUrl(hashtag)
|
|
|
|
|
, misskeyParams = makeMisskeyTimelineParameter(parser)
|
|
|
|
|
.put("tag", hashtag)
|
2018-08-24 16:38:32 +02:00
|
|
|
|
.put("limit", MISSKEY_HASHTAG_LIMIT)
|
2018-08-24 10:03:20 +02:00
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
getStatusList(client, makeHashtagUrl(hashtag))
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
TYPE_BOOSTED_BY -> getAccountList(
|
|
|
|
|
client,
|
|
|
|
|
String.format(Locale.JAPAN, PATH_BOOSTED_BY, status_id)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
TYPE_FAVOURITED_BY -> getAccountList(
|
|
|
|
|
client,
|
|
|
|
|
String.format(Locale.JAPAN, PATH_FAVOURITED_BY, status_id)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-25 02:29:24 +02:00
|
|
|
|
TYPE_MUTES -> if(isMisskey) {
|
2018-08-24 18:56:04 +02:00
|
|
|
|
getAccountList(
|
|
|
|
|
client
|
2018-08-25 02:29:24 +02:00
|
|
|
|
, PATH_MISSKEY_MUTES
|
2018-08-24 18:56:04 +02:00
|
|
|
|
, misskeyParams = access_info.putMisskeyApiToken(JSONObject())
|
|
|
|
|
|
2018-08-25 02:29:24 +02:00
|
|
|
|
, misskeyArrayFinder = misskeyArrayFinderUsers
|
2018-08-24 18:56:04 +02:00
|
|
|
|
)
|
2018-08-25 02:29:24 +02:00
|
|
|
|
} else {
|
2018-08-24 18:56:04 +02:00
|
|
|
|
getAccountList(client, PATH_MUTES)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-11-01 13:48:54 +01:00
|
|
|
|
TYPE_BLOCKS -> if(isMisskey) {
|
|
|
|
|
pagingType = PagingType.Default
|
|
|
|
|
val params = access_info.putMisskeyApiToken(JSONObject())
|
|
|
|
|
getAccountList(
|
|
|
|
|
client,
|
|
|
|
|
"/api/blocking/list",
|
|
|
|
|
misskeyParams = params,
|
|
|
|
|
misskeyCustomParser = misskeyCustomParserBlocks
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
getAccountList(client, PATH_BLOCKS)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_FOLLOW_REQUESTS -> if(isMisskey) {
|
2018-08-23 07:45:12 +02:00
|
|
|
|
getAccountList(
|
|
|
|
|
client
|
2018-08-25 02:29:24 +02:00
|
|
|
|
, PATH_MISSKEY_FOLLOW_REQUESTS
|
2018-08-23 07:45:12 +02:00
|
|
|
|
, misskeyParams = access_info.putMisskeyApiToken(JSONObject())
|
2018-08-24 10:03:20 +02:00
|
|
|
|
, misskeyCustomParser = misskeyCustomParserFollowRequest
|
2018-08-23 07:45:12 +02:00
|
|
|
|
)
|
2018-08-24 10:03:20 +02:00
|
|
|
|
} else {
|
2018-08-23 07:45:12 +02:00
|
|
|
|
getAccountList(
|
|
|
|
|
client
|
|
|
|
|
, PATH_FOLLOW_REQUESTS
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-08-25 02:29:24 +02:00
|
|
|
|
TYPE_FOLLOW_SUGGESTION -> if(isMisskey) {
|
|
|
|
|
getAccountList(
|
|
|
|
|
client
|
|
|
|
|
, PATH_MISSKEY_FOLLOW_SUGGESTION
|
|
|
|
|
, misskeyParams = access_info.putMisskeyApiToken(JSONObject())
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
getAccountList(
|
|
|
|
|
client
|
|
|
|
|
, PATH_FOLLOW_SUGGESTION
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-28 14:25:45 +02:00
|
|
|
|
TYPE_ENDORSEMENT -> getAccountList(client, PATH_ENDORSEMENT)
|
|
|
|
|
|
2018-12-26 05:47:16 +01:00
|
|
|
|
TYPE_PROFILE -> when(profile_tab) {
|
2018-05-20 00:31:59 +02:00
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
TAB_FOLLOWING -> if(isMisskey) {
|
|
|
|
|
getAccountList(
|
|
|
|
|
client
|
|
|
|
|
, PATH_MISSKEY_PROFILE_FOLLOWING
|
|
|
|
|
, misskeyParams = makeMisskeyParamsUserId(parser)
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
getAccountList(
|
|
|
|
|
client,
|
|
|
|
|
String.format(Locale.JAPAN, PATH_ACCOUNT_FOLLOWING, profile_id)
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
TAB_FOLLOWERS -> if(isMisskey) {
|
|
|
|
|
getAccountList(
|
|
|
|
|
client
|
|
|
|
|
, PATH_MISSKEY_PROFILE_FOLLOWERS
|
|
|
|
|
, misskeyParams = makeMisskeyParamsUserId(parser)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
getAccountList(
|
|
|
|
|
client,
|
|
|
|
|
String.format(Locale.JAPAN, PATH_ACCOUNT_FOLLOWERS, profile_id)
|
|
|
|
|
)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
else -> if(isMisskey) {
|
|
|
|
|
getStatusList(
|
|
|
|
|
client
|
|
|
|
|
, PATH_MISSKEY_PROFILE_STATUSES
|
|
|
|
|
, misskeyParams = makeMisskeyParamsProfileStatuses(parser)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} else {
|
2018-08-20 02:07:55 +02:00
|
|
|
|
if(access_info.isPseudo) {
|
|
|
|
|
client.request(PATH_INSTANCE)
|
|
|
|
|
} else {
|
|
|
|
|
var s =
|
|
|
|
|
String.format(
|
|
|
|
|
Locale.JAPAN,
|
|
|
|
|
PATH_ACCOUNT_STATUSES,
|
|
|
|
|
profile_id
|
|
|
|
|
)
|
|
|
|
|
if(with_attachment && ! with_highlight) s += "&only_media=1"
|
|
|
|
|
getStatusList(client, s)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-30 20:29:00 +01:00
|
|
|
|
TYPE_DIRECT_MESSAGES -> if(useConversationSummarys) {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
// try 2.6.0 new API https://github.com/tootsuite/mastodon/pull/8832
|
|
|
|
|
getConversationSummaryList(client, PATH_DIRECT_MESSAGES2)
|
2018-10-30 20:29:00 +01:00
|
|
|
|
} else {
|
2018-10-09 01:20:43 +02:00
|
|
|
|
// fallback to old api
|
|
|
|
|
getStatusList(client, PATH_DIRECT_MESSAGES)
|
|
|
|
|
}
|
2018-04-18 14:38:17 +02:00
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
else -> getStatusList(client, makeHomeTlUrl())
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
try {
|
2018-08-21 12:19:02 +02:00
|
|
|
|
updateRelation(client, list_tmp, who_account, parser)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.trace(ex)
|
|
|
|
|
}
|
2018-05-04 15:35:01 +02:00
|
|
|
|
|
2018-04-21 01:16:44 +02:00
|
|
|
|
ctClosed.set(true)
|
2018-05-04 15:35:01 +02:00
|
|
|
|
runOnMainLooperDelayed(333L) {
|
|
|
|
|
if(! isCancelled) fireShowColumnStatus()
|
2018-04-21 01:16:44 +02:00
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-10 16:47:35 +01:00
|
|
|
|
override fun onCancelled(result : TootApiResult?) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
onPostExecute(null)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onPostExecute(result : TootApiResult?) {
|
|
|
|
|
if(is_dispose.get()) return
|
|
|
|
|
|
|
|
|
|
if(isCancelled || result == null) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 15:22:21 +02:00
|
|
|
|
try {
|
|
|
|
|
|
2018-04-21 01:16:44 +02:00
|
|
|
|
lastTask = null
|
2018-04-20 15:22:21 +02:00
|
|
|
|
bRefreshLoading = false
|
|
|
|
|
|
|
|
|
|
val error = result.error
|
|
|
|
|
if(error != null) {
|
|
|
|
|
mRefreshLoadingError = error
|
|
|
|
|
fireShowContent(reason = "gap error", changeList = ArrayList())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val list_tmp = this.list_tmp
|
|
|
|
|
if(list_tmp == null) {
|
|
|
|
|
fireShowContent(reason = "gap list_tmp is null", changeList = ArrayList())
|
|
|
|
|
return
|
|
|
|
|
}
|
2018-10-30 20:29:00 +01:00
|
|
|
|
|
2018-10-28 14:08:10 +01:00
|
|
|
|
val list_new = duplicate_map.filterDuplicate(list_tmp)
|
2018-04-20 15:22:21 +02:00
|
|
|
|
// 0個でもギャップを消すために以下の処理を続ける
|
|
|
|
|
|
2018-10-28 14:08:10 +01:00
|
|
|
|
val changeList = ArrayList<AdapterChange>()
|
|
|
|
|
|
2018-10-30 20:29:00 +01:00
|
|
|
|
replaceConversationSummary(changeList, list_new, list_data)
|
2018-10-28 14:08:10 +01:00
|
|
|
|
|
2018-11-11 13:17:36 +01:00
|
|
|
|
val added = list_new.size // may 0
|
|
|
|
|
|
2018-10-28 14:08:10 +01:00
|
|
|
|
val position = list_data.indexOf(gap)
|
|
|
|
|
if(position == - 1) {
|
|
|
|
|
log.d("gap not found..")
|
|
|
|
|
fireShowContent(reason = "gap not found", changeList = ArrayList())
|
|
|
|
|
return
|
|
|
|
|
}
|
2018-04-20 15:22:21 +02:00
|
|
|
|
|
|
|
|
|
// idx番目の要素がListViewのtopから何ピクセル下にあるか
|
|
|
|
|
var restore_idx = position + 1
|
|
|
|
|
var restore_y = 0
|
|
|
|
|
val holder = viewHolder
|
|
|
|
|
if(holder != null) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
try {
|
2018-12-12 15:21:38 +01:00
|
|
|
|
restore_y = holder.getListItemOffset(restore_idx)
|
2018-04-20 15:22:21 +02:00
|
|
|
|
} catch(ex : IndexOutOfBoundsException) {
|
|
|
|
|
restore_idx = position
|
|
|
|
|
try {
|
2018-12-12 15:21:38 +01:00
|
|
|
|
restore_y = holder.getListItemOffset(restore_idx)
|
2018-04-20 15:22:21 +02:00
|
|
|
|
} catch(ex2 : IndexOutOfBoundsException) {
|
|
|
|
|
restore_idx = - 1
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-30 20:29:00 +01:00
|
|
|
|
|
2018-04-20 15:22:21 +02:00
|
|
|
|
list_data.removeAt(position)
|
|
|
|
|
list_data.addAll(position, list_new)
|
|
|
|
|
|
|
|
|
|
changeList.add(AdapterChange(AdapterChangeType.RangeRemove, position))
|
|
|
|
|
if(added > 0) {
|
|
|
|
|
changeList.add(
|
|
|
|
|
AdapterChange(
|
|
|
|
|
AdapterChangeType.RangeInsert,
|
|
|
|
|
position,
|
|
|
|
|
added
|
|
|
|
|
)
|
|
|
|
|
)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-04-20 15:22:21 +02:00
|
|
|
|
fireShowContent(reason = "gap updated", changeList = changeList)
|
|
|
|
|
|
|
|
|
|
if(holder != null) {
|
|
|
|
|
if(restore_idx >= 0) {
|
|
|
|
|
// ギャップが画面内にあるなら
|
|
|
|
|
holder.setListItemTop(restore_idx + added - 1, restore_y)
|
|
|
|
|
} else {
|
|
|
|
|
// ギャップが画面内にない場合、何もしない
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
val scroll_save = this@Column.scroll_save
|
|
|
|
|
if(scroll_save != null) {
|
|
|
|
|
scroll_save.adapterIndex += added - 1
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
2018-11-03 15:21:42 +01:00
|
|
|
|
|
|
|
|
|
updateMisskeyCapture()
|
2018-04-20 15:22:21 +02:00
|
|
|
|
} finally {
|
|
|
|
|
fireShowColumnStatus()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-21 01:16:44 +02:00
|
|
|
|
this.lastTask = task
|
2018-01-04 19:52:25 +01:00
|
|
|
|
task.executeOnExecutor(App1.task_executor)
|
2018-04-21 01:16:44 +02:00
|
|
|
|
fireShowColumnStatus()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
enum class HeaderType(val viewType : Int) {
|
2018-01-17 18:39:16 +01:00
|
|
|
|
Profile(1),
|
|
|
|
|
Search(2),
|
|
|
|
|
Instance(3),
|
2018-07-07 07:15:16 +02:00
|
|
|
|
Filter(4),
|
2018-01-17 18:39:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
val headerType : HeaderType?
|
|
|
|
|
get() = when(column_type) {
|
2018-01-17 18:39:16 +01:00
|
|
|
|
Column.TYPE_PROFILE -> HeaderType.Profile
|
|
|
|
|
Column.TYPE_SEARCH -> HeaderType.Search
|
|
|
|
|
Column.TYPE_SEARCH_MSP -> HeaderType.Search
|
|
|
|
|
Column.TYPE_SEARCH_TS -> HeaderType.Search
|
|
|
|
|
Column.TYPE_INSTANCE_INFORMATION -> HeaderType.Instance
|
2018-07-07 07:15:16 +02:00
|
|
|
|
Column.TYPE_KEYWORD_FILTER -> HeaderType.Filter
|
2018-01-17 18:39:16 +01:00
|
|
|
|
else -> null
|
|
|
|
|
}
|
2018-01-19 10:27:35 +01:00
|
|
|
|
|
|
|
|
|
fun toAdapterIndex(listIndex : Int) : Int {
|
|
|
|
|
return if(headerType != null) listIndex + 1 else listIndex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun toListIndex(adapterIndex : Int) : Int {
|
|
|
|
|
return if(headerType != null) adapterIndex - 1 else adapterIndex
|
2018-01-17 18:39:16 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun loadSearchDesc(raw_en : Int, raw_ja : Int) : String {
|
|
|
|
|
val res_id = if("ja" == context.getString(R.string.language_code)) raw_ja else raw_en
|
2018-09-27 16:21:37 +02:00
|
|
|
|
return context.loadRawResource(res_id).decodeUTF8()
|
2018-01-17 18:39:16 +01:00
|
|
|
|
}
|
2018-01-19 10:27:35 +01:00
|
|
|
|
|
|
|
|
|
private var cacheHeaderDesc : String? = null
|
2018-01-17 18:39:16 +01:00
|
|
|
|
|
|
|
|
|
fun getHeaderDesc() : String? {
|
|
|
|
|
var cache = cacheHeaderDesc
|
2018-01-19 10:27:35 +01:00
|
|
|
|
if(cache != null) return cache
|
2018-01-17 18:39:16 +01:00
|
|
|
|
cache = when(column_type) {
|
|
|
|
|
Column.TYPE_SEARCH -> context.getString(R.string.search_desc_mastodon_api)
|
2018-01-19 10:27:35 +01:00
|
|
|
|
Column.TYPE_SEARCH_MSP -> loadSearchDesc(
|
|
|
|
|
R.raw.search_desc_msp_en,
|
|
|
|
|
R.raw.search_desc_msp_ja
|
|
|
|
|
)
|
|
|
|
|
Column.TYPE_SEARCH_TS -> loadSearchDesc(
|
|
|
|
|
R.raw.search_desc_ts_en,
|
|
|
|
|
R.raw.search_desc_ts_ja
|
|
|
|
|
)
|
2018-01-17 18:39:16 +01:00
|
|
|
|
else -> ""
|
|
|
|
|
}
|
|
|
|
|
cacheHeaderDesc = cache
|
|
|
|
|
return cache
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// Streaming
|
|
|
|
|
|
|
|
|
|
internal fun onStart(callback : Callback) {
|
|
|
|
|
this.callback_ref = WeakReference(callback)
|
|
|
|
|
|
|
|
|
|
// 破棄されたカラムなら何もしない
|
|
|
|
|
if(is_dispose.get()) {
|
|
|
|
|
log.d("onStart: column was disposed.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 未初期化なら何もしない
|
|
|
|
|
if(! bFirstInitialized) {
|
|
|
|
|
log.d("onStart: column is not initialized.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初期ロード中なら何もしない
|
|
|
|
|
if(bInitialLoading) {
|
|
|
|
|
log.d("onStart: column is in initial loading.")
|
|
|
|
|
return
|
|
|
|
|
}
|
2018-08-16 21:58:30 +02:00
|
|
|
|
|
2018-07-07 07:15:16 +02:00
|
|
|
|
// フィルタ一覧のリロードが必要
|
2018-08-16 21:58:30 +02:00
|
|
|
|
if(filter_reload_required) {
|
2018-07-07 07:15:16 +02:00
|
|
|
|
filter_reload_required = false
|
|
|
|
|
startLoading()
|
|
|
|
|
return
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
// 始端リフレッシュの最中だった
|
|
|
|
|
// リフレッシュ終了時に自動でストリーミング開始するはず
|
|
|
|
|
if(bRefreshingTop) {
|
|
|
|
|
log.d("onStart: bRefreshingTop is true.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(! bRefreshLoading
|
|
|
|
|
&& canAutoRefresh()
|
2018-01-17 02:16:26 +01:00
|
|
|
|
&& ! Pref.bpDontRefreshOnResume(App1.getAppState(context).pref)
|
|
|
|
|
&& ! dont_auto_refresh
|
2018-01-19 10:27:35 +01:00
|
|
|
|
) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// リフレッシュしてからストリーミング開始
|
|
|
|
|
log.d("onStart: start auto refresh.")
|
2018-08-18 12:58:14 +02:00
|
|
|
|
startRefresh(true, false)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} else if(isSearchColumn) {
|
|
|
|
|
// 検索カラムはリフレッシュもストリーミングもないが、表示開始のタイミングでリストの再描画を行いたい
|
2018-01-20 17:59:38 +01:00
|
|
|
|
fireShowContent(reason = "Column onStart isSearchColumn", reset = true)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} else {
|
|
|
|
|
// ギャップつきでストリーミング開始
|
|
|
|
|
log.d("onStart: start streaming with gap.")
|
|
|
|
|
resumeStreaming(true)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// カラム設定に正規表現フィルタを含めるなら真
|
|
|
|
|
fun canStatusFilter() : Boolean {
|
2018-07-06 17:22:22 +02:00
|
|
|
|
if(getFilterContext() != TootFilter.CONTEXT_NONE) return true
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
return when(column_type) {
|
2018-07-06 17:22:22 +02:00
|
|
|
|
TYPE_SEARCH_MSP, TYPE_SEARCH_TS -> true
|
|
|
|
|
else -> false
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-06 17:22:22 +02:00
|
|
|
|
// マストドン2.4.3rcのキーワードフィルタのコンテキスト
|
|
|
|
|
private fun getFilterContext() = when(column_type) {
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_HOME, TYPE_LIST_TL, TYPE_MISSKEY_HYBRID -> TootFilter.CONTEXT_HOME
|
2018-07-06 17:22:22 +02:00
|
|
|
|
TYPE_NOTIFICATIONS -> TootFilter.CONTEXT_NOTIFICATIONS
|
|
|
|
|
TYPE_CONVERSATION -> TootFilter.CONTEXT_THREAD
|
|
|
|
|
TYPE_LOCAL, TYPE_FEDERATE, TYPE_HASHTAG, TYPE_PROFILE, TYPE_SEARCH -> TootFilter.CONTEXT_PUBLIC
|
|
|
|
|
TYPE_DIRECT_MESSAGES -> TootFilter.CONTEXT_PUBLIC
|
|
|
|
|
else -> TootFilter.CONTEXT_NONE
|
2018-08-31 14:47:40 +02:00
|
|
|
|
// TYPE_MISSKEY_HYBRID はHOMEでもPUBLICでもある… Misskeyだし関係ないが、NONEにするとアプリ内で完結するフィルタも働かなくなる
|
2018-07-06 17:22:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// カラム設定に「すべての画像を隠す」ボタンを含めるなら真
|
|
|
|
|
internal fun canNSFWDefault() : Boolean {
|
|
|
|
|
return canStatusFilter()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// カラム設定に「ブーストを表示しない」ボタンを含めるなら真
|
|
|
|
|
fun canFilterBoost() : Boolean {
|
|
|
|
|
return when(column_type) {
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_HOME, TYPE_MISSKEY_HYBRID, TYPE_PROFILE, TYPE_NOTIFICATIONS, TYPE_LIST_TL -> true
|
2018-08-23 04:20:10 +02:00
|
|
|
|
TYPE_LOCAL, TYPE_FEDERATE, TYPE_HASHTAG, TYPE_SEARCH -> isMisskey
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_CONVERSATION, TYPE_DIRECT_MESSAGES -> isMisskey
|
2018-01-04 19:52:25 +01:00
|
|
|
|
else -> false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-06 21:41:30 +01:00
|
|
|
|
// カラム設定に「返信を表示しない」ボタンを含めるなら真
|
2018-01-04 19:52:25 +01:00
|
|
|
|
fun canFilterReply() : Boolean {
|
|
|
|
|
return when(column_type) {
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_HOME, TYPE_MISSKEY_HYBRID, TYPE_PROFILE, TYPE_NOTIFICATIONS, TYPE_LIST_TL, TYPE_DIRECT_MESSAGES -> true
|
2018-08-21 12:19:02 +02:00
|
|
|
|
TYPE_LOCAL, TYPE_FEDERATE, TYPE_HASHTAG, TYPE_SEARCH -> isMisskey
|
2018-01-04 19:52:25 +01:00
|
|
|
|
else -> false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-10 01:36:27 +01:00
|
|
|
|
fun canFilterNormalToot() : Boolean {
|
|
|
|
|
return when(column_type) {
|
2018-08-24 10:03:20 +02:00
|
|
|
|
TYPE_HOME, TYPE_MISSKEY_HYBRID, TYPE_LIST_TL -> true
|
2018-08-23 04:20:10 +02:00
|
|
|
|
TYPE_LOCAL, TYPE_FEDERATE, TYPE_HASHTAG, TYPE_SEARCH -> isMisskey
|
2018-02-10 01:36:27 +01:00
|
|
|
|
else -> false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
internal fun canAutoRefresh() : Boolean {
|
|
|
|
|
return streamPath != null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun canReloadWhenRefreshTop() : Boolean {
|
|
|
|
|
return when(column_type) {
|
2018-08-25 02:29:24 +02:00
|
|
|
|
|
2018-07-08 19:00:47 +02:00
|
|
|
|
TYPE_KEYWORD_FILTER,
|
2018-05-30 07:18:45 +02:00
|
|
|
|
TYPE_SEARCH,
|
|
|
|
|
TYPE_SEARCH_MSP,
|
|
|
|
|
TYPE_SEARCH_TS,
|
|
|
|
|
TYPE_CONVERSATION,
|
2018-05-30 21:50:07 +02:00
|
|
|
|
TYPE_LIST_LIST,
|
2018-08-25 02:29:24 +02:00
|
|
|
|
TYPE_TREND_TAG,
|
|
|
|
|
TYPE_FOLLOW_SUGGESTION -> true
|
|
|
|
|
|
2018-08-31 14:47:40 +02:00
|
|
|
|
TYPE_LIST_MEMBER,
|
2018-08-25 02:29:24 +02:00
|
|
|
|
TYPE_MUTES,
|
|
|
|
|
TYPE_FOLLOW_REQUESTS -> isMisskey
|
|
|
|
|
|
|
|
|
|
else -> false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// カラム操作的にリフレッシュを許容するかどうか
|
|
|
|
|
fun canRefreshTopBySwipe() : Boolean {
|
|
|
|
|
return canReloadWhenRefreshTop() || when(column_type) {
|
|
|
|
|
TYPE_CONVERSATION,
|
|
|
|
|
TYPE_INSTANCE_INFORMATION -> false
|
|
|
|
|
else -> true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// カラム操作的にリフレッシュを許容するかどうか
|
|
|
|
|
fun canRefreshBottomBySwipe() : Boolean {
|
|
|
|
|
return when(column_type) {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
TYPE_LIST_LIST,
|
2018-08-25 02:29:24 +02:00
|
|
|
|
TYPE_CONVERSATION,
|
|
|
|
|
TYPE_INSTANCE_INFORMATION,
|
|
|
|
|
TYPE_KEYWORD_FILTER,
|
|
|
|
|
TYPE_SEARCH,
|
|
|
|
|
TYPE_TREND_TAG,
|
|
|
|
|
TYPE_FOLLOW_SUGGESTION -> false
|
|
|
|
|
|
|
|
|
|
TYPE_FOLLOW_REQUESTS -> isMisskey
|
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
TYPE_LIST_MEMBER -> ! isMisskey
|
2018-08-31 14:47:40 +02:00
|
|
|
|
|
2018-08-25 02:29:24 +02:00
|
|
|
|
else -> true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// データ的にリフレッシュを許容するかどうか
|
|
|
|
|
private fun canRefreshTop() : Boolean {
|
|
|
|
|
return when(pagingType) {
|
|
|
|
|
PagingType.Default -> idRecent != null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
else -> false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-25 02:29:24 +02:00
|
|
|
|
// データ的にリフレッシュを許容するかどうか
|
|
|
|
|
private fun canRefreshBottom() : Boolean {
|
|
|
|
|
return when(pagingType) {
|
|
|
|
|
PagingType.Default, PagingType.Cursor -> idOld != null
|
|
|
|
|
PagingType.None -> false
|
|
|
|
|
PagingType.Offset -> true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
internal fun canSpeech() : Boolean {
|
|
|
|
|
return canStreaming() && column_type != TYPE_NOTIFICATIONS
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-16 21:58:30 +02:00
|
|
|
|
internal fun canStreaming() = when {
|
|
|
|
|
access_info.isNA -> false
|
2018-08-25 03:21:59 +02:00
|
|
|
|
access_info.isMisskey -> streamPath != null
|
2018-08-16 21:58:30 +02:00
|
|
|
|
access_info.isPseudo -> isPublicStream
|
|
|
|
|
else -> streamPath != null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 15:22:21 +02:00
|
|
|
|
private val streamCallback = object : StreamReader.StreamCallback {
|
|
|
|
|
|
|
|
|
|
override fun onListeningStateChanged() {
|
|
|
|
|
if(is_dispose.get()) return
|
|
|
|
|
runOnMainLooper {
|
2018-11-03 17:14:14 +01:00
|
|
|
|
when {
|
|
|
|
|
is_dispose.get() -> {
|
2018-11-03 15:21:42 +01:00
|
|
|
|
|
|
|
|
|
}
|
2018-11-03 17:14:14 +01:00
|
|
|
|
|
|
|
|
|
else -> {
|
2018-11-03 15:21:42 +01:00
|
|
|
|
fireShowColumnStatus()
|
|
|
|
|
updateMisskeyCapture()
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-20 15:22:21 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun onTimelineItem(item : TimelineItem) {
|
|
|
|
|
if(is_dispose.get()) return
|
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
if(item is TootConversationSummary) {
|
|
|
|
|
if(column_type != TYPE_DIRECT_MESSAGES) return
|
|
|
|
|
if(isFiltered(item.last_status)) return
|
2018-10-30 20:29:00 +01:00
|
|
|
|
if(use_old_api) {
|
2018-10-10 22:43:10 +02:00
|
|
|
|
useConversationSummaryStreaming = false
|
|
|
|
|
return
|
2018-10-30 20:29:00 +01:00
|
|
|
|
} else {
|
2018-10-10 22:43:10 +02:00
|
|
|
|
useConversationSummaryStreaming = true
|
|
|
|
|
}
|
2018-10-30 20:29:00 +01:00
|
|
|
|
} else if(item is TootNotification) {
|
2018-04-20 15:22:21 +02:00
|
|
|
|
if(column_type != TYPE_NOTIFICATIONS) return
|
|
|
|
|
if(isFiltered(item)) return
|
|
|
|
|
} else if(item is TootStatus) {
|
|
|
|
|
if(column_type == TYPE_NOTIFICATIONS) return
|
2018-10-30 20:29:00 +01:00
|
|
|
|
|
2018-10-10 22:43:10 +02:00
|
|
|
|
// マストドン2.6.0形式のDMカラム用イベントを利用したならば、その直後に発生する普通の投稿イベントを無視する
|
2018-10-30 20:29:00 +01:00
|
|
|
|
if(useConversationSummaryStreaming) return
|
2018-10-09 01:20:43 +02:00
|
|
|
|
|
2018-08-25 03:32:10 +02:00
|
|
|
|
if(column_type == TYPE_LOCAL && ! isMisskey && item.account.acct.indexOf('@') != - 1) return
|
2018-04-20 15:22:21 +02:00
|
|
|
|
if(isFiltered(item)) return
|
|
|
|
|
}
|
2018-10-30 20:29:00 +01:00
|
|
|
|
|
2018-08-25 13:59:57 +02:00
|
|
|
|
stream_data_queue.add(item)
|
2018-08-26 04:09:34 +02:00
|
|
|
|
|
2018-08-25 13:59:57 +02:00
|
|
|
|
val handler = App1.getAppState(context).handler
|
|
|
|
|
handler.post(mergeStreamingMessage)
|
2018-04-20 15:22:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-03 17:14:14 +01:00
|
|
|
|
override fun onNoteUpdated(ev : MisskeyNoteUpdate) {
|
|
|
|
|
// userId が自分かどうか調べる
|
|
|
|
|
// アクセストークンの更新をして自分のuserIdが分かる状態でないとキャプチャ結果を反映させない
|
|
|
|
|
// (でないとリアクションの2重カウントなどが発生してしまう)
|
|
|
|
|
val myId = EntityId.from(access_info.token_info, TootApiClient.KEY_USER_ID)
|
|
|
|
|
if(myId == null) {
|
|
|
|
|
log.w("onNoteUpdated: missing my userId. updating access token is recommenced!!")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val byMe = myId == ev.userId
|
|
|
|
|
|
|
|
|
|
runOnMainLooper {
|
|
|
|
|
if(is_dispose.get()) return@runOnMainLooper
|
|
|
|
|
|
|
|
|
|
val changeList = ArrayList<AdapterChange>()
|
|
|
|
|
|
|
|
|
|
fun scanStatus1(s : TootStatus?, idx : Int, block : (s : TootStatus) -> Boolean) {
|
|
|
|
|
s ?: return
|
|
|
|
|
if(s.id == ev.noteId) {
|
|
|
|
|
if(block(s)) {
|
|
|
|
|
changeList.add(AdapterChange(AdapterChangeType.RangeChange, idx, 1))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
scanStatus1(s.reblog, idx, block)
|
|
|
|
|
scanStatus1(s.reply, idx, block)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun scanStatusAll(block : (s : TootStatus) -> Boolean) {
|
|
|
|
|
for(i in 0 until list_data.size) {
|
|
|
|
|
val o = list_data[i]
|
|
|
|
|
if(o is TootStatus) {
|
|
|
|
|
scanStatus1(o, i, block)
|
|
|
|
|
} else if(o is TootNotification) {
|
|
|
|
|
scanStatus1(o.status, i, block)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
when(ev.type) {
|
|
|
|
|
MisskeyNoteUpdate.Type.REACTION -> {
|
|
|
|
|
scanStatusAll { s ->
|
|
|
|
|
s.increaseReaction(ev.reaction, byMe, "onNoteUpdated ${ev.userId}")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MisskeyNoteUpdate.Type.VOTED -> {
|
|
|
|
|
scanStatusAll { s ->
|
|
|
|
|
s.enquete?.increaseVote(context, ev.choice, byMe) ?: false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MisskeyNoteUpdate.Type.DELETED -> {
|
|
|
|
|
scanStatusAll { s ->
|
|
|
|
|
s.markDeleted(context, ev.deletedAt) ?: false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(changeList.isNotEmpty()) {
|
|
|
|
|
fireShowContent(reason = "onNoteUpdated", changeList = changeList)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-20 15:22:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
private fun resumeStreaming(bPutGap : Boolean) {
|
|
|
|
|
|
|
|
|
|
// カラム種別によってはストリーミングAPIを利用できない
|
|
|
|
|
val stream_path = streamPath ?: return
|
|
|
|
|
|
|
|
|
|
// 疑似アカウントではストリーミングAPIを利用できない
|
|
|
|
|
// 2.1 では公開ストリームのみ利用できるらしい
|
|
|
|
|
if(access_info.isNA || access_info.isPseudo && ! isPublicStream) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(! isActivityStart) {
|
|
|
|
|
log.d("resumeStreaming: isActivityStart is false.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 破棄されたカラムなら何もしない
|
|
|
|
|
if(is_dispose.get()) {
|
|
|
|
|
log.d("resumeStreaming: column was disposed.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 未初期化なら何もしない
|
|
|
|
|
if(! bFirstInitialized) {
|
|
|
|
|
log.d("resumeStreaming: column is not initialized.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 初期ロード中なら何もしない
|
|
|
|
|
if(bInitialLoading) {
|
|
|
|
|
log.d("resumeStreaming: is in initial loading.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
if(Pref.bpDontUseStreaming(context)) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
log.d("resumeStreaming: disabled in app setting.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(dont_streaming) {
|
|
|
|
|
log.d("resumeStreaming: disabled in column setting.")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.bPutGap = bPutGap
|
|
|
|
|
|
|
|
|
|
stream_data_queue.clear()
|
|
|
|
|
|
2018-11-03 17:14:14 +01:00
|
|
|
|
streamReader = app_state.stream_reader.register(
|
|
|
|
|
access_info,
|
|
|
|
|
stream_path,
|
|
|
|
|
highlight_trie,
|
|
|
|
|
streamCallback
|
|
|
|
|
)
|
2018-04-20 15:22:21 +02:00
|
|
|
|
fireShowColumnStatus()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-03 15:21:42 +01:00
|
|
|
|
private var streamReader : StreamReader.Reader? = null
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// onPauseの時はまとめて止められるが
|
|
|
|
|
// カラム破棄やリロード開始時は個別にストリーミングを止める必要がある
|
|
|
|
|
internal fun stopStreaming() {
|
2018-11-03 15:21:42 +01:00
|
|
|
|
streamReader = null
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val stream_path = streamPath
|
|
|
|
|
if(stream_path != null) {
|
2018-04-20 15:22:21 +02:00
|
|
|
|
app_state.stream_reader.unregister(access_info, stream_path, streamCallback)
|
|
|
|
|
fireShowColumnStatus()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-20 15:22:21 +02:00
|
|
|
|
fun getStreamingStatus() : StreamingIndicatorState {
|
2018-05-04 15:35:01 +02:00
|
|
|
|
if(is_dispose.get() || ! bFirstInitialized) return StreamingIndicatorState.NONE
|
2018-04-20 15:22:21 +02:00
|
|
|
|
val stream_path = streamPath ?: return StreamingIndicatorState.NONE
|
|
|
|
|
return app_state.stream_reader.getStreamingStatus(access_info, stream_path, streamCallback)
|
2018-01-12 10:01:25 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-26 04:09:34 +02:00
|
|
|
|
private val mergeStreamingMessage : Runnable = object : Runnable {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
override fun run() {
|
2018-01-12 10:01:25 +01:00
|
|
|
|
|
|
|
|
|
// 前回マージしてから暫くは待機する
|
2018-08-26 04:09:34 +02:00
|
|
|
|
val handler = App1.getAppState(context).handler
|
2018-08-25 13:59:57 +02:00
|
|
|
|
val now = SystemClock.elapsedRealtime()
|
|
|
|
|
val remain = last_show_stream_data.get() + 333L - now
|
2018-08-26 04:09:34 +02:00
|
|
|
|
if(remain > 0) {
|
2018-08-25 13:59:57 +02:00
|
|
|
|
handler.removeCallbacks(this)
|
|
|
|
|
handler.postDelayed(this, remain)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
return
|
|
|
|
|
}
|
2018-08-25 13:59:57 +02:00
|
|
|
|
last_show_stream_data.set(now)
|
|
|
|
|
|
|
|
|
|
val tmpList = ArrayList<TimelineItem>()
|
2018-08-26 04:09:34 +02:00
|
|
|
|
while(stream_data_queue.isNotEmpty()) {
|
2018-08-25 13:59:57 +02:00
|
|
|
|
tmpList.add(stream_data_queue.poll())
|
|
|
|
|
}
|
|
|
|
|
if(tmpList.isEmpty()) return
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-25 13:59:57 +02:00
|
|
|
|
// キューから読めた件数が0の場合を除き、少し後に再処理させることでマージ漏れを防ぐ
|
|
|
|
|
handler.postDelayed(this, 333L)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-08-30 08:51:32 +02:00
|
|
|
|
tmpList.sortBy { it.getOrderId() }
|
|
|
|
|
tmpList.reverse()
|
|
|
|
|
|
2018-08-25 13:59:57 +02:00
|
|
|
|
val list_new = duplicate_map.filterDuplicate(tmpList)
|
2018-01-12 10:01:25 +01:00
|
|
|
|
if(list_new.isEmpty()) return
|
2018-08-26 04:09:34 +02:00
|
|
|
|
|
|
|
|
|
for(item in list_new) {
|
|
|
|
|
if(enable_speech && item is TootStatus) {
|
2018-08-25 13:59:57 +02:00
|
|
|
|
App1.getAppState(context).addSpeech(item.reblog ?: item)
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-12 10:01:25 +01:00
|
|
|
|
|
|
|
|
|
// 通知カラムならストリーミング経由で届いたデータを通知ワーカーに伝達する
|
2018-08-25 03:32:10 +02:00
|
|
|
|
if(column_type == TYPE_NOTIFICATIONS) {
|
2018-01-12 10:01:25 +01:00
|
|
|
|
val list = ArrayList<TootNotification>()
|
|
|
|
|
for(o in list_new) {
|
|
|
|
|
if(o is TootNotification) {
|
|
|
|
|
list.add(o)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-01-12 10:01:25 +01:00
|
|
|
|
if(! list.isEmpty()) {
|
2018-08-25 02:54:17 +02:00
|
|
|
|
PollingWorker.injectData(context, access_info, list)
|
2018-01-12 10:01:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 最新のIDをsince_idとして覚える(ソートはしない)
|
2018-08-20 02:07:55 +02:00
|
|
|
|
var new_id_max : EntityId? = null
|
|
|
|
|
var new_id_min : EntityId? = null
|
2018-01-12 10:01:25 +01:00
|
|
|
|
for(o in list_new) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
try {
|
2018-08-25 03:05:27 +02:00
|
|
|
|
val id = o.getOrderId()
|
2018-08-25 03:32:10 +02:00
|
|
|
|
if(id.toString().isEmpty()) continue
|
2018-08-20 02:07:55 +02:00
|
|
|
|
if(new_id_max == null || id > new_id_max) new_id_max = id
|
|
|
|
|
if(new_id_min == null || id < new_id_min) new_id_min = id
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} catch(ex : Throwable) {
|
2018-01-12 10:01:25 +01:00
|
|
|
|
// IDを取得できないタイプのオブジェクトだった
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// ストリームに来るのは通知かステータスだから、多分ここは通らない
|
2018-01-12 10:01:25 +01:00
|
|
|
|
log.trace(ex)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-25 03:32:10 +02:00
|
|
|
|
|
|
|
|
|
val tmpRecent = idRecent
|
|
|
|
|
val tmpNewMax = new_id_max
|
2018-08-25 10:05:50 +02:00
|
|
|
|
|
2018-08-26 04:09:34 +02:00
|
|
|
|
if(tmpNewMax is EntityIdString && ! isMisskey) {
|
2018-08-25 10:05:50 +02:00
|
|
|
|
log.e("EntityId should be Long for non-misskey column! columnType=$column_type")
|
2018-08-26 04:09:34 +02:00
|
|
|
|
} else if(tmpRecent is EntityIdString && tmpNewMax is EntityIdLong) {
|
2018-08-25 10:05:50 +02:00
|
|
|
|
log.e("EntityId type mismatch! recent=String,newMax=Long,columnType=$column_type")
|
2018-08-26 04:09:34 +02:00
|
|
|
|
} else if(tmpRecent is EntityIdLong && tmpNewMax is EntityIdString) {
|
2018-08-25 10:05:50 +02:00
|
|
|
|
log.e("EntityId type mismatch! recent=Long,newMax=String,columnType=$column_type")
|
2018-08-26 04:09:34 +02:00
|
|
|
|
} else if(tmpNewMax != null && (tmpRecent?.compareTo(tmpNewMax) ?: - 1) == - 1) {
|
2018-08-25 03:32:10 +02:00
|
|
|
|
idRecent = tmpNewMax
|
2018-01-12 10:01:25 +01:00
|
|
|
|
// XXX: コレはリフレッシュ時に取得漏れを引き起こすのでは…?
|
|
|
|
|
// しかしコレなしだとリフレッシュ時に大量に読むことになる…
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
val holder = viewHolder
|
|
|
|
|
|
|
|
|
|
// 事前にスクロール位置を覚えておく
|
2018-01-10 16:47:35 +01:00
|
|
|
|
val holder_sp : ScrollPosition? = holder?.scrollPosition
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
2018-01-19 10:27:35 +01:00
|
|
|
|
// idx番目の要素がListViewの上端から何ピクセル下にあるか
|
|
|
|
|
var restore_idx = - 2
|
2018-01-04 19:52:25 +01:00
|
|
|
|
var restore_y = 0
|
|
|
|
|
if(holder != null) {
|
|
|
|
|
if(list_data.size > 0) {
|
|
|
|
|
try {
|
2018-01-19 10:27:35 +01:00
|
|
|
|
restore_idx = holder.findFirstVisibleListItem()
|
2018-12-12 15:21:38 +01:00
|
|
|
|
restore_y = holder.getListItemOffset(restore_idx)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
} catch(ex : IndexOutOfBoundsException) {
|
2018-01-19 10:27:35 +01:00
|
|
|
|
restore_idx = - 2
|
2018-01-04 19:52:25 +01:00
|
|
|
|
restore_y = 0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-12 10:01:25 +01:00
|
|
|
|
// 画面復帰時の自動リフレッシュではギャップが残る可能性がある
|
2018-01-04 19:52:25 +01:00
|
|
|
|
if(bPutGap) {
|
|
|
|
|
bPutGap = false
|
|
|
|
|
try {
|
2018-08-20 02:07:55 +02:00
|
|
|
|
if(list_data.size > 0 && new_id_min != null) {
|
2018-08-25 03:05:27 +02:00
|
|
|
|
val since = list_data[0].getOrderId()
|
2018-08-25 03:10:35 +02:00
|
|
|
|
if(new_id_min > since) {
|
2018-01-12 10:01:25 +01:00
|
|
|
|
val gap = TootGap(new_id_min, since)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
list_new.add(gap)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.e(ex, "can't put gap.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-28 14:08:10 +01:00
|
|
|
|
val changeList = ArrayList<AdapterChange>()
|
2018-10-30 20:29:00 +01:00
|
|
|
|
|
|
|
|
|
replaceConversationSummary(changeList, list_new, list_data)
|
2018-10-28 14:08:10 +01:00
|
|
|
|
|
2018-11-11 13:17:36 +01:00
|
|
|
|
val added = list_new.size // may 0
|
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
loop@ for(o in list_new) {
|
|
|
|
|
when(o) {
|
2018-10-30 20:29:00 +01:00
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
is TootStatus -> {
|
|
|
|
|
val highlight_sound = o.highlight_sound
|
|
|
|
|
if(highlight_sound != null) {
|
|
|
|
|
App1.sound(highlight_sound)
|
|
|
|
|
break@loop
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-30 20:29:00 +01:00
|
|
|
|
|
2018-10-09 01:20:43 +02:00
|
|
|
|
changeList.add(AdapterChange(AdapterChangeType.RangeInsert, 0, added))
|
2018-01-20 07:51:14 +01:00
|
|
|
|
list_data.addAll(0, list_new)
|
2018-10-09 01:20:43 +02:00
|
|
|
|
|
2018-01-20 17:59:38 +01:00
|
|
|
|
fireShowContent(reason = "mergeStreamingMessage", changeList = changeList)
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
|
|
if(holder != null) {
|
2018-11-25 16:11:25 +01:00
|
|
|
|
when {
|
|
|
|
|
holder_sp == null -> {
|
|
|
|
|
// スクロール位置が先頭なら先頭にする
|
|
|
|
|
log.d("mergeStreamingMessage: has VH. missing scroll position.")
|
|
|
|
|
viewHolder?.scrollToTop()
|
|
|
|
|
}
|
2018-12-01 00:02:18 +01:00
|
|
|
|
|
2018-11-25 16:11:25 +01:00
|
|
|
|
holder_sp.isHead -> {
|
|
|
|
|
// スクロール位置が先頭なら先頭にする
|
|
|
|
|
log.d("mergeStreamingMessage: has VH. keep head. $holder_sp")
|
2018-12-12 15:21:38 +01:00
|
|
|
|
holder.setScrollPosition(ScrollPosition())
|
2018-11-25 16:11:25 +01:00
|
|
|
|
}
|
2018-12-01 00:02:18 +01:00
|
|
|
|
|
|
|
|
|
restore_idx < - 1 -> {
|
2018-11-25 16:11:25 +01:00
|
|
|
|
// 可視範囲の検出に失敗
|
|
|
|
|
log.d("mergeStreamingMessage: has VH. can't get visible range.")
|
|
|
|
|
}
|
2018-12-01 00:02:18 +01:00
|
|
|
|
|
2018-11-25 16:11:25 +01:00
|
|
|
|
else -> {
|
|
|
|
|
// 現在の要素が表示され続けるようにしたい
|
|
|
|
|
log.d("mergeStreamingMessage: has VH. added=$added")
|
|
|
|
|
holder.setListItemTop(restore_idx + added, restore_y)
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2018-01-10 16:47:35 +01:00
|
|
|
|
val scroll_save = this@Column.scroll_save
|
2018-12-01 00:02:18 +01:00
|
|
|
|
if(scroll_save == null || scroll_save.isHead) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
// スクロール位置が先頭なら先頭のまま
|
|
|
|
|
} else {
|
|
|
|
|
// 現在の要素が表示され続けるようにしたい
|
2018-01-19 10:27:35 +01:00
|
|
|
|
scroll_save.adapterIndex += added
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-03 15:21:42 +01:00
|
|
|
|
|
|
|
|
|
updateMisskeyCapture()
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-03 17:14:14 +01:00
|
|
|
|
private fun min(a : Int, b : Int) : Int = if(a < b) a else b
|
2018-11-03 15:21:42 +01:00
|
|
|
|
|
2018-11-03 17:14:14 +01:00
|
|
|
|
private fun updateMisskeyCapture() {
|
|
|
|
|
if(! isMisskey) return
|
|
|
|
|
streamReader ?: return
|
2018-11-03 15:21:42 +01:00
|
|
|
|
|
|
|
|
|
val max = 40
|
2018-11-03 17:14:14 +01:00
|
|
|
|
val list = ArrayList<EntityId>(max * 2) // リブログなどで膨れる場合がある
|
|
|
|
|
|
|
|
|
|
fun add(s : TootStatus?) {
|
|
|
|
|
s ?: return
|
|
|
|
|
list.add(s.id)
|
|
|
|
|
add(s.reblog)
|
|
|
|
|
add(s.reply)
|
2018-11-03 15:21:42 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-03 17:14:14 +01:00
|
|
|
|
for(i in 0 until min(max, list_data.size)) {
|
2018-11-03 15:21:42 +01:00
|
|
|
|
val o = list_data[i]
|
2018-11-03 17:14:14 +01:00
|
|
|
|
if(o is TootStatus) {
|
2018-11-03 15:21:42 +01:00
|
|
|
|
add(o)
|
2018-11-03 17:14:14 +01:00
|
|
|
|
} else if(o is TootNotification) {
|
2018-11-03 15:21:42 +01:00
|
|
|
|
add(o.status)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-03 17:14:14 +01:00
|
|
|
|
if(list.isNotEmpty()) streamReader?.capture(list)
|
2018-11-03 15:21:42 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-28 14:08:10 +01:00
|
|
|
|
private fun replaceConversationSummary(
|
2018-10-30 20:29:00 +01:00
|
|
|
|
changeList : ArrayList<AdapterChange>,
|
|
|
|
|
list_new : ArrayList<TimelineItem>,
|
|
|
|
|
list_data : BucketList<TimelineItem>
|
|
|
|
|
) {
|
2018-11-11 11:43:20 +01:00
|
|
|
|
|
2018-11-18 03:38:52 +01:00
|
|
|
|
val newMap = HashMap<EntityId, TootConversationSummary>()
|
2018-11-23 18:56:57 +01:00
|
|
|
|
|
2018-11-11 11:43:20 +01:00
|
|
|
|
for(o in list_new) {
|
2018-11-18 03:38:52 +01:00
|
|
|
|
if(o is TootConversationSummary) newMap[o.id] = o
|
2018-10-28 14:08:10 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-01 00:02:18 +01:00
|
|
|
|
if(list_data.isEmpty() || newMap.isEmpty()) return
|
2018-11-23 18:56:57 +01:00
|
|
|
|
|
|
|
|
|
val removeSet = HashSet<EntityId>()
|
|
|
|
|
for(i in list_data.size - 1 downTo 0) {
|
|
|
|
|
val o = list_data[i] as? TootConversationSummary ?: continue
|
|
|
|
|
val newItem = newMap[o.id] ?: continue
|
|
|
|
|
|
|
|
|
|
if(o.last_status.uri == newItem.last_status.uri) {
|
|
|
|
|
// 投稿が同じなので順序を入れ替えず、その場所で更新する
|
|
|
|
|
changeList.add(AdapterChange(AdapterChangeType.RangeChange, i, 1))
|
|
|
|
|
list_data[i] = newItem
|
|
|
|
|
removeSet.add(newItem.id)
|
|
|
|
|
log.d("replaceConversationSummary: in-place update")
|
|
|
|
|
} else {
|
|
|
|
|
// 投稿が異なるので古い方を削除して、リストの順序を変える
|
|
|
|
|
changeList.add(AdapterChange(AdapterChangeType.RangeRemove, i, 1))
|
|
|
|
|
list_data.removeAt(i)
|
|
|
|
|
log.d("replaceConversationSummary: order change")
|
2018-11-11 11:43:20 +01:00
|
|
|
|
}
|
2018-10-28 14:08:10 +01:00
|
|
|
|
}
|
2018-11-23 18:56:57 +01:00
|
|
|
|
val it = list_new.iterator()
|
2018-12-01 00:02:18 +01:00
|
|
|
|
while(it.hasNext()) {
|
2018-11-23 18:56:57 +01:00
|
|
|
|
val o = it.next() as? TootConversationSummary ?: continue
|
|
|
|
|
if(removeSet.contains(o.id)) it.remove()
|
|
|
|
|
}
|
2018-10-28 14:08:10 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
private fun makeMisskeyBaseParameter(parser : TootParser?) : JSONObject =
|
|
|
|
|
access_info
|
|
|
|
|
.putMisskeyApiToken(JSONObject())
|
2018-08-21 12:19:02 +02:00
|
|
|
|
.apply {
|
2018-08-20 02:07:55 +02:00
|
|
|
|
if(access_info.isMisskey) {
|
|
|
|
|
if(parser != null) parser.serviceType = ServiceType.MISSKEY
|
2018-12-02 10:35:04 +01:00
|
|
|
|
put("limit", 40)
|
2018-08-20 02:07:55 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-08-20 02:07:55 +02:00
|
|
|
|
private fun JSONObject.putMisskeyParamsTimeline() : JSONObject {
|
|
|
|
|
if(with_attachment && ! with_highlight) {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
put("mediaOnly", true)
|
2018-08-20 19:37:42 +02:00
|
|
|
|
put("withMedia", true)
|
2018-09-05 16:03:26 +02:00
|
|
|
|
put("withFiles", true)
|
2018-08-24 10:03:20 +02:00
|
|
|
|
put("media", true)
|
2018-03-01 07:41:07 +01:00
|
|
|
|
}
|
2018-08-18 12:58:14 +02:00
|
|
|
|
return this
|
2018-03-01 07:41:07 +01:00
|
|
|
|
}
|
2018-08-20 02:07:55 +02:00
|
|
|
|
|
|
|
|
|
private fun makeMisskeyParamsUserId(parser : TootParser) : JSONObject =
|
|
|
|
|
makeMisskeyBaseParameter(parser).put("userId", profile_id.toString())
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
|
|
|
|
private fun makeMisskeyTimelineParameter(parser : TootParser) =
|
|
|
|
|
makeMisskeyBaseParameter(parser).putMisskeyParamsTimeline()
|
|
|
|
|
|
|
|
|
|
private fun makeMisskeyParamsProfileStatuses(parser : TootParser) =
|
2018-08-20 19:37:42 +02:00
|
|
|
|
makeMisskeyParamsUserId(parser)
|
|
|
|
|
.putMisskeyParamsTimeline()
|
2018-08-21 12:19:02 +02:00
|
|
|
|
.put("includeReplies", true)
|
2018-03-21 06:18:19 +01:00
|
|
|
|
|
|
|
|
|
private fun makePublicLocalUrl() : String {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
return when {
|
|
|
|
|
access_info.isMisskey -> "/api/notes/local-timeline"
|
|
|
|
|
with_attachment -> "$PATH_LOCAL&only_media=true" // mastodon 2.3 or later
|
|
|
|
|
else -> PATH_LOCAL
|
2018-08-23 04:20:10 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-24 10:03:20 +02:00
|
|
|
|
|
2018-08-23 04:20:10 +02:00
|
|
|
|
private fun makeMisskeyHybridTlUrl() : String {
|
|
|
|
|
return when {
|
|
|
|
|
access_info.isMisskey -> "/api/notes/hybrid-timeline"
|
|
|
|
|
with_attachment -> "$PATH_LOCAL&only_media=true" // mastodon 2.3 or later
|
|
|
|
|
else -> PATH_LOCAL
|
2018-03-10 01:28:53 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-03-21 06:18:19 +01:00
|
|
|
|
|
|
|
|
|
private fun makePublicFederateUrl() : String {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
return when {
|
|
|
|
|
access_info.isMisskey -> "/api/notes/global-timeline"
|
|
|
|
|
with_attachment -> "$PATH_TL_FEDERATE&only_media=true"
|
|
|
|
|
else -> PATH_TL_FEDERATE
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun makeHomeTlUrl() : String {
|
|
|
|
|
return when {
|
|
|
|
|
access_info.isMisskey -> "/api/notes/timeline"
|
|
|
|
|
with_attachment -> "$PATH_HOME&only_media=true"
|
|
|
|
|
else -> PATH_HOME
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun makeNotificationUrl() : String {
|
|
|
|
|
return when {
|
|
|
|
|
access_info.isMisskey -> "/api/i/notifications"
|
2018-08-24 10:03:20 +02:00
|
|
|
|
|
|
|
|
|
else -> {
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val sb = StringBuilder(PATH_NOTIFICATIONS) // always contain "?limit=XX"
|
|
|
|
|
if(dont_show_favourite) sb.append("&exclude_types[]=favourite")
|
|
|
|
|
if(dont_show_boost) sb.append("&exclude_types[]=reblog")
|
|
|
|
|
if(dont_show_follow) sb.append("&exclude_types[]=follow")
|
|
|
|
|
if(dont_show_reply) sb.append("&exclude_types[]=mention")
|
2018-08-21 12:19:02 +02:00
|
|
|
|
// reaction,voteはmastodonにはない
|
2018-08-18 12:58:14 +02:00
|
|
|
|
sb.toString()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun makeListTlUrl() : String {
|
2018-10-01 02:10:16 +02:00
|
|
|
|
return if(isMisskey) {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
"/api/notes/user-list-timeline"
|
2018-10-01 02:10:16 +02:00
|
|
|
|
} else {
|
2018-08-31 14:47:40 +02:00
|
|
|
|
String.format(Locale.JAPAN, PATH_LIST_TL, profile_id)
|
|
|
|
|
}
|
2018-03-10 01:28:53 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun makeHashtagUrl(
|
2018-03-21 06:18:19 +01:00
|
|
|
|
hashtag : String // 先頭の#を含まない
|
|
|
|
|
) : String {
|
2018-08-24 10:03:20 +02:00
|
|
|
|
return if(isMisskey) {
|
|
|
|
|
"/api/notes/search_by_tag"
|
|
|
|
|
} else {
|
|
|
|
|
val sb = StringBuilder("/api/v1/timelines/tag/")
|
|
|
|
|
.append(hashtag.encodePercent())
|
|
|
|
|
.append("?limit=")
|
|
|
|
|
.append(READ_LIMIT)
|
|
|
|
|
if(with_attachment) sb.append("&only_media=true")
|
|
|
|
|
if(instance_local) sb.append("&local=true")
|
|
|
|
|
sb.toString()
|
|
|
|
|
}
|
2018-03-10 01:28:53 +01:00
|
|
|
|
}
|
2018-08-20 02:07:55 +02:00
|
|
|
|
|
2018-07-06 17:22:22 +02:00
|
|
|
|
private fun loadFilter2(client : TootApiClient) : ArrayList<TootFilter>? {
|
2018-08-20 02:07:55 +02:00
|
|
|
|
if(access_info.isPseudo || access_info.isMisskey) return null
|
2018-07-06 17:22:22 +02:00
|
|
|
|
val column_context = getFilterContext()
|
|
|
|
|
if(column_context == 0) return null
|
|
|
|
|
val result = client.request(PATH_FILTERS)
|
|
|
|
|
|
|
|
|
|
val jsonArray = result?.jsonArray ?: return null
|
|
|
|
|
return TootFilter.parseList(jsonArray)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun encodeFilterTree(filterList : ArrayList<TootFilter>?) : WordTrieTree? {
|
|
|
|
|
val column_context = getFilterContext()
|
|
|
|
|
if(column_context == 0 || filterList == null) return null
|
2018-07-10 08:44:34 +02:00
|
|
|
|
val tree = WordTrieTree()
|
2018-07-06 17:22:22 +02:00
|
|
|
|
for(filter in filterList) {
|
|
|
|
|
if((filter.context and column_context) != 0) {
|
2018-08-16 21:58:30 +02:00
|
|
|
|
tree.add(
|
|
|
|
|
filter.phrase, validator = when(filter.whole_word) {
|
|
|
|
|
true -> WordTrieTree.WORD_VALIDATOR
|
2018-07-10 08:44:34 +02:00
|
|
|
|
else -> WordTrieTree.EMPTY_VALIDATOR
|
2018-08-16 21:58:30 +02:00
|
|
|
|
}
|
|
|
|
|
)
|
2018-07-06 17:22:22 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return tree
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun checkFiltersForListData(tree : WordTrieTree?) {
|
|
|
|
|
tree ?: return
|
|
|
|
|
|
|
|
|
|
val changeList = ArrayList<AdapterChange>()
|
|
|
|
|
list_data.forEachIndexed { idx, item ->
|
|
|
|
|
when(item) {
|
|
|
|
|
is TootStatus -> {
|
|
|
|
|
val old_filtered = item.filtered
|
|
|
|
|
item.updateFiltered(tree)
|
|
|
|
|
if(old_filtered != item.filtered) {
|
|
|
|
|
changeList.add(AdapterChange(AdapterChangeType.RangeChange, idx))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
is TootNotification -> {
|
|
|
|
|
val s = item.status
|
|
|
|
|
if(s != null) {
|
|
|
|
|
val old_filtered = s.filtered
|
|
|
|
|
s.updateFiltered(tree)
|
|
|
|
|
if(old_filtered != s.filtered) {
|
|
|
|
|
changeList.add(AdapterChange(AdapterChangeType.RangeChange, idx))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fireShowContent(reason = "filter updated", changeList = changeList)
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun onFiltersChanged2(filterList : ArrayList<TootFilter>) {
|
|
|
|
|
val newFilter = encodeFilterTree(filterList) ?: return
|
|
|
|
|
this.muted_word2 = newFilter
|
|
|
|
|
checkFiltersForListData(newFilter)
|
|
|
|
|
}
|
2018-07-07 07:15:16 +02:00
|
|
|
|
|
|
|
|
|
fun onFilterDeleted(filter : TootFilter, filterList : ArrayList<TootFilter>) {
|
|
|
|
|
if(column_type == TYPE_KEYWORD_FILTER) {
|
|
|
|
|
val tmp_list = ArrayList<TimelineItem>(list_data.size)
|
|
|
|
|
for(o in list_data) {
|
|
|
|
|
if(o is TootFilter) {
|
|
|
|
|
if(o.id == filter.id) continue
|
|
|
|
|
}
|
|
|
|
|
tmp_list.add(o)
|
|
|
|
|
}
|
|
|
|
|
if(tmp_list.size != list_data.size) {
|
|
|
|
|
list_data.clear()
|
|
|
|
|
list_data.addAll(tmp_list)
|
|
|
|
|
fireShowContent(reason = "onFilterDeleted")
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
val context = getFilterContext()
|
|
|
|
|
if(context != TootFilter.CONTEXT_NONE) {
|
|
|
|
|
onFiltersChanged2(filterList)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-07-16 20:19:19 +02:00
|
|
|
|
|
2018-08-18 12:58:14 +02:00
|
|
|
|
val isMisskey : Boolean = access_info.isMisskey
|
|
|
|
|
|
2018-08-24 15:45:27 +02:00
|
|
|
|
fun saveScrollPosition() {
|
|
|
|
|
try {
|
|
|
|
|
if(viewHolder?.saveScrollPosition() == true) {
|
|
|
|
|
val ss = this.scroll_save
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(ss != null) {
|
2018-08-28 15:53:30 +02:00
|
|
|
|
val idx = toListIndex(ss.adapterIndex)
|
2018-10-01 02:10:16 +02:00
|
|
|
|
if(0 <= idx && idx < list_data.size) {
|
2018-08-28 15:53:30 +02:00
|
|
|
|
val item = list_data[idx]
|
|
|
|
|
this.last_viewing_item_id = item.getOrderId()
|
|
|
|
|
// とりあえず保存はするが
|
|
|
|
|
// TLデータそのものを永続化しないかぎり出番はないっぽい
|
|
|
|
|
}
|
2018-08-24 15:45:27 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-08-24 16:38:32 +02:00
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
|
log.e(ex, "can't get last_viewing_item_id.")
|
2018-08-24 15:45:27 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-19 23:46:14 +01:00
|
|
|
|
fun getContentColor() : Int = when {
|
|
|
|
|
content_color != 0 -> content_color
|
|
|
|
|
else -> defaultColorContentText
|
2018-11-18 03:38:52 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-19 23:46:14 +01:00
|
|
|
|
fun getAcctColor() : Int = when {
|
|
|
|
|
acct_color != 0 -> acct_color
|
|
|
|
|
else -> defaultColorContentAcct
|
2018-11-18 03:38:52 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-19 23:46:14 +01:00
|
|
|
|
fun getHeaderPageNumberColor() = when {
|
|
|
|
|
header_fg_color != 0 -> header_fg_color
|
|
|
|
|
else -> defaultColorHeaderPageNumber
|
2018-11-18 15:29:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-19 23:46:14 +01:00
|
|
|
|
fun getHeaderNameColor() = when {
|
|
|
|
|
header_fg_color != 0 -> header_fg_color
|
|
|
|
|
else -> defaultColorHeaderName
|
2018-11-18 15:29:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-19 23:46:14 +01:00
|
|
|
|
fun getHeaderBackgroundColor() = when {
|
|
|
|
|
header_bg_color != 0 -> header_bg_color
|
|
|
|
|
else -> defaultColorHeaderBg
|
2018-11-18 15:29:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-20 01:33:57 +01:00
|
|
|
|
fun setHeaderBackground(view : View) {
|
2018-11-18 17:54:31 +01:00
|
|
|
|
ViewCompat.setBackground(
|
|
|
|
|
view,
|
2018-12-01 00:02:18 +01:00
|
|
|
|
getAdaptiveRippleDrawable(
|
2018-11-19 23:46:14 +01:00
|
|
|
|
getHeaderBackgroundColor(),
|
|
|
|
|
getHeaderNameColor()
|
2018-11-18 17:54:31 +01:00
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-25 02:29:24 +02:00
|
|
|
|
// fun findListIndexByTimelineId(orderId : EntityId) : Int? {
|
|
|
|
|
// list_data.forEachIndexed { i, v ->
|
|
|
|
|
// if(v.getOrderId() == orderId) return i
|
|
|
|
|
// }
|
|
|
|
|
// return null
|
|
|
|
|
// }
|
2018-08-18 12:58:14 +02:00
|
|
|
|
|
2018-10-01 02:10:16 +02:00
|
|
|
|
init {
|
|
|
|
|
registerColumnId(column_id, this)
|
2018-09-07 07:51:59 +02:00
|
|
|
|
}
|
2018-11-18 03:38:52 +01:00
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
|
}
|