SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/streaming/StreamGroupAcct.kt

166 lines
5.4 KiB
Kotlin
Raw Normal View History

2020-12-21 03:13:03 +01:00
package jp.juggler.subwaytooter.streaming
import jp.juggler.subwaytooter.api.TootParser
import jp.juggler.subwaytooter.api.entity.TootInstance
import jp.juggler.subwaytooter.table.SavedAccount
import jp.juggler.util.log.LogCategory
2020-12-21 03:13:03 +01:00
import java.util.concurrent.ConcurrentHashMap
// ストリーミング接続をacct単位でグルーピングする
class StreamGroupAcct(
private val manager: StreamManager,
2020-12-21 21:16:33 +01:00
val account: SavedAccount,
var ti: TootInstance,
2020-12-21 03:13:03 +01:00
) {
companion object {
private val log = LogCategory("StreamGroupAcct")
}
2020-12-21 21:16:33 +01:00
val parser: TootParser = TootParser(manager.appState.context, linkHelper = account)
2020-12-21 03:13:03 +01:00
2020-12-26 22:03:58 +01:00
val keyGroups = ConcurrentHashMap<StreamSpec, StreamGroup>()
2020-12-21 03:13:03 +01:00
2020-12-21 21:16:33 +01:00
// 接続を束ねない場合に使われる
private val connections = ConcurrentHashMap<StreamSpec, StreamConnection>()
2020-12-21 03:13:03 +01:00
2020-12-21 21:16:33 +01:00
// 接続を束ねる場合に使われる
2020-12-21 03:13:03 +01:00
private var mergedConnection: StreamConnection? = null
2020-12-21 21:16:33 +01:00
// カラムIDから出力先へのマップ
@Volatile
2020-12-26 22:03:58 +01:00
private var destinations = ConcurrentHashMap<Int, StreamRelation>()
2020-12-21 21:16:33 +01:00
2020-12-26 22:03:58 +01:00
fun addSpec(dst: StreamRelation) {
2020-12-21 21:16:33 +01:00
val spec = dst.spec
var group = keyGroups[spec]
2020-12-21 03:13:03 +01:00
if (group == null) {
2020-12-26 22:03:58 +01:00
group = StreamGroup(spec)
2020-12-21 21:16:33 +01:00
keyGroups[spec] = group
2020-12-21 03:13:03 +01:00
}
group.destinations[dst.columnInternalId] = dst
2020-12-21 03:13:03 +01:00
}
private fun updateDestinations() {
destinations = ConcurrentHashMap<Int, StreamRelation>().apply {
keyGroups.values.forEach { group ->
group.destinations.values.forEach { relation ->
put(relation.columnInternalId, relation)
}
}
}
}
// マージではなく、作られてデータを追加された直後に呼ばれる
fun initialize() {
updateDestinations()
}
2020-12-21 03:13:03 +01:00
fun merge(newServer: StreamGroupAcct) {
// 新スペックの値をコピー
this.ti = newServer.ti
2020-12-21 21:16:33 +01:00
newServer.keyGroups.entries.forEach {
keyGroups[it.key] = it.value
2020-12-21 03:13:03 +01:00
}
// 新グループにないグループを削除
2020-12-21 21:16:33 +01:00
keyGroups.entries.toList().forEach {
if (!newServer.keyGroups.containsKey(it.key)) keyGroups.remove(it.key)
2020-12-21 03:13:03 +01:00
}
// グループにない接続を破棄
connections.entries.toList().forEach {
2020-12-21 21:16:33 +01:00
if (!keyGroups.containsKey(it.key)) {
2020-12-21 03:13:03 +01:00
it.value.dispose()
connections.remove(it.key)
}
}
updateDestinations()
2020-12-21 03:13:03 +01:00
}
// このオブジェクトはもう使われなくなる
fun dispose() {
connections.values.forEach { it.dispose() }
connections.clear()
mergedConnection?.dispose()
mergedConnection = null
2020-12-21 21:16:33 +01:00
keyGroups.clear()
2020-12-21 03:13:03 +01:00
}
private fun findConnection(spec: StreamSpec): StreamConnection? =
when (val mergedConnection = this.mergedConnection) {
null -> when (val conn = connections[spec]) {
null -> {
log.w("findConnection: missing connection for ${spec.name}")
null
}
else -> conn
}
else -> mergedConnection
}
2020-12-21 03:13:03 +01:00
// ストリーミング接続インジケータ
fun getStreamStatus(columnInternalId: Int): StreamStatus =
when (val spec = destinations[columnInternalId]?.spec) {
null -> {
log.w("getStreamStatus: missing destination for ${account.acct.pretty}")
StreamStatus.Missing
}
else -> findConnection(spec)?.getStreamStatus(spec) ?: StreamStatus.Missing
}
2020-12-21 03:13:03 +01:00
suspend fun updateConnection() {
2020-12-21 21:16:33 +01:00
val multiplex = if (account.isMastodon) {
2020-12-21 03:13:03 +01:00
ti.versionGE(TootInstance.VERSION_3_3_0_rc1)
} else {
2020-12-21 21:16:33 +01:00
account.misskeyVersion >= 11
2020-12-21 03:13:03 +01:00
}
if (multiplex) {
connections.values.forEach { it.dispose() }
connections.clear()
if (destinations.isEmpty()) {
2020-12-21 03:13:03 +01:00
mergedConnection?.dispose()
mergedConnection = null
} else {
2020-12-21 03:13:03 +01:00
if (mergedConnection == null) {
mergedConnection = StreamConnection(
manager,
this,
2020-12-21 21:16:33 +01:00
spec = null,
name = "[${account.acct.pretty}:multiplex]",
2020-12-21 03:13:03 +01:00
)
}
mergedConnection?.updateConnection()
}
} else {
mergedConnection?.dispose()
mergedConnection = null
2020-12-21 21:16:33 +01:00
keyGroups.entries.forEach { pair ->
val (streamKey, group) = pair
2020-12-21 03:13:03 +01:00
var conn = connections[streamKey]
if (conn == null) {
conn = StreamConnection(
manager,
this,
2020-12-21 21:16:33 +01:00
spec = group.spec,
"[${account.acct.pretty}:${group.spec.name}]",
2020-12-21 03:13:03 +01:00
)
connections[streamKey] = conn
}
conn.updateConnection()
}
}
}
fun getConnection(internalId: Int) =
mergedConnection ?: destinations[internalId]?.spec?.let { connections[it] }
2020-12-21 03:13:03 +01:00
}