add push policy in account settings

This commit is contained in:
tateisu 2021-05-11 15:12:43 +09:00
parent 970264bd3a
commit 3589e2a054
15 changed files with 372 additions and 304 deletions

View File

@ -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("""<html><body>Error!</body></html>""".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<String>(null)
result = client.authentication2Mastodon(clientName, "DUMMY_CODE",refToken)
jsonObject = result?.jsonObject
assertNotNull(jsonObject)
if (jsonObject == null) return@runBlocking

View File

@ -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<ResizeItems>
private class ResizeItem(val config: ResizeConfig, val caption: String)
private lateinit var imageResizeItems: List<ResizeItem>
private class PushPolicyItem(val id: String?, val caption: String)
private lateinit var pushPolicyItems: List<PushPolicyItem>
///////////////////////////////////////////////////
@ -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()
}

View File

@ -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)
}
}

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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")
}
}

View File

@ -397,7 +397,7 @@ class PollingWorker private constructor(contextArg: Context) {
private val workerNotifier = Channel<Unit>(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 -> {

View File

@ -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<String>()
@ -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<Call>? = 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<EntityId>()
private val dstListData = LinkedList<NotificationData>()
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)

View File

@ -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)

View File

@ -480,6 +480,7 @@ class PushSubscriptionHelper(
})
put("data", JsonObject().apply {
put("alerts", newAlerts)
account.push_policy?.let{ put("policy",it )}
})
}

View File

@ -8,3 +8,6 @@ fun <E : List<*>> E?.notEmpty(): E? =
fun <E : Map<*, *>> E?.notEmpty(): E? =
if (this?.isNotEmpty() == true) this else null
fun ByteArray?.notEmpty(): ByteArray? =
if (this?.isNotEmpty() == true) this else null

View File

@ -458,77 +458,45 @@
style="@style/setting_row_label"
android:text="@string/confirmation" />
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbConfirmFollow"
style="@style/setting_row_form"
android:text="@string/follow" />
<CheckBox
android:id="@+id/cbConfirmFollow"
style="@style/setting_horizontal_stretch"
android:text="@string/follow" />
<CheckBox
android:id="@+id/cbConfirmFollowLockedUser"
style="@style/setting_row_form"
android:text="@string/follow_locked_user" />
</LinearLayout>
<CheckBox
android:id="@+id/cbConfirmUnfollow"
style="@style/setting_row_form"
android:text="@string/unfollow" />
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbConfirmBoost"
style="@style/setting_row_form"
android:text="@string/boost" />
<CheckBox
android:id="@+id/cbConfirmFollowLockedUser"
style="@style/setting_horizontal_stretch"
android:text="@string/follow_locked_user" />
<CheckBox
android:id="@+id/cbConfirmUnboost"
style="@style/setting_row_form"
android:text="@string/unboost" />
</LinearLayout>
<CheckBox
android:id="@+id/cbConfirmFavourite"
style="@style/setting_row_form"
android:text="@string/favourite" />
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbConfirmUnfavourite"
style="@style/setting_row_form"
android:text="@string/unfavourite" />
<CheckBox
android:id="@+id/cbConfirmUnfollow"
style="@style/setting_horizontal_stretch"
android:text="@string/unfollow" />
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbConfirmBoost"
style="@style/setting_wrap"
android:text="@string/boost" />
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbConfirmUnboost"
style="@style/setting_wrap"
android:text="@string/unboost" />
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbConfirmFavourite"
style="@style/setting_wrap"
android:text="@string/favourite" />
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbConfirmUnfavourite"
style="@style/setting_wrap"
android:text="@string/unfavourite" />
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbConfirmToot"
style="@style/setting_wrap"
android:text="@string/act_post" />
</LinearLayout>
<CheckBox
android:id="@+id/cbConfirmToot"
style="@style/setting_row_form"
android:text="@string/act_post" />
<View style="@style/setting_divider" />
@ -536,74 +504,63 @@
style="@style/setting_row_label"
android:text="@string/notifications" />
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbNotificationMention"
style="@style/setting_row_form"
android:text="@string/mention2" />
<CheckBox
android:id="@+id/cbNotificationMention"
style="@style/setting_horizontal_stretch"
android:text="@string/mention2" />
</LinearLayout>
<CheckBox
android:id="@+id/cbNotificationBoost"
style="@style/setting_row_form"
android:text="@string/boost" />
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbNotificationFavourite"
style="@style/setting_row_form"
android:text="@string/favourite" />
<CheckBox
android:id="@+id/cbNotificationBoost"
style="@style/setting_horizontal_stretch"
android:text="@string/boost" />
</LinearLayout>
<CheckBox
android:id="@+id/cbNotificationFollow"
style="@style/setting_row_form"
android:text="@string/follow" />
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbNotificationFollowRequest"
style="@style/setting_row_form"
android:text="@string/follow_request" />
<CheckBox
android:id="@+id/cbNotificationFavourite"
style="@style/setting_horizontal_stretch"
android:text="@string/favourite" />
</LinearLayout>
<CheckBox
android:id="@+id/cbNotificationReaction"
style="@style/setting_row_form"
android:text="@string/reaction" />
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbNotificationVote"
style="@style/setting_row_form"
android:text="@string/vote_polls" />
<CheckBox
android:id="@+id/cbNotificationFollow"
style="@style/setting_horizontal_stretch"
android:text="@string/follow" />
</LinearLayout>
<CheckBox
android:id="@+id/cbNotificationPost"
style="@style/setting_row_form"
android:text="@string/notification_type_post" />
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbNotificationFollowRequest"
style="@style/setting_horizontal_stretch"
android:text="@string/follow_request" />
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbNotificationReaction"
style="@style/setting_horizontal_stretch"
android:text="@string/reaction" />
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbNotificationVote"
style="@style/setting_horizontal_stretch"
android:text="@string/vote_polls" />
</LinearLayout>
<LinearLayout style="@style/setting_row_form">
<CheckBox
android:id="@+id/cbNotificationPost"
style="@style/setting_horizontal_stretch"
android:text="@string/notification_type_post" />
</LinearLayout>
<TextView
style="@style/setting_row_form"
android:layout_marginTop="12dp"
android:text="@string/push_notification_filter" />
<Spinner
android:id="@+id/spPushPolicy"
style="@style/setting_row_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="40dp"
/>
<Button
android:id="@+id/btnPushSubscription"
style="@style/setting_row_button"
android:layout_marginTop="12dp"
android:ellipsize="start"
android:text="@string/update_push_subscription"
android:textAllCaps="false" />

