diff --git a/app/src/androidTest/java/jp/juggler/subwaytooter/api/TestTootApiClient.kt b/app/src/androidTest/java/jp/juggler/subwaytooter/api/TestTootApiClient.kt
index d860848b..370e9a7c 100644
--- a/app/src/androidTest/java/jp/juggler/subwaytooter/api/TestTootApiClient.kt
+++ b/app/src/androidTest/java/jp/juggler/subwaytooter/api/TestTootApiClient.kt
@@ -22,6 +22,7 @@ import okio.ByteString
import org.junit.Assert.*
import org.junit.Test
import org.junit.runner.RunWith
+import java.util.concurrent.atomic.AtomicReference
@Suppress("MemberVisibilityCanPrivate")
@RunWith(AndroidJUnit4::class)
@@ -354,7 +355,7 @@ class TestTootApiClient {
// json error
response = createResponseErrorCode()
- message = TootApiClient.simplifyErrorHtml(response, response.body?.string() ?: "")
+ message = TootApiClient.simplifyErrorHtml(response)
assertEquals("Error!", message)
// HTML error
@@ -367,7 +368,7 @@ class TestTootApiClient {
.body("""
Error!""".toResponseBody(mediaTypeHtml))
.build()
- message = TootApiClient.simplifyErrorHtml(response, response.body?.string() ?: "")
+ message = TootApiClient.simplifyErrorHtml(response)
assertEquals("Error!", message)
// other error
@@ -383,7 +384,7 @@ class TestTootApiClient {
.body("Error!".toResponseBody("text/plain".toMediaType()))
.build()
- message = TootApiClient.simplifyErrorHtml(response, response.body?.string() ?: "")
+ message =TootApiClient.simplifyErrorHtml(response)
assertEquals("Error!", message)
// empty body
@@ -399,7 +400,7 @@ class TestTootApiClient {
.body("".toResponseBody("text/plain".toMediaType()))
.build()
- message = TootApiClient.simplifyErrorHtml(response, response.body?.string() ?: "")
+ message = TootApiClient.simplifyErrorHtml(response=response,caption="caption" )
assertEquals("", message)
}
@@ -423,7 +424,7 @@ class TestTootApiClient {
.message("This is test")
.build()
- message = TootApiClient.formatResponse(response, "caption", null)
+ message = TootApiClient.formatResponse(response,"caption")
assertEquals("(HTTP 500 This is test) caption", message)
@@ -440,7 +441,7 @@ class TestTootApiClient {
.body("""{"error":"Error!"}""".toResponseBody(MEDIA_TYPE_JSON))
.build()
- message = TootApiClient.formatResponse(response, "caption", null)
+ message = TootApiClient.formatResponse(response,"caption")
assertEquals("Error! (HTTP 500 status-message) caption", message)
// json error (after reading body)
@@ -459,7 +460,7 @@ class TestTootApiClient {
bodyString = response.body?.string()
- message = TootApiClient.formatResponse(response, "caption", bodyString)
+ message = TootApiClient.formatResponse(response,"caption",bodyString)
assertEquals("Error! (HTTP 500 status-message) caption", message)
// without status message
@@ -477,9 +478,8 @@ class TestTootApiClient {
bodyString = response.body?.string()
- message = TootApiClient.formatResponse(response, "caption", bodyString)
+ message = TootApiClient.formatResponse(response = response,caption = "caption",bodyString = bodyString)
assertEquals("Error! (HTTP 500) caption", message)
-
}
@Test
@@ -1051,7 +1051,8 @@ class TestTootApiClient {
println(url)
// ブラウザからコールバックで受け取ったcodeを処理する
- result = client.authentication2Mastodon(clientName, "DUMMY_CODE")
+ val refToken = AtomicReference(null)
+ result = client.authentication2Mastodon(clientName, "DUMMY_CODE",refToken)
jsonObject = result?.jsonObject
assertNotNull(jsonObject)
if (jsonObject == null) return@runBlocking
diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.kt b/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.kt
index f7d15046..6f1d801f 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/ActAccountSetting.kt
@@ -167,9 +167,15 @@ class ActAccountSetting : AsyncActivity(), View.OnClickListener,
private lateinit var spResizeImage: Spinner
- private class ResizeItems(val config: ResizeConfig, val caption: String)
+ private lateinit var spPushPolicy: Spinner
- private lateinit var imageResizeItems: List
+ private class ResizeItem(val config: ResizeConfig, val caption: String)
+
+ private lateinit var imageResizeItems: List
+
+ private class PushPolicyItem(val id: String?, val caption: String)
+
+ private lateinit var pushPolicyItems: List
///////////////////////////////////////////////////
@@ -336,6 +342,7 @@ class ActAccountSetting : AsyncActivity(), View.OnClickListener,
etMediaSizeMax = findViewById(R.id.etMediaSizeMax)
etMovieSizeMax = findViewById(R.id.etMovieSizeMax)
spResizeImage = findViewById(R.id.spResizeImage)
+ spPushPolicy = findViewById(R.id.spPushPolicy)
imageResizeItems = SavedAccount.resizeConfigList.map {
val caption = when (it.type) {
@@ -355,7 +362,7 @@ class ActAccountSetting : AsyncActivity(), View.OnClickListener,
)
}
}
- ResizeItems(it, caption)
+ ResizeItem(it, caption)
}
spResizeImage.adapter = ArrayAdapter(
this,
@@ -365,6 +372,22 @@ class ActAccountSetting : AsyncActivity(), View.OnClickListener,
setDropDownViewResource(R.layout.lv_spinner_dropdown)
}
+ pushPolicyItems = listOf(
+ PushPolicyItem(null, getString(R.string.unspecified)),
+ PushPolicyItem("all", getString(R.string.all)),
+ PushPolicyItem("followed", getString(R.string.following)),
+ PushPolicyItem("follower", getString(R.string.followers)),
+ PushPolicyItem("none", getString(R.string.no_one)),
+ )
+
+ spPushPolicy.adapter = ArrayAdapter(
+ this,
+ android.R.layout.simple_spinner_item,
+ pushPolicyItems.map { it.caption }.toTypedArray()
+ ).apply {
+ setDropDownViewResource(R.layout.lv_spinner_dropdown)
+ }
+
listEtFieldName = arrayOf(
R.id.etFieldName1,
R.id.etFieldName2,
@@ -438,6 +461,7 @@ class ActAccountSetting : AsyncActivity(), View.OnClickListener,
spResizeImage.onItemSelectedListener = this
+ spPushPolicy.onItemSelectedListener = this
btnNotificationStyleEditReply.vg(Pref.bpSeparateReplyNotificationGroup(pref))
@@ -587,18 +611,28 @@ class ActAccountSetting : AsyncActivity(), View.OnClickListener,
etMediaSizeMax.setText(a.image_max_megabytes ?: "")
etMovieSizeMax.setText(a.movie_max_megabytes ?: "")
} else {
- etMediaSizeMax.setText(a.image_max_megabytes
- ?: a.getImageMaxBytes(ti).div(1000000).toString())
- etMovieSizeMax.setText(a.movie_max_megabytes
- ?: a.getMovieMaxBytes(ti).div(1000000).toString())
+ etMediaSizeMax.setText(
+ a.image_max_megabytes
+ ?: a.getImageMaxBytes(ti).div(1000000).toString()
+ )
+ etMovieSizeMax.setText(
+ a.movie_max_megabytes
+ ?: a.getMovieMaxBytes(ti).div(1000000).toString()
+ )
}
val currentResizeConfig = a.getResizeConfig()
var index = imageResizeItems.indexOfFirst { it.config.spec == currentResizeConfig.spec }
log.d("ResizeItem current ${currentResizeConfig.spec} index=$index ")
- if (index == -1) index = imageResizeItems.indexOfFirst { it.config.spec == SavedAccount.defaultResizeConfig.spec }
+ if (index == -1) index =
+ imageResizeItems.indexOfFirst { it.config.spec == SavedAccount.defaultResizeConfig.spec }
spResizeImage.setSelection(index, false)
+ val currentPushPolicy = a.push_policy
+ index = pushPolicyItems.indexOfFirst { it.id == currentPushPolicy }
+ if (index == -1) index = 0
+ spPushPolicy.setSelection(index, false)
+
showVisibility()
showAcctColor()
}
@@ -657,6 +691,9 @@ class ActAccountSetting : AsyncActivity(), View.OnClickListener,
?: SavedAccount.defaultResizeConfig
).spec
+ account.push_policy =
+ pushPolicyItems.elementAtOrNull(spPushPolicy.selectedItemPosition)?.id
+
account.saveSetting()
}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActMediaViewer.kt b/app/src/main/java/jp/juggler/subwaytooter/ActMediaViewer.kt
index b892c3ee..12157a57 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/ActMediaViewer.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/ActMediaViewer.kt
@@ -577,9 +577,9 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
if (client.isApiCancelled) return Pair(null, null)
- val response = requireNotNull(result.response)
+ val response = result.response!!
if (!response.isSuccessful) {
- result.setError(TootApiClient.formatResponse(response, result.caption))
+ result.parseErrorResponse()
return Pair(result, null)
}
@@ -594,7 +594,7 @@ class ActMediaViewer : AppCompatActivity(), View.OnClickListener {
if (client.isApiCancelled) return Pair(null, null)
return Pair(result, ba)
} catch (ex: Throwable) {
- result.setError(TootApiClient.formatResponse(response, result.caption, "?"))
+ result.parseErrorResponse( "?")
return Pair(result, null)
}
}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/ActPost.kt b/app/src/main/java/jp/juggler/subwaytooter/ActPost.kt
index ed793ab4..6c93f002 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/ActPost.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/ActPost.kt
@@ -369,17 +369,14 @@ class ActPost : AsyncActivity(),
val request = Request.Builder().url(url).build()
val call = App1.ok_http_client.newCall(request)
val response = call.await()
- if (response.isSuccessful) {
- return true
- }
- log.e(TootApiClient.formatResponse(response, "check_exist failed."))
+ if (response.isSuccessful) return true
+
+ log.e(TootApiClient.formatResponse(response,"check_exist failed."))
} catch (ex: Throwable) {
log.trace(ex)
}
-
return false
}
-
}
private lateinit var btnAccount: Button
diff --git a/app/src/main/java/jp/juggler/subwaytooter/App1.kt b/app/src/main/java/jp/juggler/subwaytooter/App1.kt
index dba0eefa..784c225c 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/App1.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/App1.kt
@@ -124,8 +124,9 @@ class App1 : Application() {
// 2020/9/20 56=>57 SavedAccountテーブルに項目追加
// 2020/9/20 57=>58 UserRelationテーブルに項目追加
// 2021/2/10 58=>59 SavedAccountテーブルに項目追加
+ // 2021/5/11 59=>60 SavedAccountテーブルに項目追加
- internal const val DB_VERSION = 59
+ internal const val DB_VERSION = 60
private val tableList = arrayOf(
LogData,
diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/TootApiClient.kt b/app/src/main/java/jp/juggler/subwaytooter/api/TootApiClient.kt
index a9e1abb5..032e00dd 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/api/TootApiClient.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/api/TootApiClient.kt
@@ -42,82 +42,10 @@ class TootApiClient(
private val reStartJsonArray = """\A\s*\[""".asciiPattern()
private val reStartJsonObject = """\A\s*\{""".asciiPattern()
- private val reWhiteSpace = """\s+""".asciiPattern()
-
val DEFAULT_JSON_ERROR_PARSER =
{ json: JsonObject -> json["error"]?.toString() }
- internal fun simplifyErrorHtml(
- response: Response,
- sv: String,
- jsonErrorParser: (json: JsonObject) -> String? = DEFAULT_JSON_ERROR_PARSER
- ): String {
-
- // JsonObjectとして解釈できるならエラーメッセージを検出する
- try {
- val json = sv.decodeJsonObject()
- val error_message =
- jsonErrorParser(json)?.notEmpty()
- if (error_message != null) {
- return error_message
- }
- } catch (_: Throwable) {
- }
-
- // HTMLならタグの除去を試みる
- val ct = response.body?.contentType()
- if (ct?.subtype == "html") {
- val decoded = DecodeOptions().decodeHTML(sv).toString()
- return reWhiteSpace.matcher(decoded).replaceAll(" ").trim()
- }
-
- // XXX: Amazon S3 が403を返した場合にcontent-typeが?/xmlでserverがAmazonならXMLをパースしてエラーを整形することもできるが、多分必要ない
-
- return reWhiteSpace.matcher(sv).replaceAll(" ").trim()
- }
-
- fun formatResponse(
- response: Response,
- caption: String,
- bodyString: String? = null,
- jsonErrorParser: (json: JsonObject) -> String? = DEFAULT_JSON_ERROR_PARSER
- ): String {
- val sb = StringBuilder()
- try {
- // body は既に読み終わっているか、そうでなければこれから読む
- if (bodyString != null) {
- sb.append(simplifyErrorHtml(response, bodyString, jsonErrorParser))
- } else {
- try {
- val string = response.body?.string()
- if (string != null) {
- sb.append(simplifyErrorHtml(response, string, jsonErrorParser))
- }
- } catch (ex: Throwable) {
- log.e(ex, "missing response body.")
- sb.append("(missing response body)")
- }
- }
-
- if (sb.isNotEmpty()) sb.append(' ')
- sb.append("(HTTP ").append(response.code.toString())
-
- val message = response.message
- if (message.isNotEmpty()) sb.append(' ').append(message)
- sb.append(")")
-
- if (caption.isNotEmpty()) {
- sb.append(' ').append(caption)
- }
-
- } catch (ex: Throwable) {
- log.trace(ex)
- }
-
- return sb.toString().replace("\n+".toRegex(), "\n")
- }
-
fun getScopeString(ti: TootInstance?) = when {
// 古いサーバ
ti?.versionGE(TootInstance.VERSION_2_4_0_rc1) == false -> "read+write+follow"
@@ -198,6 +126,25 @@ class TootApiClient(
return encodeScopeArray(a) == encodeScopeArray(b)
}
+ fun formatResponse(
+ response: Response,
+ caption: String = "?",
+ bodyString: String? = null
+ ) = TootApiResult(
+ response = response,
+ caption = caption,
+ bodyString = bodyString
+ ).apply { parseErrorResponse() }.error ?: "(null)"
+
+ fun simplifyErrorHtml(
+ response: Response,
+ caption:String = "?",
+ bodyString:String =response.body?.string() ?: "",
+ jsonErrorParser: (json: JsonObject) -> String? = DEFAULT_JSON_ERROR_PARSER
+ ) = TootApiResult(
+ response = response,
+ caption = caption,
+ ).simplifyErrorHtml( bodyString,jsonErrorParser)
}
// 認証に関する設定を保存する
@@ -345,11 +292,8 @@ class TootApiClient(
}
if (!response.isSuccessful || bodyString?.isEmpty() != false) {
-
- result.error = formatResponse(
- response,
- result.caption,
- if (bodyString?.isNotEmpty() == true) bodyString else NO_INFORMATION,
+ result.parseErrorResponse(
+ bodyString?.notEmpty() ?: NO_INFORMATION,
jsonErrorParser
)
}
@@ -388,11 +332,8 @@ class TootApiClient(
if (isApiCancelled) return null
if (!response.isSuccessful || bodyBytes?.isEmpty() != false) {
-
- result.error = formatResponse(
- response,
- result.caption,
- if (bodyBytes?.isNotEmpty() == true) bodyBytes.decodeUTF8() else NO_INFORMATION,
+ result.parseErrorResponse(
+ bodyBytes?.notEmpty()?.decodeUTF8() ?: NO_INFORMATION,
jsonErrorParser
)
}
@@ -411,16 +352,12 @@ class TootApiClient(
progressPath: String? = null,
jsonErrorParser: (json: JsonObject) -> String? = DEFAULT_JSON_ERROR_PARSER
): TootApiResult? {
-
- val response = result.response!! // nullにならないはず
-
try {
readBodyBytes(result, progressPath, jsonErrorParser)
?: return if (isApiCancelled) null else result
} catch (ex: Throwable) {
log.trace(ex)
- result.error =
- formatResponse(response, result.caption, result.bodyString ?: NO_INFORMATION)
+ result.parseErrorResponse(result.bodyString ?: NO_INFORMATION)
}
return result
}
@@ -430,19 +367,14 @@ class TootApiClient(
progressPath: String? = null,
jsonErrorParser: (json: JsonObject) -> String? = DEFAULT_JSON_ERROR_PARSER
): TootApiResult? {
-
- val response = result.response!! // nullにならないはず
-
try {
val bodyString = readBodyString(result, progressPath, jsonErrorParser)
?: return if (isApiCancelled) null else result
result.data = bodyString
-
} catch (ex: Throwable) {
log.trace(ex)
- result.error =
- formatResponse(response, result.caption, result.bodyString ?: NO_INFORMATION)
+ result.parseErrorResponse(result.bodyString ?: NO_INFORMATION)
}
return result
}
@@ -506,8 +438,7 @@ class TootApiClient(
} catch (ex: Throwable) {
log.trace(ex)
- result.error =
- formatResponse(response, result.caption, result.bodyString ?: NO_INFORMATION)
+ result.parseErrorResponse(result.bodyString ?: NO_INFORMATION)
}
return result
diff --git a/app/src/main/java/jp/juggler/subwaytooter/api/TootApiResult.kt b/app/src/main/java/jp/juggler/subwaytooter/api/TootApiResult.kt
index e2e06d13..ce060ad5 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/api/TootApiResult.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/api/TootApiResult.kt
@@ -1,5 +1,6 @@
package jp.juggler.subwaytooter.api
+import jp.juggler.subwaytooter.util.DecodeOptions
import jp.juggler.util.*
import okhttp3.Response
import okhttp3.WebSocket
@@ -8,13 +9,16 @@ open class TootApiResult(
@Suppress("unused") val dummy : Int = 0,
var error : String? = null,
var response : Response? = null,
+ var caption : String = "?",
var bodyString : String? = null
) {
companion object {
private val log = LogCategory("TootApiResult")
-
+
+ private val reWhiteSpace = """\s+""".asciiPattern()
+
private val reLinkURL = """<([^>]+)>;\s*rel="([^"]+)"""".asciiPattern()
fun makeWithCaption(caption : String?) : TootApiResult {
@@ -52,7 +56,7 @@ open class TootApiResult(
var link_older : String? = null // より古いデータへのリンク
var link_newer : String? = null // より新しいデータへの
- var caption : String = "?"
+
constructor() : this(0)
@@ -97,4 +101,75 @@ open class TootApiResult(
}
}
}
+
+ // アカウント作成APIのdetailsを読むため、エラー応答のjsonオブジェクトを保持する
+ var errorJson : JsonObject? = null
+
+ internal fun simplifyErrorHtml(
+ sv: String,
+ jsonErrorParser: (json: JsonObject) -> String? = TootApiClient.DEFAULT_JSON_ERROR_PARSER
+ ): String {
+ val response = this.response!!
+
+ // JsonObjectとして解釈できるならエラーメッセージを検出する
+ try {
+ val json = sv.decodeJsonObject()
+ this.errorJson = json
+ jsonErrorParser(json)?.notEmpty()?.let{ return it }
+ } catch (_: Throwable) {
+ }
+
+ // HTMLならタグの除去を試みる
+ val ct = response.body?.contentType()
+ if (ct?.subtype == "html") {
+ val decoded = DecodeOptions().decodeHTML(sv).toString()
+ return reWhiteSpace.matcher(decoded).replaceAll(" ").trim()
+ }
+
+ // XXX: Amazon S3 が403を返した場合にcontent-typeが?/xmlでserverがAmazonならXMLをパースしてエラーを整形することもできるが、多分必要ない
+
+ return reWhiteSpace.matcher(sv).replaceAll(" ").trim()
+ }
+
+ fun parseErrorResponse(
+ bodyString: String? = null,
+ jsonErrorParser: (json: JsonObject) -> String? = TootApiClient.DEFAULT_JSON_ERROR_PARSER
+ ){
+ val response = this.response!!
+
+ val sb = StringBuilder()
+ try {
+ // body は既に読み終わっているか、そうでなければこれから読む
+ if (bodyString != null) {
+ sb.append(simplifyErrorHtml( bodyString, jsonErrorParser))
+ } else {
+ try {
+ val string = response.body?.string()
+ if (string != null) {
+ sb.append(simplifyErrorHtml( string, jsonErrorParser))
+ }
+ } catch (ex: Throwable) {
+ log.e(ex, "missing response body.")
+ sb.append("(missing response body)")
+ }
+ }
+
+ if (sb.isNotEmpty()) sb.append(' ')
+ sb.append("(HTTP ").append(response.code.toString())
+
+ val message = response.message
+ if (message.isNotEmpty()) sb.append(' ').append(message)
+ sb.append(")")
+
+ if (caption.isNotEmpty()) {
+ sb.append(' ').append(caption)
+ }
+
+ } catch (ex: Throwable) {
+ log.trace(ex)
+ }
+
+ this.error = sb.toString().replace("\n+".toRegex(), "\n")
+ }
+
}
diff --git a/app/src/main/java/jp/juggler/subwaytooter/notification/PollingWorker.kt b/app/src/main/java/jp/juggler/subwaytooter/notification/PollingWorker.kt
index adae0c79..c93fe8d6 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/notification/PollingWorker.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/notification/PollingWorker.kt
@@ -397,7 +397,7 @@ class PollingWorker private constructor(contextArg: Context) {
private val workerNotifier = Channel(capacity = Channel.CONFLATED)
- fun notifyWorker(){
+ fun notifyWorker() {
GlobalScope.launch { workerNotifier.send(Unit) }
}
@@ -607,7 +607,6 @@ class PollingWorker private constructor(contextArg: Context) {
}
-
// JobService#onStopJob から呼ばれる
// return True to indicate to the JobManager whether you'd like to reschedule this job based on the retry criteria provided at job creation-time.
// return False to drop the job. Regardless of the value returned, your job must stop executing.
@@ -616,7 +615,7 @@ class PollingWorker private constructor(contextArg: Context) {
// 同じジョブ番号がジョブリストにあるか?
synchronized(startedJobList) {
- startedJobList.removeFirst { it.jobId == jobId }?.let{ item->
+ startedJobList.removeFirst { it.jobId == jobId }?.let { item ->
log.w("onStopJob: jobId=${jobId}, set cancel flag.")
// リソースがなくてStopされるのだからrescheduleはtrue
item.cancel(true)
@@ -644,12 +643,11 @@ class PollingWorker private constructor(contextArg: Context) {
}
-
// ポーリングが完了した
- fun onPollingComplete(requiredNextPolling:Boolean) {
- when(requiredNextPolling) {
+ fun onPollingComplete(requiredNextPolling: Boolean) {
+ when (requiredNextPolling) {
// まだスケジュールされてないなら登録する
- true-> if (! scheduler.allPendingJobs.any { it.id == JobId.Polling.int } ) {
+ true -> if (!scheduler.allPendingJobs.any { it.id == JobId.Polling.int }) {
log.d("registering next polling…")
scheduleJob(context, JobId.Polling)
}
@@ -664,7 +662,7 @@ class PollingWorker private constructor(contextArg: Context) {
}
// ジョブ完了後にメインスレッドで呼ばれる
- fun onJobComplete(item:JobItem) {
+ fun onJobComplete(item: JobItem) {
synchronized(startedJobList) {
startedJobList.remove(item)
@@ -683,7 +681,7 @@ class PollingWorker private constructor(contextArg: Context) {
}
// return false if app data import started.
- fun onStartTask(taskId: TaskId):Boolean {
+ fun onStartTask(taskId: TaskId): Boolean {
@Suppress("NON_EXHAUSTIVE_WHEN")
when (taskId) {
TaskId.AppDataImportBefore -> {
diff --git a/app/src/main/java/jp/juggler/subwaytooter/notification/TaskRunner.kt b/app/src/main/java/jp/juggler/subwaytooter/notification/TaskRunner.kt
index b77dcb2b..cae20a0a 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/notification/TaskRunner.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/notification/TaskRunner.kt
@@ -16,10 +16,7 @@ import jp.juggler.subwaytooter.api.TootApiClient
import jp.juggler.subwaytooter.api.TootApiResult
import jp.juggler.subwaytooter.api.TootParser
import jp.juggler.subwaytooter.api.entity.*
-import jp.juggler.subwaytooter.table.AcctColor
-import jp.juggler.subwaytooter.table.NotificationCache
-import jp.juggler.subwaytooter.table.NotificationTracking
-import jp.juggler.subwaytooter.table.SavedAccount
+import jp.juggler.subwaytooter.table.*
import jp.juggler.subwaytooter.util.PushSubscriptionHelper
import jp.juggler.util.*
import kotlinx.coroutines.*
@@ -30,12 +27,12 @@ import kotlin.math.min
class TaskRunner(
- private val pollingWorker: PollingWorker,
+ private val pollingWorker: PollingWorker,
val job: JobItem,
private val taskId: TaskId,
private val taskData: JsonObject
) {
- companion object{
+ companion object {
private val log = LogCategory("TaskRunner")
private var workerStatus = ""
@@ -46,7 +43,7 @@ class TaskRunner(
}
val context = pollingWorker.context
- val notification_manager = pollingWorker.notification_manager
+ val notification_manager = pollingWorker.notification_manager
val pref = pollingWorker.pref
val error_instance = ArrayList()
@@ -70,7 +67,7 @@ class TaskRunner(
pollingWorker.processInjectedData(job.injectedAccounts)
TaskId.ResetTrackingState ->
- NotificationTracking.resetTrackingState( taskData.long(PollingWorker.EXTRA_DB_ID))
+ NotificationTracking.resetTrackingState(taskData.long(PollingWorker.EXTRA_DB_ID))
// プッシュ通知が届いた
TaskId.FcmMessage -> {
@@ -106,7 +103,8 @@ class TaskRunner(
TaskId.NotificationDelete -> {
val db_id = taskData.long(PollingWorker.EXTRA_DB_ID)
- val type = TrackingType.parseStr(taskData.string(PollingWorker.EXTRA_NOTIFICATION_TYPE))
+ val type =
+ TrackingType.parseStr(taskData.string(PollingWorker.EXTRA_NOTIFICATION_TYPE))
val typeName = type.typeName
val id = taskData.string(PollingWorker.EXTRA_NOTIFICATION_ID)
log.d("Notification deleted! db_id=$db_id,type=$type,id=$id")
@@ -118,7 +116,8 @@ class TaskRunner(
TaskId.NotificationClick -> {
val db_id = taskData.long(PollingWorker.EXTRA_DB_ID)
- val type = TrackingType.parseStr(taskData.string(PollingWorker.EXTRA_NOTIFICATION_TYPE))
+ val type =
+ TrackingType.parseStr(taskData.string(PollingWorker.EXTRA_NOTIFICATION_TYPE))
val typeName = type.typeName
val id = taskData.string(PollingWorker.EXTRA_NOTIFICATION_ID).notEmpty()
log.d("Notification clicked! db_id=$db_id,type=$type,id=$id")
@@ -132,7 +131,10 @@ class TaskRunner(
val itemTag = "$notification_tag/$id"
notification_manager.cancel(itemTag, PollingWorker.NOTIFICATION_ID)
} else {
- notification_manager.cancel(notification_tag, PollingWorker.NOTIFICATION_ID)
+ notification_manager.cancel(
+ notification_tag,
+ PollingWorker.NOTIFICATION_ID
+ )
}
// DB更新処理
NotificationTracking.updateRead(db_id, typeName)
@@ -179,7 +181,8 @@ class TaskRunner(
liveSet.add(t.account.apiHost)
}
if (liveSet.isEmpty()) break
- PollingWorker.workerStatus = "waiting ${liveSet.joinToString(", ") { it.pretty }}"
+ PollingWorker.workerStatus =
+ "waiting ${liveSet.joinToString(", ") { it.pretty }}"
delay(if (job.isJobCancelled) 100L else 1000L)
}
@@ -208,6 +211,36 @@ class TaskRunner(
private var currentCall: WeakReference? = null
+ private var policyFilter: (TootNotification) -> Boolean = when (account.push_policy) {
+
+ "followed" -> { it ->
+ val who = it.account
+ when {
+ who == null -> true
+ account.isMe(who) -> true
+
+ else -> UserRelation.load(account.db_id, who.id).following
+ }
+ }
+
+ "follower" -> { it ->
+ val who = it.account
+ when {
+ it.type == TootNotification.TYPE_FOLLOW ||
+ it.type == TootNotification.TYPE_FOLLOW_REQUEST -> true
+
+ who == null -> true
+ account.isMe(who) -> true
+
+ else -> UserRelation.load(account.db_id, who.id).followed_by
+ }
+ }
+
+ "none" -> { _ -> false }
+
+ else -> { _ -> true }
+ }
+
///////////////////
val isActive: Boolean
@@ -353,14 +386,17 @@ class TaskRunner(
private val duplicate_check = HashSet()
private val dstListData = LinkedList()
+
fun checkAccount() {
- this.nr = NotificationTracking.load(account.acct.pretty, account.db_id, trackingName)
+ this.nr =
+ NotificationTracking.load(account.acct.pretty, account.db_id, trackingName)
- fun JsonObject.isMention() = when (NotificationCache.parseNotificationType(account, this)) {
- TootNotification.TYPE_REPLY, TootNotification.TYPE_MENTION -> true
- else -> false
- }
+ fun JsonObject.isMention() =
+ when (NotificationCache.parseNotificationType(account, this)) {
+ TootNotification.TYPE_REPLY, TootNotification.TYPE_MENTION -> true
+ else -> false
+ }
val jsonList = when (trackingType) {
@@ -425,6 +461,9 @@ class TaskRunner(
}
}
+ // Mastodon 3.4.0rc1 push policy
+ if (!policyFilter(notification)) return
+
// 後から処理したものが先頭に来る
dstListData.add(0, NotificationData(account, notification))
}
@@ -692,7 +731,7 @@ class TaskRunner(
// FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY を付与してはいけない
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
},
- PendingIntent.FLAG_UPDATE_CURRENT or (if(Build.VERSION.SDK_INT>=23) PendingIntent.FLAG_IMMUTABLE else 0)
+ PendingIntent.FLAG_UPDATE_CURRENT or (if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_IMMUTABLE else 0)
)
)
@@ -705,7 +744,7 @@ class TaskRunner(
data =
"subwaytooter://notification_delete/?$params".toUri()
},
- PendingIntent.FLAG_UPDATE_CURRENT or (if(Build.VERSION.SDK_INT>=23) PendingIntent.FLAG_IMMUTABLE else 0)
+ PendingIntent.FLAG_UPDATE_CURRENT or (if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_IMMUTABLE else 0)
)
)
@@ -730,7 +769,11 @@ class TaskRunner(
log.d("showNotification[${account.acct.pretty}] set notification...")
- notification_manager.notify(notification_tag, PollingWorker.NOTIFICATION_ID, builder.build())
+ notification_manager.notify(
+ notification_tag,
+ PollingWorker.NOTIFICATION_ID,
+ builder.build()
+ )
}
}
}
@@ -748,7 +791,7 @@ class TaskRunner(
context,
3,
intent_click,
- PendingIntent.FLAG_UPDATE_CURRENT or (if(Build.VERSION.SDK_INT>=23) PendingIntent.FLAG_IMMUTABLE else 0)
+ PendingIntent.FLAG_UPDATE_CURRENT or (if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_IMMUTABLE else 0)
)
val builder = if (Build.VERSION.SDK_INT >= 26) {
@@ -799,7 +842,6 @@ class TaskRunner(
}
-
private fun NotificationData.getNotificationLine(): String {
val name = when (Pref.bpShowAcctInSystemNotification(pref)) {
@@ -861,7 +903,7 @@ class TaskRunner(
}
private fun deleteCacheData(db_id: Long?) {
- if(db_id != null) {
+ if (db_id != null) {
log.d("Notification clear! db_id=$db_id")
SavedAccount.loadAccount(context, db_id) ?: return
NotificationCache.deleteCache(db_id)
diff --git a/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.kt b/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.kt
index fae4dd16..c8350165 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/table/SavedAccount.kt
@@ -79,7 +79,9 @@ class SavedAccount(
var image_resize: String? = null
var image_max_megabytes : String? = null
var movie_max_megabytes : String? = null
-
+
+ var push_policy : String? = null
+
init {
val tmpAcct = Acct.parse(acctArg)
this.username = tmpAcct.username
@@ -165,6 +167,7 @@ class SavedAccount(
image_resize = cursor.getStringOrNull(COL_IMAGE_RESIZE)
image_max_megabytes = cursor.getStringOrNull(COL_IMAGE_MAX_MEGABYTES)
movie_max_megabytes = cursor.getStringOrNull(COL_MOVIE_MAX_MEGABYTES)
+ push_policy = cursor.getStringOrNull(COL_PUSH_POLICY)
}
val isNA : Boolean
@@ -232,6 +235,7 @@ class SavedAccount(
cv.putOrNull(COL_IMAGE_RESIZE,image_resize)
cv.putOrNull(COL_IMAGE_MAX_MEGABYTES, image_max_megabytes)
cv.putOrNull(COL_MOVIE_MAX_MEGABYTES, movie_max_megabytes)
+ cv.putOrNull(COL_PUSH_POLICY,push_policy)
// UIからは更新しない
// notification_tag
@@ -298,6 +302,7 @@ class SavedAccount(
this.image_resize = b.image_resize
this.image_max_megabytes = b.image_max_megabytes
this.movie_max_megabytes = b.movie_max_megabytes
+ this.push_policy = b.push_policy
}
fun getFullAcct(who : TootAccount?) = getFullAcct(who?.acct)
@@ -400,6 +405,8 @@ class SavedAccount(
private const val COL_IMAGE_MAX_MEGABYTES = "image_max_megabytes" // スキーマ59
private const val COL_MOVIE_MAX_MEGABYTES = "movie_max_megabytes" // スキーマ59
+ private const val COL_PUSH_POLICY = "push_policy" // スキーマ60
+
/////////////////////////////////
// login information
const val INVALID_DB_ID = - 1L
@@ -497,6 +504,9 @@ class SavedAccount(
+ ",$COL_IMAGE_MAX_MEGABYTES text default null"
+ ",$COL_MOVIE_MAX_MEGABYTES text default null"
+ // スキーマ60から
+ + ",$COL_PUSH_POLICY text default null"
+
+ ")"
)
db.execSQL("create index if not exists ${table}_user on ${table}(u)")
@@ -504,7 +514,11 @@ class SavedAccount(
}
override fun onDBUpgrade(db : SQLiteDatabase, oldVersion : Int, newVersion : Int) {
- if(oldVersion < 2 && newVersion >= 2) {
+ fun isUpgraded(n:Int,block:()->Unit){
+ if( oldVersion < n && newVersion >= n ) block()
+ }
+
+ isUpgraded(2){
try {
db.execSQL("alter table $table add column notification_mention integer default 1")
} catch(ex : Throwable) {
@@ -530,7 +544,7 @@ class SavedAccount(
}
}
- if(oldVersion < 10 && newVersion >= 10) {
+ isUpgraded( 10) {
try {
db.execSQL("alter table $table add column $COL_CONFIRM_FOLLOW integer default 1")
} catch(ex : Throwable) {
@@ -556,7 +570,7 @@ class SavedAccount(
}
}
- if(oldVersion < 13 && newVersion >= 13) {
+ isUpgraded( 13) {
try {
db.execSQL("alter table $table add column $COL_NOTIFICATION_TAG text default ''")
} catch(ex : Throwable) {
@@ -564,7 +578,7 @@ class SavedAccount(
}
}
- if(oldVersion < 14 && newVersion >= 14) {
+ isUpgraded( 14) {
try {
db.execSQL("alter table $table add column $COL_REGISTER_KEY text default ''")
} catch(ex : Throwable) {
@@ -578,7 +592,7 @@ class SavedAccount(
}
}
- if(oldVersion < 16 && newVersion >= 16) {
+ isUpgraded( 16) {
try {
db.execSQL("alter table $table add column $COL_SOUND_URI text default ''")
} catch(ex : Throwable) {
@@ -586,7 +600,7 @@ class SavedAccount(
}
}
- if(oldVersion < 18 && newVersion >= 18) {
+ isUpgraded( 18) {
try {
db.execSQL("alter table $table add column $COL_DONT_SHOW_TIMEOUT integer default 0")
} catch(ex : Throwable) {
@@ -594,7 +608,7 @@ class SavedAccount(
}
}
- if(oldVersion < 23 && newVersion >= 23) {
+ isUpgraded( 23) {
try {
db.execSQL("alter table $table add column $COL_CONFIRM_FAVOURITE integer default 1")
} catch(ex : Throwable) {
@@ -602,7 +616,7 @@ class SavedAccount(
}
}
- if(oldVersion < 24 && newVersion >= 24) {
+ isUpgraded( 24) {
try {
db.execSQL("alter table $table add column $COL_CONFIRM_UNFAVOURITE integer default 1")
} catch(ex : Throwable) {
@@ -615,7 +629,7 @@ class SavedAccount(
}
}
- if(oldVersion < 27 && newVersion >= 27) {
+ isUpgraded( 27) {
try {
db.execSQL("alter table $table add column $COL_DEFAULT_TEXT text default ''")
} catch(ex : Throwable) {
@@ -623,15 +637,15 @@ class SavedAccount(
}
}
- if(oldVersion < 28 && newVersion >= 28) {
+ isUpgraded( 28) {
try {
db.execSQL("alter table $table add column $COL_MISSKEY_VERSION integer default 0")
} catch(ex : Throwable) {
log.trace(ex)
}
}
-
- if(oldVersion < 33 && newVersion >= 33) {
+
+ isUpgraded( 33) {
try {
db.execSQL("alter table $table add column $COL_NOTIFICATION_REACTION integer default 1")
} catch(ex : Throwable) {
@@ -643,8 +657,8 @@ class SavedAccount(
log.trace(ex)
}
}
-
- if(oldVersion < 38 && newVersion >= 38) {
+
+ isUpgraded( 38) {
try {
db.execSQL("alter table $table add column $COL_DEFAULT_SENSITIVE integer default 0")
} catch(ex : Throwable) {
@@ -657,53 +671,53 @@ class SavedAccount(
}
}
-
- if(oldVersion < 39 && newVersion >= 39) {
+
+ isUpgraded( 39) {
try {
db.execSQL("alter table $table add column $COL_MAX_TOOT_CHARS integer default 0")
} catch(ex : Throwable) {
log.trace(ex)
}
}
-
- if(oldVersion < 42 && newVersion >= 42) {
+
+ isUpgraded( 42) {
try {
db.execSQL("alter table $table add column $COL_LAST_NOTIFICATION_ERROR text")
} catch(ex : Throwable) {
log.trace(ex)
}
}
-
- if(oldVersion < 44 && newVersion >= 44) {
+
+ isUpgraded( 44) {
try {
db.execSQL("alter table $table add column $COL_NOTIFICATION_FOLLOW_REQUEST integer default 1")
} catch(ex : Throwable) {
log.trace(ex)
}
}
-
- if(oldVersion < 45 && newVersion >= 45) {
+
+ isUpgraded( 45) {
try {
db.execSQL("alter table $table add column $COL_LAST_SUBSCRIPTION_ERROR text")
} catch(ex : Throwable) {
log.trace(ex)
}
}
- if(oldVersion < 46 && newVersion >= 46) {
+ isUpgraded(46) {
try {
db.execSQL("alter table $table add column $COL_LAST_PUSH_ENDPOINT text")
} catch(ex : Throwable) {
log.trace(ex)
}
}
- if(oldVersion < 56 && newVersion >= 56) {
+ isUpgraded( 56) {
try {
db.execSQL("alter table $table add column $COL_DOMAIN text")
} catch(ex : Throwable) {
log.trace(ex)
}
}
- if(oldVersion < 57 && newVersion >= 57) {
+ isUpgraded( 57) {
try {
db.execSQL("alter table $table add column $COL_NOTIFICATION_POST integer default 1")
} catch(ex : Throwable) {
@@ -711,7 +725,7 @@ class SavedAccount(
}
}
- if(oldVersion < 59 && newVersion >= 59) {
+ isUpgraded( 59) {
try {
db.execSQL("alter table $table add column $COL_IMAGE_RESIZE text default null")
} catch(ex : Throwable) {
@@ -728,6 +742,13 @@ class SavedAccount(
log.trace(ex)
}
}
+ isUpgraded( 60) {
+ try {
+ db.execSQL("alter table $table add column $COL_PUSH_POLICY text default null")
+ } catch(ex : Throwable) {
+ log.trace(ex)
+ }
+ }
}
val defaultResizeConfig = ResizeConfig(ResizeType.LongSide, 1280)
diff --git a/app/src/main/java/jp/juggler/subwaytooter/util/PushSubscriptionHelper.kt b/app/src/main/java/jp/juggler/subwaytooter/util/PushSubscriptionHelper.kt
index e3baa2ef..7e0edff1 100644
--- a/app/src/main/java/jp/juggler/subwaytooter/util/PushSubscriptionHelper.kt
+++ b/app/src/main/java/jp/juggler/subwaytooter/util/PushSubscriptionHelper.kt
@@ -480,6 +480,7 @@ class PushSubscriptionHelper(
})
put("data", JsonObject().apply {
put("alerts", newAlerts)
+ account.push_policy?.let{ put("policy",it )}
})
}
diff --git a/app/src/main/java/jp/juggler/util/CollectionUtils.kt b/app/src/main/java/jp/juggler/util/CollectionUtils.kt
index b56d0de0..302040e6 100644
--- a/app/src/main/java/jp/juggler/util/CollectionUtils.kt
+++ b/app/src/main/java/jp/juggler/util/CollectionUtils.kt
@@ -8,3 +8,6 @@ fun > E?.notEmpty(): E? =
fun > E?.notEmpty(): E? =
if (this?.isNotEmpty() == true) this else null
+
+fun ByteArray?.notEmpty(): ByteArray? =
+ if (this?.isNotEmpty() == true) this else null
diff --git a/app/src/main/res/layout/act_account_setting.xml b/app/src/main/res/layout/act_account_setting.xml
index 6ffbedb7..04ea2d3c 100644
--- a/app/src/main/res/layout/act_account_setting.xml
+++ b/app/src/main/res/layout/act_account_setting.xml
@@ -458,77 +458,45 @@
style="@style/setting_row_label"
android:text="@string/confirmation" />
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -536,74 +504,63 @@
style="@style/setting_row_label"
android:text="@string/notifications" />
-
+
-
-
+
-
+
-
-
+
-
+
-
-
+
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 77c5f4e8..d6bbc569 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -865,7 +865,7 @@
ヘッダ部アイコンの大きさ(単位:dp。デフォルト:24。アプリ再起動が必要)
ヘッダ部テキストの大きさ(単位:sp。デフォルト:14。アプリ再起動が必要)
カラムストリップのアイコンの大きさ(単位:dp。デフォルト:30。アプリ再起動が必要)
- All
+ 全て
本文中のリンクをコンテキストメニューに表示する
予約投稿はマストドン2.7.0以降で使えます
通知カラムのクイックフィルタをカラム設定内部に表示する(アプリ再起動が必要)
@@ -1077,5 +1077,7 @@
登録メールアドレスを変更する
確認メールの再送を要求しました。
確認メールの再送を要求した後の手順:\n- あなたのメーラーで新着メールが届くのを確認する。\n- メール中の確認リンクを開く。\n- このダイアログを閉じてカラムをリロードする。
+ プッシュ通知フィルタ(Mastodon 3.4.0以降。プッシュ通知の更新が必要)
+ 誰もいない
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d619e40b..c9a0ec57 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1091,4 +1091,6 @@
Update e-mail address
Resending confirm E-mail was requested.
After requesting resending confirm E-mail,\n- please check the mail on your mailer.\n- open confirm link in the mail.\n- close this dialog and reload column.
+ Push notification filter (Mastodon 3.4.0+, requires update push subscription)
+ No one