From 3589e2a054bf55e3b1a6768c9ba4cc5f32253eef Mon Sep 17 00:00:00 2001 From: tateisu Date: Tue, 11 May 2021 15:12:43 +0900 Subject: [PATCH] add push policy in account settings --- .../subwaytooter/api/TestTootApiClient.kt | 21 +- .../juggler/subwaytooter/ActAccountSetting.kt | 53 ++++- .../jp/juggler/subwaytooter/ActMediaViewer.kt | 6 +- .../java/jp/juggler/subwaytooter/ActPost.kt | 9 +- .../main/java/jp/juggler/subwaytooter/App1.kt | 3 +- .../juggler/subwaytooter/api/TootApiClient.kt | 121 +++-------- .../juggler/subwaytooter/api/TootApiResult.kt | 79 ++++++- .../notification/PollingWorker.kt | 16 +- .../subwaytooter/notification/TaskRunner.kt | 88 +++++--- .../subwaytooter/table/SavedAccount.kt | 75 ++++--- .../util/PushSubscriptionHelper.kt | 1 + .../java/jp/juggler/util/CollectionUtils.kt | 3 + .../main/res/layout/act_account_setting.xml | 195 +++++++----------- app/src/main/res/values-ja/strings.xml | 4 +- app/src/main/res/values/strings.xml | 2 + 15 files changed, 372 insertions(+), 304 deletions(-) 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" /> - + - - + - + - - + - + - - + - + - - + - - - - - - - - - - - - - - - - - - - + +