View File

@ -865,7 +865,7 @@
<string name="header_icon_size">ヘッダ部アイコンの大きさ(単位:dp。デフォルト:24。アプリ再起動が必要)</string>
<string name="header_text_size">ヘッダ部テキストの大きさ(単位:sp。デフォルト:14。アプリ再起動が必要)</string>
<string name="strip_icon_size">カラムストリップのアイコンの大きさ(単位:dp。デフォルト:30。アプリ再起動が必要)</string>
<string name="all">All</string>
<string name="all">全て</string>
<string name="show_links_in_context_menu">本文中のリンクをコンテキストメニューに表示する</string>
<string name="scheduled_status_requires_mastodon_2_7_0">予約投稿はマストドン2.7.0以降で使えます</string>
<string name="move_notifications_quick_filter_to_column_setting">通知カラムのクイックフィルタをカラム設定内部に表示する(アプリ再起動が必要)</string>
@ -1077,5 +1077,7 @@
<string name="update_mail_address">登録メールアドレスを変更する</string>
<string name="resend_confirm_mail_requested">確認メールの再送を要求しました。</string>
<string name="confirm_mail_description">確認メールの再送を要求した後の手順:\n- あなたのメーラーで新着メールが届くのを確認する。\n- メール中の確認リンクを開く。\n- このダイアログを閉じてカラムをリロードする。</string>
<string name="push_notification_filter">プッシュ通知フィルタ(Mastodon 3.4.0以降。プッシュ通知の更新が必要)</string>
<string name="no_one">誰もいない</string>
</resources>

View File

@ -1091,4 +1091,6 @@
<string name="update_mail_address">Update e-mail address</string>
<string name="resend_confirm_mail_requested">Resending confirm E-mail was requested.</string>
<string name="confirm_mail_description">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.</string>
<string name="push_notification_filter">Push notification filter (Mastodon 3.4.0+, requires update push subscription)</string>
<string name="no_one">No one</string>
</resources>