TootApiClientのisApiCancelleをval から suspend funに変更
This commit is contained in:
parent
c7ec0fa3cd
commit
b0ddddfe49
|
@ -541,7 +541,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
||||||
request
|
request
|
||||||
}) return Pair(result, null)
|
}) return Pair(result, null)
|
||||||
|
|
||||||
if (client.isApiCancelled) return Pair(null, null)
|
if (client.isApiCancelled()) return Pair(null, null)
|
||||||
|
|
||||||
val response = result.response!!
|
val response = result.response!!
|
||||||
if (!response.isSuccessful) {
|
if (!response.isSuccessful) {
|
||||||
|
@ -557,7 +557,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
|
||||||
}
|
}
|
||||||
client.publishApiProgressRatio(bytesRead.toInt(), bytesTotal.toInt())
|
client.publishApiProgressRatio(bytesRead.toInt(), bytesTotal.toInt())
|
||||||
}
|
}
|
||||||
if (client.isApiCancelled) return Pair(null, null)
|
if (client.isApiCancelled()) return Pair(null, null)
|
||||||
return Pair(result, ba)
|
return Pair(result, ba)
|
||||||
} catch (ignored: Throwable) {
|
} catch (ignored: Throwable) {
|
||||||
result.parseErrorResponse("?")
|
result.parseErrorResponse("?")
|
||||||
|
|
|
@ -344,7 +344,7 @@ suspend fun Context.accountListWithFilter(
|
||||||
.mapNotNull { it.await() }
|
.mapNotNull { it.await() }
|
||||||
.let { accountListReorder(it, pickupHost) }
|
.let { accountListReorder(it, pickupHost) }
|
||||||
}
|
}
|
||||||
if (client.isApiCancelled) null else TootApiResult()
|
if (client.isApiCancelled()) null else TootApiResult()
|
||||||
}
|
}
|
||||||
return resultList
|
return resultList
|
||||||
}
|
}
|
||||||
|
@ -352,7 +352,7 @@ suspend fun Context.accountListWithFilter(
|
||||||
suspend fun ActMain.accountListCanQuote(pickupHost: Host? = null) =
|
suspend fun ActMain.accountListCanQuote(pickupHost: Host? = null) =
|
||||||
accountListWithFilter(pickupHost) { client, a ->
|
accountListWithFilter(pickupHost) { client, a ->
|
||||||
when {
|
when {
|
||||||
client.isApiCancelled -> false
|
client.isApiCancelled() -> false
|
||||||
a.isPseudo -> false
|
a.isPseudo -> false
|
||||||
a.isMisskey -> true
|
a.isMisskey -> true
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -368,7 +368,7 @@ suspend fun ActMain.accountListCanQuote(pickupHost: Host? = null) =
|
||||||
suspend fun ActMain.accountListCanReaction(pickupHost: Host? = null) =
|
suspend fun ActMain.accountListCanReaction(pickupHost: Host? = null) =
|
||||||
accountListWithFilter(pickupHost) { client, a ->
|
accountListWithFilter(pickupHost) { client, a ->
|
||||||
when {
|
when {
|
||||||
client.isApiCancelled -> false
|
client.isApiCancelled() -> false
|
||||||
a.isPseudo -> false
|
a.isPseudo -> false
|
||||||
a.isMisskey -> true
|
a.isMisskey -> true
|
||||||
else -> {
|
else -> {
|
||||||
|
@ -384,7 +384,7 @@ suspend fun ActMain.accountListCanReaction(pickupHost: Host? = null) =
|
||||||
suspend fun ActMain.accountListCanSeeMyReactions(pickupHost: Host? = null) =
|
suspend fun ActMain.accountListCanSeeMyReactions(pickupHost: Host? = null) =
|
||||||
accountListWithFilter(pickupHost) { client, a ->
|
accountListWithFilter(pickupHost) { client, a ->
|
||||||
when {
|
when {
|
||||||
client.isApiCancelled -> false
|
client.isApiCancelled() -> false
|
||||||
a.isPseudo -> false
|
a.isPseudo -> false
|
||||||
else -> {
|
else -> {
|
||||||
val (ti, ri) = TootInstance.getEx(client.copy(), account = a)
|
val (ti, ri) = TootInstance.getEx(client.copy(), account = a)
|
||||||
|
|
|
@ -142,7 +142,8 @@ fun ActPost.restoreDraft(draft: JsonObject) {
|
||||||
fun isTaskCancelled() = !this.coroutineContext.isActive
|
fun isTaskCancelled() = !this.coroutineContext.isActive
|
||||||
|
|
||||||
var content = draft.string(DRAFT_CONTENT) ?: ""
|
var content = draft.string(DRAFT_CONTENT) ?: ""
|
||||||
val tmpAttachmentList = draft.jsonArray(DRAFT_ATTACHMENT_LIST)?.objectList()?.toMutableList()
|
val tmpAttachmentList =
|
||||||
|
draft.jsonArray(DRAFT_ATTACHMENT_LIST)?.objectList()?.toMutableList()
|
||||||
|
|
||||||
val accountDbId = draft.long(DRAFT_ACCOUNT_DB_ID) ?: -1L
|
val accountDbId = draft.long(DRAFT_ACCOUNT_DB_ID) ?: -1L
|
||||||
val account = SavedAccount.loadAccount(this@restoreDraft, accountDbId)
|
val account = SavedAccount.loadAccount(this@restoreDraft, accountDbId)
|
||||||
|
@ -175,8 +176,7 @@ fun ActPost.restoreDraft(draft: JsonObject) {
|
||||||
|
|
||||||
// アカウントがあるなら基本的にはすべての情報を復元できるはずだが、いくつか確認が必要だ
|
// アカウントがあるなら基本的にはすべての情報を復元できるはずだが、いくつか確認が必要だ
|
||||||
val apiClient = TootApiClient(this@restoreDraft, callback = object : TootApiCallback {
|
val apiClient = TootApiClient(this@restoreDraft, callback = object : TootApiCallback {
|
||||||
override val isApiCancelled: Boolean
|
override suspend fun isApiCancelled() = isTaskCancelled()
|
||||||
get() = isTaskCancelled()
|
|
||||||
|
|
||||||
override suspend fun publishApiProgress(s: String) {
|
override suspend fun publishApiProgress(s: String) {
|
||||||
progress.setMessageEx(s)
|
progress.setMessageEx(s)
|
||||||
|
@ -236,7 +236,8 @@ fun ActPost.restoreDraft(draft: JsonObject) {
|
||||||
val tmpAttachmentList = draft.jsonArray(DRAFT_ATTACHMENT_LIST)
|
val tmpAttachmentList = draft.jsonArray(DRAFT_ATTACHMENT_LIST)
|
||||||
val replyId = EntityId.from(draft, DRAFT_REPLY_ID)
|
val replyId = EntityId.from(draft, DRAFT_REPLY_ID)
|
||||||
|
|
||||||
val draftVisibility = TootVisibility.parseSavedVisibility(draft.string(DRAFT_VISIBILITY))
|
val draftVisibility =
|
||||||
|
TootVisibility.parseSavedVisibility(draft.string(DRAFT_VISIBILITY))
|
||||||
|
|
||||||
val evEmoji = DecodeOptions(this@restoreDraft, decodeEmoji = true)
|
val evEmoji = DecodeOptions(this@restoreDraft, decodeEmoji = true)
|
||||||
.decodeEmoji(content)
|
.decodeEmoji(content)
|
||||||
|
|
|
@ -120,8 +120,7 @@ private class TootTaskRunner(
|
||||||
//////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////
|
||||||
// implements TootApiClient.Callback
|
// implements TootApiClient.Callback
|
||||||
|
|
||||||
override val isApiCancelled: Boolean
|
override suspend fun isApiCancelled() = task?.isActive == false
|
||||||
get() = task?.isActive == false
|
|
||||||
|
|
||||||
override suspend fun publishApiProgress(s: String) {
|
override suspend fun publishApiProgress(s: String) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package jp.juggler.subwaytooter.api
|
package jp.juggler.subwaytooter.api
|
||||||
|
|
||||||
interface TootApiCallback {
|
interface TootApiCallback {
|
||||||
val isApiCancelled: Boolean
|
suspend fun isApiCancelled(): Boolean
|
||||||
suspend fun publishApiProgress(s: String) {}
|
suspend fun publishApiProgress(s: String) {}
|
||||||
suspend fun publishApiProgressRatio(value: Int, max: Int) {}
|
suspend fun publishApiProgressRatio(value: Int, max: Int) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package jp.juggler.subwaytooter.api
|
package jp.juggler.subwaytooter.api
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import jp.juggler.subwaytooter.*
|
import jp.juggler.subwaytooter.App1
|
||||||
|
import jp.juggler.subwaytooter.R
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
import jp.juggler.subwaytooter.api.entity.*
|
||||||
import jp.juggler.subwaytooter.pref.PrefDevice
|
import jp.juggler.subwaytooter.pref.PrefDevice
|
||||||
import jp.juggler.subwaytooter.pref.pref
|
import jp.juggler.subwaytooter.pref.pref
|
||||||
|
@ -169,8 +170,7 @@ class TootApiClient(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("unused")
|
@Suppress("unused")
|
||||||
internal val isApiCancelled: Boolean
|
internal suspend fun isApiCancelled() = callback.isApiCancelled()
|
||||||
get() = callback.isApiCancelled
|
|
||||||
|
|
||||||
suspend fun publishApiProgress(s: String) {
|
suspend fun publishApiProgress(s: String) {
|
||||||
callback.publishApiProgress(s)
|
callback.publishApiProgress(s)
|
||||||
|
@ -271,7 +271,7 @@ class TootApiClient(
|
||||||
): String? {
|
): String? {
|
||||||
val response = result.response ?: return null
|
val response = result.response ?: return null
|
||||||
try {
|
try {
|
||||||
if (isApiCancelled) return null
|
if (isApiCancelled()) return null
|
||||||
|
|
||||||
val request = response.request
|
val request = response.request
|
||||||
publishApiProgress(
|
publishApiProgress(
|
||||||
|
@ -283,7 +283,7 @@ class TootApiClient(
|
||||||
)
|
)
|
||||||
|
|
||||||
val bodyString = response.body?.string()
|
val bodyString = response.body?.string()
|
||||||
if (isApiCancelled) return null
|
if (isApiCancelled()) return null
|
||||||
|
|
||||||
// Misskey の /api/notes/favorites/create は 204(no content)を返す。ボディはカラになる。
|
// Misskey の /api/notes/favorites/create は 204(no content)を返す。ボディはカラになる。
|
||||||
if (bodyString?.isEmpty() != false && response.code in 200 until 300) {
|
if (bodyString?.isEmpty() != false && response.code in 200 until 300) {
|
||||||
|
@ -305,7 +305,7 @@ class TootApiClient(
|
||||||
result.bodyString = bodyString
|
result.bodyString = bodyString
|
||||||
bodyString
|
bodyString
|
||||||
}
|
}
|
||||||
}finally{
|
} finally {
|
||||||
response.body?.closeQuietly()
|
response.body?.closeQuietly()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,7 +318,7 @@ class TootApiClient(
|
||||||
jsonErrorParser: (json: JsonObject) -> String? = DEFAULT_JSON_ERROR_PARSER,
|
jsonErrorParser: (json: JsonObject) -> String? = DEFAULT_JSON_ERROR_PARSER,
|
||||||
): ByteArray? {
|
): ByteArray? {
|
||||||
|
|
||||||
if (isApiCancelled) return null
|
if (isApiCancelled()) return null
|
||||||
|
|
||||||
val response = result.response!!
|
val response = result.response!!
|
||||||
|
|
||||||
|
@ -332,7 +332,7 @@ class TootApiClient(
|
||||||
)
|
)
|
||||||
|
|
||||||
val bodyBytes = response.body?.bytes()
|
val bodyBytes = response.body?.bytes()
|
||||||
if (isApiCancelled) return null
|
if (isApiCancelled()) return null
|
||||||
|
|
||||||
if (!response.isSuccessful || bodyBytes?.isEmpty() != false) {
|
if (!response.isSuccessful || bodyBytes?.isEmpty() != false) {
|
||||||
result.parseErrorResponse(
|
result.parseErrorResponse(
|
||||||
|
@ -357,7 +357,7 @@ class TootApiClient(
|
||||||
): TootApiResult? {
|
): TootApiResult? {
|
||||||
try {
|
try {
|
||||||
readBodyBytes(result, progressPath, jsonErrorParser)
|
readBodyBytes(result, progressPath, jsonErrorParser)
|
||||||
?: return if (isApiCancelled) null else result
|
?: return if (isApiCancelled()) null else result
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
log.trace(ex)
|
log.trace(ex)
|
||||||
result.parseErrorResponse(result.bodyString ?: NO_INFORMATION)
|
result.parseErrorResponse(result.bodyString ?: NO_INFORMATION)
|
||||||
|
@ -372,7 +372,7 @@ class TootApiClient(
|
||||||
): TootApiResult? {
|
): TootApiResult? {
|
||||||
try {
|
try {
|
||||||
val bodyString = readBodyString(result, progressPath, jsonErrorParser)
|
val bodyString = readBodyString(result, progressPath, jsonErrorParser)
|
||||||
?: return if (isApiCancelled) null else result
|
?: return if (isApiCancelled()) null else result
|
||||||
|
|
||||||
result.data = bodyString
|
result.data = bodyString
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
|
@ -393,7 +393,7 @@ class TootApiClient(
|
||||||
|
|
||||||
try {
|
try {
|
||||||
var bodyString = readBodyString(result, progressPath, jsonErrorParser)
|
var bodyString = readBodyString(result, progressPath, jsonErrorParser)
|
||||||
?: return if (isApiCancelled) null else result
|
?: return if (isApiCancelled()) null else result
|
||||||
|
|
||||||
if (bodyString.isEmpty()) {
|
if (bodyString.isEmpty()) {
|
||||||
|
|
||||||
|
@ -1250,7 +1250,7 @@ class TootApiClient(
|
||||||
val request = requestBuilder.url(url).build()
|
val request = requestBuilder.url(url).build()
|
||||||
publishApiProgress(context.getString(R.string.request_api, request.method, path))
|
publishApiProgress(context.getString(R.string.request_api, request.method, path))
|
||||||
ws = httpClient.getWebSocket(request, wsListener)
|
ws = httpClient.getWebSocket(request, wsListener)
|
||||||
if (isApiCancelled) {
|
if (isApiCancelled()) {
|
||||||
ws.cancel()
|
ws.cancel()
|
||||||
return Pair(null, null)
|
return Pair(null, null)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,7 @@ class ColumnTask_Gap(
|
||||||
ctStarted.set(true)
|
ctStarted.set(true)
|
||||||
|
|
||||||
val client = TootApiClient(context, callback = object : TootApiCallback {
|
val client = TootApiClient(context, callback = object : TootApiCallback {
|
||||||
override val isApiCancelled: Boolean
|
override suspend fun isApiCancelled() = isCancelled || column.isDispose.get()
|
||||||
get() = isCancelled || column.isDispose.get()
|
|
||||||
|
|
||||||
override suspend fun publishApiProgress(s: String) {
|
override suspend fun publishApiProgress(s: String) {
|
||||||
runOnMainLooper {
|
runOnMainLooper {
|
||||||
|
|
|
@ -32,8 +32,7 @@ class ColumnTask_Loading(
|
||||||
}
|
}
|
||||||
|
|
||||||
val client = TootApiClient(context, callback = object : TootApiCallback {
|
val client = TootApiClient(context, callback = object : TootApiCallback {
|
||||||
override val isApiCancelled: Boolean
|
override suspend fun isApiCancelled() = isCancelled || column.isDispose.get()
|
||||||
get() = isCancelled || column.isDispose.get()
|
|
||||||
|
|
||||||
override suspend fun publishApiProgress(s: String) {
|
override suspend fun publishApiProgress(s: String) {
|
||||||
runOnMainLooper {
|
runOnMainLooper {
|
||||||
|
@ -1004,7 +1003,7 @@ class ColumnTask_Loading(
|
||||||
// 祖先
|
// 祖先
|
||||||
val listAsc = ArrayList<TootStatus>()
|
val listAsc = ArrayList<TootStatus>()
|
||||||
while (true) {
|
while (true) {
|
||||||
if (client.isApiCancelled) return null
|
if (client.isApiCancelled()) return null
|
||||||
queryParams["offset"] = listAsc.size
|
queryParams["offset"] = listAsc.size
|
||||||
result = client.request(
|
result = client.request(
|
||||||
"/api/notes/conversation", queryParams.toPostRequestBuilder()
|
"/api/notes/conversation", queryParams.toPostRequestBuilder()
|
||||||
|
@ -1021,7 +1020,7 @@ class ColumnTask_Loading(
|
||||||
var untilId: EntityId? = null
|
var untilId: EntityId? = null
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (client.isApiCancelled) return null
|
if (client.isApiCancelled()) return null
|
||||||
|
|
||||||
when {
|
when {
|
||||||
untilId == null -> {
|
untilId == null -> {
|
||||||
|
|
|
@ -33,8 +33,7 @@ class ColumnTask_Refresh(
|
||||||
ctStarted.set(true)
|
ctStarted.set(true)
|
||||||
|
|
||||||
val client = TootApiClient(context, callback = object : TootApiCallback {
|
val client = TootApiClient(context, callback = object : TootApiCallback {
|
||||||
override val isApiCancelled: Boolean
|
override suspend fun isApiCancelled() = isCancelled || column.isDispose.get()
|
||||||
get() = isCancelled || column.isDispose.get()
|
|
||||||
|
|
||||||
override suspend fun publishApiProgress(s: String) {
|
override suspend fun publishApiProgress(s: String) {
|
||||||
runOnMainLooper {
|
runOnMainLooper {
|
||||||
|
|
|
@ -689,7 +689,7 @@ enum class ColumnType(
|
||||||
loading = { client ->
|
loading = { client ->
|
||||||
val whoResult = column.loadProfileAccount(client, parser, true)
|
val whoResult = column.loadProfileAccount(client, parser, true)
|
||||||
when {
|
when {
|
||||||
client.isApiCancelled || column.whoAccount == null -> whoResult
|
client.isApiCancelled() || column.whoAccount == null -> whoResult
|
||||||
else -> column.profileTab.ct.loading(this, client)
|
else -> column.profileTab.ct.loading(this, client)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -395,8 +395,8 @@ class TaskRunner(
|
||||||
{ currentCall = WeakReference(it) }
|
{ currentCall = WeakReference(it) }
|
||||||
|
|
||||||
private val client = TootApiClient(context, callback = object : TootApiCallback {
|
private val client = TootApiClient(context, callback = object : TootApiCallback {
|
||||||
override val isApiCancelled: Boolean
|
override suspend fun isApiCancelled() =
|
||||||
get() = job.isJobCancelled || (suspendJob?.isCancelled == true)
|
job.isJobCancelled || (suspendJob?.isCancelled == true)
|
||||||
}).apply {
|
}).apply {
|
||||||
currentCallCallback = onCallCreated
|
currentCallCallback = onCallCreated
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ object MspHelper {
|
||||||
var user_token: String? = PrefS.spMspUserToken(pref)
|
var user_token: String? = PrefS.spMspUserToken(pref)
|
||||||
|
|
||||||
for (nTry in 0 until 3) {
|
for (nTry in 0 until 3) {
|
||||||
if (callback.isApiCancelled) return null
|
if (callback.isApiCancelled()) return null
|
||||||
|
|
||||||
// ユーザトークンがなければ取得する
|
// ユーザトークンがなければ取得する
|
||||||
if (user_token == null || user_token.isEmpty()) {
|
if (user_token == null || user_token.isEmpty()) {
|
||||||
|
|
|
@ -1,21 +1,20 @@
|
||||||
package jp.juggler.subwaytooter.streaming
|
package jp.juggler.subwaytooter.streaming
|
||||||
|
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
|
||||||
import jp.juggler.subwaytooter.api.TootApiCallback
|
import jp.juggler.subwaytooter.api.TootApiCallback
|
||||||
import jp.juggler.subwaytooter.api.TootApiClient
|
import jp.juggler.subwaytooter.api.TootApiClient
|
||||||
import jp.juggler.subwaytooter.api.TootApiResult
|
import jp.juggler.subwaytooter.api.TootApiResult
|
||||||
import jp.juggler.subwaytooter.api.entity.*
|
import jp.juggler.subwaytooter.api.entity.*
|
||||||
import jp.juggler.subwaytooter.column.onStatusRemoved
|
import jp.juggler.subwaytooter.column.onStatusRemoved
|
||||||
import jp.juggler.subwaytooter.column.reloadFilter
|
import jp.juggler.subwaytooter.column.reloadFilter
|
||||||
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import okhttp3.WebSocket
|
import okhttp3.WebSocket
|
||||||
import okhttp3.WebSocketListener
|
import okhttp3.WebSocketListener
|
||||||
import java.net.ProtocolException
|
import java.net.ProtocolException
|
||||||
import java.net.SocketException
|
import java.net.SocketException
|
||||||
import java.util.ArrayList
|
import java.util.*
|
||||||
import java.util.HashSet
|
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
@ -38,8 +37,7 @@ class StreamConnection(
|
||||||
|
|
||||||
private val isDisposed = AtomicBoolean(false)
|
private val isDisposed = AtomicBoolean(false)
|
||||||
|
|
||||||
override val isApiCancelled: Boolean
|
override suspend fun isApiCancelled() = isDisposed.get()
|
||||||
get() = isDisposed.get()
|
|
||||||
|
|
||||||
val client = TootApiClient(manager.context, callback = this)
|
val client = TootApiClient(manager.context, callback = this)
|
||||||
.apply { account = acctGroup.account }
|
.apply { account = acctGroup.account }
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package jp.juggler.subwaytooter.streaming
|
package jp.juggler.subwaytooter.streaming
|
||||||
|
|
||||||
import jp.juggler.subwaytooter.AppState
|
import jp.juggler.subwaytooter.AppState
|
||||||
import jp.juggler.subwaytooter.column.Column
|
|
||||||
import jp.juggler.subwaytooter.pref.PrefB
|
|
||||||
import jp.juggler.subwaytooter.api.TootApiCallback
|
import jp.juggler.subwaytooter.api.TootApiCallback
|
||||||
import jp.juggler.subwaytooter.api.TootApiClient
|
import jp.juggler.subwaytooter.api.TootApiClient
|
||||||
import jp.juggler.subwaytooter.api.entity.Acct
|
import jp.juggler.subwaytooter.api.entity.Acct
|
||||||
import jp.juggler.subwaytooter.api.entity.TootInstance
|
import jp.juggler.subwaytooter.api.entity.TootInstance
|
||||||
|
import jp.juggler.subwaytooter.column.Column
|
||||||
|
import jp.juggler.subwaytooter.pref.PrefB
|
||||||
import jp.juggler.subwaytooter.table.HighlightWord
|
import jp.juggler.subwaytooter.table.HighlightWord
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.util.LogCategory
|
import jp.juggler.util.LogCategory
|
||||||
|
@ -37,7 +37,7 @@ class StreamManager(val appState: AppState) {
|
||||||
val client = TootApiClient(
|
val client = TootApiClient(
|
||||||
appState.context,
|
appState.context,
|
||||||
callback = object : TootApiCallback {
|
callback = object : TootApiCallback {
|
||||||
override val isApiCancelled = false
|
override suspend fun isApiCancelled() = false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,12 @@ import jp.juggler.subwaytooter.api.entity.*
|
||||||
import jp.juggler.subwaytooter.api.runApiTask
|
import jp.juggler.subwaytooter.api.runApiTask
|
||||||
import jp.juggler.subwaytooter.table.SavedAccount
|
import jp.juggler.subwaytooter.table.SavedAccount
|
||||||
import jp.juggler.util.*
|
import jp.juggler.util.*
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.channels.ClosedReceiveChannelException
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.isActive
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.MediaType
|
import okhttp3.MediaType
|
||||||
import okhttp3.MediaType.Companion.toMediaType
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
|
@ -23,8 +28,8 @@ import okhttp3.RequestBody
|
||||||
import okio.BufferedSink
|
import okio.BufferedSink
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.CancellationException
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
import kotlin.coroutines.coroutineContext
|
||||||
|
|
||||||
class AttachmentRequest(
|
class AttachmentRequest(
|
||||||
val account: SavedAccount,
|
val account: SavedAccount,
|
||||||
|
@ -32,7 +37,7 @@ class AttachmentRequest(
|
||||||
val uri: Uri,
|
val uri: Uri,
|
||||||
val mimeType: String,
|
val mimeType: String,
|
||||||
val isReply: Boolean,
|
val isReply: Boolean,
|
||||||
val onUploadEnd: () -> Unit ={},
|
val onUploadEnd: () -> Unit = {},
|
||||||
)
|
)
|
||||||
|
|
||||||
class AttachmentUploader(
|
class AttachmentUploader(
|
||||||
|
@ -153,19 +158,47 @@ class AttachmentUploader(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val context: Context = contextArg.applicationContext
|
private val context = contextArg.applicationContext!!
|
||||||
private val attachmentQueue = ConcurrentLinkedQueue<AttachmentRequest>()
|
|
||||||
private var attachmentWorker: AttachmentWorker? = null
|
|
||||||
private var lastAttachmentAdd = 0L
|
private var lastAttachmentAdd = 0L
|
||||||
private var lastAttachmentComplete = 0L
|
private var lastAttachmentComplete = 0L
|
||||||
|
private var channel: Channel<AttachmentRequest>? = null
|
||||||
|
|
||||||
fun onActivityDestroy() {
|
private fun prepareChannel(): Channel<AttachmentRequest> {
|
||||||
attachmentWorker?.cancel()
|
// double check before/after lock
|
||||||
|
channel?.let { return it }
|
||||||
|
synchronized(this) {
|
||||||
|
channel?.let { return it }
|
||||||
|
return Channel<AttachmentRequest>(capacity = Channel.UNLIMITED)
|
||||||
|
.also {
|
||||||
|
channel = it
|
||||||
|
launchIO {
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
handleRequest(it.receive())
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
when (ex) {
|
||||||
|
is CancellationException, is ClosedReceiveChannelException -> break
|
||||||
|
else -> context.showToast(ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addRequest(
|
fun onActivityDestroy() {
|
||||||
request: AttachmentRequest,
|
try {
|
||||||
) {
|
synchronized(this) {
|
||||||
|
channel?.close()
|
||||||
|
channel = null
|
||||||
|
}
|
||||||
|
} catch (ex: Throwable) {
|
||||||
|
log.e(ex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addRequest(request: AttachmentRequest) {
|
||||||
// アップロード開始トースト(連発しない)
|
// アップロード開始トースト(連発しない)
|
||||||
val now = System.currentTimeMillis()
|
val now = System.currentTimeMillis()
|
||||||
if (now - lastAttachmentAdd >= 5000L) {
|
if (now - lastAttachmentAdd >= 5000L) {
|
||||||
|
@ -176,66 +209,13 @@ class AttachmentUploader(
|
||||||
// マストドンは添付メディアをID順に表示するため
|
// マストドンは添付メディアをID順に表示するため
|
||||||
// 画像が複数ある場合は一つずつ処理する必要がある
|
// 画像が複数ある場合は一つずつ処理する必要がある
|
||||||
// 投稿画面ごとに1スレッドだけ作成してバックグラウンド処理を行う
|
// 投稿画面ごとに1スレッドだけ作成してバックグラウンド処理を行う
|
||||||
attachmentQueue.add(request)
|
launchIO { prepareChannel().send(request) }
|
||||||
val oldWorker = attachmentWorker
|
|
||||||
if (oldWorker == null || !oldWorker.isAlive || oldWorker.isCancelled.get()) {
|
|
||||||
oldWorker?.cancel()
|
|
||||||
attachmentWorker = AttachmentWorker()
|
|
||||||
} else {
|
|
||||||
oldWorker.notifyEx()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleResult(request: AttachmentRequest, result: TootApiResult?) {
|
private suspend fun handleRequest(request: AttachmentRequest) {
|
||||||
val pa = request.pa
|
|
||||||
pa.status = when (pa.attachment) {
|
|
||||||
null -> {
|
|
||||||
if (result != null) {
|
|
||||||
context.showToast(
|
|
||||||
true,
|
|
||||||
"${result.error} ${result.response?.request?.method} ${result.response?.request?.url}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
PostAttachment.Status.Error
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
val now = System.currentTimeMillis()
|
|
||||||
if (now - lastAttachmentComplete >= 5000L) {
|
|
||||||
context.showToast(false, R.string.attachment_uploaded)
|
|
||||||
}
|
|
||||||
lastAttachmentComplete = now
|
|
||||||
|
|
||||||
PostAttachment.Status.Ok
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 投稿中に画面回転があった場合、新しい画面のコールバックを呼び出す必要がある
|
|
||||||
pa.callback?.onPostAttachmentComplete(pa)
|
|
||||||
}
|
|
||||||
|
|
||||||
inner class AttachmentWorker : WorkerBase() {
|
|
||||||
|
|
||||||
internal val isCancelled = AtomicBoolean(false)
|
|
||||||
|
|
||||||
override fun cancel() {
|
|
||||||
isCancelled.set(true)
|
|
||||||
notifyEx()
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun run() {
|
|
||||||
try {
|
|
||||||
while (!isCancelled.get()) {
|
|
||||||
val request = attachmentQueue.poll()
|
|
||||||
if (request == null) {
|
|
||||||
waitEx(86400000L)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
val result = request.upload()
|
val result = request.upload()
|
||||||
handler.post { handleResult(request, result) }
|
withContext(Dispatchers.Main) {
|
||||||
}
|
handleResult(request, result)
|
||||||
} catch (ex: Throwable) {
|
|
||||||
log.trace(ex)
|
|
||||||
log.e(ex, "AttachmentWorker")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,10 +224,8 @@ class AttachmentUploader(
|
||||||
if (mimeType.isEmpty()) return TootApiResult("mime_type is empty.")
|
if (mimeType.isEmpty()) return TootApiResult("mime_type is empty.")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
val client = TootApiClient(context, callback = object : TootApiCallback {
|
val client = TootApiClient(context, callback = object : TootApiCallback {
|
||||||
override val isApiCancelled: Boolean
|
override suspend fun isApiCancelled() = !coroutineContext.isActive
|
||||||
get() = isCancelled.get()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
client.account = account
|
client.account = account
|
||||||
|
@ -260,9 +238,15 @@ class AttachmentUploader(
|
||||||
return TootApiResult(context.getString(R.string.pixelfed_does_not_allow_reply_with_media))
|
return TootApiResult(context.getString(R.string.pixelfed_does_not_allow_reply_with_media))
|
||||||
}
|
}
|
||||||
if (!acceptableMimeTypesPixelfed.contains(mimeType)) {
|
if (!acceptableMimeTypesPixelfed.contains(mimeType)) {
|
||||||
return TootApiResult(context.getString(R.string.mime_type_not_acceptable, mimeType))
|
return TootApiResult(
|
||||||
|
context.getString(
|
||||||
|
R.string.mime_type_not_acceptable,
|
||||||
|
mimeType
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 設定からリサイズ指定を読む
|
// 設定からリサイズ指定を読む
|
||||||
val resizeConfig = account.getResizeConfig()
|
val resizeConfig = account.getResizeConfig()
|
||||||
|
|
||||||
|
@ -408,7 +392,8 @@ class AttachmentUploader(
|
||||||
}
|
}
|
||||||
|
|
||||||
// ポーリングして処理完了を待つ
|
// ポーリングして処理完了を待つ
|
||||||
val id = parseItem(::TootAttachment, ServiceType.MASTODON, result?.jsonObject)
|
val id =
|
||||||
|
parseItem(::TootAttachment, ServiceType.MASTODON, result?.jsonObject)
|
||||||
?.id
|
?.id
|
||||||
?: return TootApiResult("/api/v2/media did not return the media ID.")
|
?: return TootApiResult("/api/v2/media did not return the media ID.")
|
||||||
|
|
||||||
|
@ -441,7 +426,8 @@ class AttachmentUploader(
|
||||||
|
|
||||||
val jsonObject = result?.jsonObject
|
val jsonObject = result?.jsonObject
|
||||||
if (jsonObject != null) {
|
if (jsonObject != null) {
|
||||||
when (val a = parseItem(::TootAttachment, ServiceType.MASTODON, jsonObject)) {
|
when (val a =
|
||||||
|
parseItem(::TootAttachment, ServiceType.MASTODON, jsonObject)) {
|
||||||
null -> result.error = "TootAttachment.parse failed"
|
null -> result.error = "TootAttachment.parse failed"
|
||||||
else -> pa.attachment = a
|
else -> pa.attachment = a
|
||||||
}
|
}
|
||||||
|
@ -452,10 +438,35 @@ class AttachmentUploader(
|
||||||
return TootApiResult(ex.withCaption("read failed."))
|
return TootApiResult(ex.withCaption("read failed."))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleResult(request: AttachmentRequest, result: TootApiResult?) {
|
||||||
|
val pa = request.pa
|
||||||
|
pa.status = when (pa.attachment) {
|
||||||
|
null -> {
|
||||||
|
if (result != null) {
|
||||||
|
context.showToast(
|
||||||
|
true,
|
||||||
|
"${result.error} ${result.response?.request?.method} ${result.response?.request?.url}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PostAttachment.Status.Error
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
if (now - lastAttachmentComplete >= 5000L) {
|
||||||
|
context.showToast(false, R.string.attachment_uploaded)
|
||||||
|
}
|
||||||
|
lastAttachmentComplete = now
|
||||||
|
|
||||||
|
PostAttachment.Status.Ok
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 投稿中に画面回転があった場合、新しい画面のコールバックを呼び出す必要がある
|
||||||
|
pa.callback?.onPostAttachmentComplete(pa)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal interface InputStreamOpener {
|
internal interface InputStreamOpener {
|
||||||
|
|
||||||
val mimeType: String
|
val mimeType: String
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
|
@ -538,7 +549,8 @@ class AttachmentUploader(
|
||||||
|
|
||||||
@Throws(IOException::class)
|
@Throws(IOException::class)
|
||||||
override fun open(): InputStream {
|
override fun open(): InputStream {
|
||||||
return context.contentResolver.openInputStream(uri) ?: error("openInputStream returns null")
|
return context.contentResolver.openInputStream(uri)
|
||||||
|
?: error("openInputStream returns null")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun deleteTempFile() {
|
override fun deleteTempFile() {
|
||||||
|
@ -760,7 +772,8 @@ class AttachmentUploader(
|
||||||
}
|
}
|
||||||
.toPutRequestBuilder()
|
.toPutRequestBuilder()
|
||||||
)?.also { result ->
|
)?.also { result ->
|
||||||
resultAttachment = parseItem(::TootAttachment, ServiceType.MASTODON, result.jsonObject)
|
resultAttachment =
|
||||||
|
parseItem(::TootAttachment, ServiceType.MASTODON, result.jsonObject)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
|
|
|
@ -22,7 +22,7 @@ private object EndlessScope : CoroutineScope {
|
||||||
// メインスレッド上で動作するコルーチンを起動して、終了を待たずにリターンする。
|
// メインスレッド上で動作するコルーチンを起動して、終了を待たずにリターンする。
|
||||||
// 起動されたアクティビティのライフサイクルに関わらず中断しない。
|
// 起動されたアクティビティのライフサイクルに関わらず中断しない。
|
||||||
fun launchMain(block: suspend CoroutineScope.() -> Unit): Job =
|
fun launchMain(block: suspend CoroutineScope.() -> Unit): Job =
|
||||||
EndlessScope.launch(context = Dispatchers.Main) {
|
EndlessScope.launch(context = Dispatchers.Main.immediate) {
|
||||||
try {
|
try {
|
||||||
block()
|
block()
|
||||||
} catch (ex: CancellationException) {
|
} catch (ex: CancellationException) {
|
||||||
|
|
|
@ -48,7 +48,7 @@ object ToastUtils {
|
||||||
fun Context.showToast(bLong: Boolean, caption: String?): Boolean =
|
fun Context.showToast(bLong: Boolean, caption: String?): Boolean =
|
||||||
ToastUtils.showToastImpl(this, bLong, caption ?: "(null)")
|
ToastUtils.showToastImpl(this, bLong, caption ?: "(null)")
|
||||||
|
|
||||||
fun Context.showToast(ex: Throwable, caption: String): Boolean =
|
fun Context.showToast(ex: Throwable, caption: String="error."): Boolean =
|
||||||
ToastUtils.showToastImpl(this, true, ex.withCaption(caption))
|
ToastUtils.showToastImpl(this, true, ex.withCaption(caption))
|
||||||
|
|
||||||
fun Context.showToast(bLong: Boolean, stringId: Int, vararg args: Any): Boolean =
|
fun Context.showToast(bLong: Boolean, stringId: Int, vararg args: Any): Boolean =
|
||||||
|
|
Loading…
Reference in New Issue