アプリ設定に「Pull通知の確認間隔」を追加

This commit is contained in:
tateisu 2018-04-10 16:08:30 +09:00
parent 87d5d78de5
commit 33b2a5549c
10 changed files with 127 additions and 106 deletions

View File

@ -17,6 +17,7 @@
<inspection_tool class="NumericOverflow" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="NumericOverflow" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PrivatePropertyName" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> <inspection_tool class="PrivatePropertyName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="PropertyName" enabled="false" level="WEAK WARNING" enabled_by_default="false" /> <inspection_tool class="PropertyName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="RemoveCurlyBracesFromTemplate" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TryFinallyCanBeTryWithResources" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="TryFinallyCanBeTryWithResources" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnnecessaryModuleDependencyInspection" enabled="false" level="WARNING" enabled_by_default="false" /> <inspection_tool class="UnnecessaryModuleDependencyInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UseWithIndex" enabled="false" level="INFO" enabled_by_default="false" /> <inspection_tool class="UseWithIndex" enabled="false" level="INFO" enabled_by_default="false" />

View File

@ -12,8 +12,8 @@ android {
minSdkVersion 21 minSdkVersion 21
targetSdkVersion 27 targetSdkVersion 27
versionCode 234 versionCode 235
versionName "2.3.4" versionName "2.3.5"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

View File

@ -131,6 +131,7 @@ class ActAppSetting : AppCompatActivity()
private lateinit var tvTimelineFontSize : TextView private lateinit var tvTimelineFontSize : TextView
private lateinit var tvAcctFontSize : TextView private lateinit var tvAcctFontSize : TextView
private lateinit var etAvatarIconSize : EditText private lateinit var etAvatarIconSize : EditText
private lateinit var etPullNotificationCheckInterval: EditText
private var load_busy : Boolean = false private var load_busy : Boolean = false
@ -139,6 +140,13 @@ class ActAppSetting : AppCompatActivity()
// DefaultAccount の Spinnerの値を復元するため、このタイミングでも保存することになった // DefaultAccount の Spinnerの値を復元するため、このタイミングでも保存することになった
saveUIToData() saveUIToData()
// Pull通知チェック間隔を変更したかもしれないのでジョブを再設定する
try {
PollingWorker.scheduleJob(this,PollingWorker.JOB_POLLING)
} catch(ex : Throwable) {
log.trace(ex)
}
} }
override fun onCreate(savedInstanceState : Bundle?) { override fun onCreate(savedInstanceState : Bundle?) {
@ -276,6 +284,7 @@ class ActAppSetting : AppCompatActivity()
etAcctFontSize.addTextChangedListener(SizeCheckTextWatcher(tvAcctFontSize, etAcctFontSize, default_acct_font_size)) etAcctFontSize.addTextChangedListener(SizeCheckTextWatcher(tvAcctFontSize, etAcctFontSize, default_acct_font_size))
etAvatarIconSize = findViewById(R.id.etAvatarIconSize) etAvatarIconSize = findViewById(R.id.etAvatarIconSize)
etPullNotificationCheckInterval = findViewById(R.id.etPullNotificationCheckInterval)
tvTimelineFontUrl = findViewById(R.id.tvTimelineFontUrl) tvTimelineFontUrl = findViewById(R.id.tvTimelineFontUrl)
tvTimelineFontBoldUrl = findViewById(R.id.tvTimelineFontBoldUrl) tvTimelineFontBoldUrl = findViewById(R.id.tvTimelineFontBoldUrl)
@ -310,6 +319,7 @@ class ActAppSetting : AppCompatActivity()
etQuoteNameFormat.setText(Pref.spQuoteNameFormat(pref)) etQuoteNameFormat.setText(Pref.spQuoteNameFormat(pref))
etAutoCWLines.setText(Pref.spAutoCWLines(pref)) etAutoCWLines.setText(Pref.spAutoCWLines(pref))
etAvatarIconSize.setText(Pref.spAvatarIconSize(pref)) etAvatarIconSize.setText(Pref.spAvatarIconSize(pref))
etPullNotificationCheckInterval.setText(Pref.spPullNotificationCheckInterval(pref))
etMediaSizeMax.setText(Pref.spMediaSizeMax(pref)) etMediaSizeMax.setText(Pref.spMediaSizeMax(pref))
etRoundRatio.setText(Pref.spRoundRatio(pref)) etRoundRatio.setText(Pref.spRoundRatio(pref))
@ -355,6 +365,7 @@ class ActAppSetting : AppCompatActivity()
.put(Pref.spQuoteNameFormat, etQuoteNameFormat.text.toString()) // not trimmed .put(Pref.spQuoteNameFormat, etQuoteNameFormat.text.toString()) // not trimmed
.put(Pref.spAutoCWLines, etAutoCWLines.text.toString().trim { it <= ' ' }) .put(Pref.spAutoCWLines, etAutoCWLines.text.toString().trim { it <= ' ' })
.put(Pref.spAvatarIconSize, etAvatarIconSize.text.toString().trim { it <= ' ' }) .put(Pref.spAvatarIconSize, etAvatarIconSize.text.toString().trim { it <= ' ' })
.put(Pref.spPullNotificationCheckInterval, etPullNotificationCheckInterval.text.toString().trim { it <= ' ' })
.put(Pref.spMediaSizeMax, etMediaSizeMax.text.toString().trim { it <= ' ' }) .put(Pref.spMediaSizeMax, etMediaSizeMax.text.toString().trim { it <= ' ' })
.put(Pref.spRoundRatio, etRoundRatio.text.toString().trim { it <= ' ' }) .put(Pref.spRoundRatio, etRoundRatio.text.toString().trim { it <= ' ' })

View File

@ -1442,7 +1442,7 @@ class ActPost : AppCompatActivity(), View.OnClickListener, PostAttachment.Callba
val opener = createOpener(uri, mime_type) val opener = createOpener(uri, mime_type)
val media_size_max = val media_size_max =
1000000 * Math.max(1, Pref.spMediaSizeMax.optInt(pref) ?: 8) 1000000 * Math.max(1, Pref.spMediaSizeMax.toInt(pref) )
val content_length = getStreamSize(true, opener.open()) val content_length = getStreamSize(true, opener.open())
if(content_length > media_size_max) { if(content_length > media_size_max) {

View File

@ -119,7 +119,8 @@ class PollingWorker private constructor(c : Context) {
fun scheduleJob(context : Context, job_id : Int) { fun scheduleJob(context : Context, job_id : Int) {
val scheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE) as? JobScheduler val scheduler = context.getSystemService(Context.JOB_SCHEDULER_SERVICE)
as? JobScheduler
?: throw NotImplementedError("missing JobScheduler system service") ?: throw NotImplementedError("missing JobScheduler system service")
val component = ComponentName(context, PollingService::class.java) val component = ComponentName(context, PollingService::class.java)
@ -128,19 +129,33 @@ class PollingWorker private constructor(c : Context) {
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
if(job_id == JOB_POLLING) { if(job_id == JOB_POLLING) {
if(Build.VERSION.SDK_INT >= 24) {
builder.setPeriodic(60000L * 5, 60000L * 10) val intervalMin = if(Build.VERSION.SDK_INT >= 24) {
JobInfo.getMinPeriodMillis()
} else { } else {
builder.setPeriodic(60000L * 5) 300000L
} }
val intervalMillis = Math.max(
intervalMin,
60000L * Pref.spPullNotificationCheckInterval.toInt(Pref.pref(context))
)
if(Build.VERSION.SDK_INT >= 24) {
val flexMin = JobInfo.getMinFlexMillis()
builder.setPeriodic(intervalMillis, flexMin)
} else {
builder.setPeriodic(intervalMillis)
}
builder.setPersisted(true) builder.setPersisted(true)
} else { } else {
builder builder
.setMinimumLatency(0) .setMinimumLatency(0)
.setOverrideDeadline(60000L) .setOverrideDeadline(60000L)
} }
val jobInfo = builder.build()
scheduler.schedule(builder.build()) scheduler.schedule(jobInfo)
} }
// タスクの追加 // タスクの追加
@ -239,7 +254,7 @@ class PollingWorker private constructor(c : Context) {
internal val job_status = AtomicReference<String>(null) internal val job_status = AtomicReference<String>(null)
fun handleFCMMessage(context : Context, tag : String?, callback : JobStatusCallback) { fun handleFCMMessage(context : Context, tag : String?, callback : JobStatusCallback) {
log.d("handleFCMMessage: start. tag=%s", tag) log.d("handleFCMMessage: start. tag=$tag")
val time_start = SystemClock.elapsedRealtime() val time_start = SystemClock.elapsedRealtime()
callback.onStatus("=>") callback.onStatus("=>")
@ -497,14 +512,14 @@ class PollingWorker private constructor(c : Context) {
while(it.hasNext()) { while(it.hasNext()) {
val itemOld = it.next() val itemOld = it.next()
if(itemOld.jobId == jobId) { if(itemOld.jobId == jobId) {
log.w("addJob: jobId=%s, old job cancelled.", jobId) log.w("addJob: jobId=$jobId, old job cancelled.")
// 同じジョブをすぐに始めるのだからrescheduleはfalse // 同じジョブをすぐに始めるのだからrescheduleはfalse
itemOld.cancel(false) itemOld.cancel(false)
it.remove() it.remove()
} }
} }
} }
log.d("addJob: jobId=%s, add to list.", jobId) log.d("addJob: jobId=$jobId, add to list.")
job_list.add(item) job_list.add(item)
} }
@ -521,7 +536,7 @@ class PollingWorker private constructor(c : Context) {
while(it.hasNext()) { while(it.hasNext()) {
val item = it.next() val item = it.next()
if(item.jobId == jobId) { if(item.jobId == jobId) {
log.w("onStopJob: jobId=%s, set cancel flag.") log.w("onStopJob: jobId=${jobId}, set cancel flag.")
// リソースがなくてStopされるのだからrescheduleはtrue // リソースがなくてStopされるのだからrescheduleはtrue
item.cancel(true) item.cancel(true)
it.remove() it.remove()
@ -531,7 +546,7 @@ class PollingWorker private constructor(c : Context) {
} }
// 該当するジョブを依頼されていない // 該当するジョブを依頼されていない
log.w("onStopJob: jobId=%s, not started..") log.w("onStopJob: jobId=${jobId}, not started..")
return false return false
// 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 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. // return False to drop the job. Regardless of the value returned, your job must stop executing.
@ -598,7 +613,7 @@ class PollingWorker private constructor(c : Context) {
job_status.set("job start.") job_status.set("job start.")
try { try {
log.d("(JobItem.run jobId=%s", jobId) log.d("(JobItem.run jobId=${jobId}")
if(isJobCancelled) throw JobCancelledException() if(isJobCancelled) throw JobCancelledException()
job_status.set("check network status..") job_status.set("check network status..")
@ -633,28 +648,22 @@ class PollingWorker private constructor(c : Context) {
} }
job_status.set("make next schedule.") job_status.set("make next schedule.")
log.d("pollingComplete=${bPollingComplete},isJobCancelled=${isJobCancelled},bPollingRequired=${bPollingRequired.get()}")
if(! isJobCancelled && bPollingComplete) { if(! isJobCancelled && bPollingComplete) {
// ポーリングが完了したのならポーリングが必要かどうかに合わせてジョブのスケジュールを変更する // ポーリングが完了した
if(! bPollingRequired.get()) { if(! bPollingRequired.get()) {
// Pull通知を必要とするアカウントが存在しないなら、スケジュール登録を解除する
log.d("polling job is no longer required.") log.d("polling job is no longer required.")
try { try {
scheduler.cancel(JOB_POLLING) scheduler.cancel(JOB_POLLING)
} catch(ex : Throwable) { } catch(ex : Throwable) {
log.trace(ex) log.trace(ex)
} }
} else if(! scheduler.allPendingJobs.any { it.id == JOB_POLLING }) {
} else { // まだスケジュールされてないなら登録する
var bRegistered = false log.d("registering polling job…")
for(info in scheduler.allPendingJobs) { scheduleJob(context, JOB_POLLING)
if(info.id == JOB_POLLING) {
bRegistered = true
break
}
}
if(! bRegistered) {
scheduleJob(context, JOB_POLLING)
log.d("polling job is registered!")
}
} }
} }
} catch(ex : JobCancelledException) { } catch(ex : JobCancelledException) {
@ -677,8 +686,9 @@ class PollingWorker private constructor(c : Context) {
try { try {
val jobService = refJobService?.get() val jobService = refJobService?.get()
if(jobService != null) { if(jobService != null) {
log.d("sending jobFinished. reschedule=%s", mReschedule.get()) val willReschedule = mReschedule.get()
jobService.jobFinished(jobParams, mReschedule.get()) log.d("sending jobFinished. willReschedule=$willReschedule")
jobService.jobFinished(jobParams, willReschedule)
} }
} catch(ex : Throwable) { } catch(ex : Throwable) {
log.trace(ex) log.trace(ex)
@ -686,7 +696,7 @@ class PollingWorker private constructor(c : Context) {
} }
}) })
} }
log.d(")JobItem.run jobId=%s, cancel=%s", jobId, isJobCancelled) log.d(")JobItem.run jobId=${jobId}, cancel=${isJobCancelled}")
} }
private fun checkNetwork() : Boolean { private fun checkNetwork() : Boolean {
@ -697,7 +707,7 @@ class PollingWorker private constructor(c : Context) {
} else { } else {
val state = ni.state val state = ni.state
val detail = ni.detailedState val detail = ni.detailedState
log.d("checkNetwork: state=%s,detail=%s", state, detail) log.d("checkNetwork: state=${state},detail=${detail}")
if(state != NetworkInfo.State.CONNECTED) { if(state != NetworkInfo.State.CONNECTED) {
log.d("checkNetwork: not connected.") log.d("checkNetwork: not connected.")
false false
@ -721,8 +731,8 @@ class PollingWorker private constructor(c : Context) {
fun runTask(job : JobItem, taskId : Int, taskData : JSONObject) { fun runTask(job : JobItem, taskId : Int, taskData : JSONObject) {
try { try {
log.e("(runTask: taskId=%s", taskId) log.e("(runTask: taskId=${taskId}")
job_status.set("start task " + taskId) job_status.set("start task $taskId")
this.job = job this.job = job
this.taskId = taskId this.taskId = taskId
@ -788,7 +798,7 @@ class PollingWorker private constructor(c : Context) {
TASK_NOTIFICATION_CLEAR -> { TASK_NOTIFICATION_CLEAR -> {
val db_id = taskData.parseLong(EXTRA_DB_ID) val db_id = taskData.parseLong(EXTRA_DB_ID)
log.d("Notification clear! db_id=%s", db_id) log.d("Notification clear! db_id=$db_id")
if(db_id != null) { if(db_id != null) {
deleteCacheData(db_id) deleteCacheData(db_id)
} }
@ -796,7 +806,7 @@ class PollingWorker private constructor(c : Context) {
TASK_NOTIFICATION_DELETE -> { TASK_NOTIFICATION_DELETE -> {
val db_id = taskData.parseLong(EXTRA_DB_ID) val db_id = taskData.parseLong(EXTRA_DB_ID)
log.d("Notification deleted! db_id=%s", db_id) log.d("Notification deleted! db_id=$db_id")
if(db_id != null) { if(db_id != null) {
NotificationTracking.updateRead(db_id) NotificationTracking.updateRead(db_id)
} }
@ -805,7 +815,7 @@ class PollingWorker private constructor(c : Context) {
TASK_NOTIFICATION_CLICK -> { TASK_NOTIFICATION_CLICK -> {
val db_id = taskData.parseLong(EXTRA_DB_ID) val db_id = taskData.parseLong(EXTRA_DB_ID)
log.d("Notification clicked! db_id=%s", db_id) log.d("Notification clicked! db_id=$db_id")
if(db_id != null) { if(db_id != null) {
// 通知をキャンセル // 通知をキャンセル
notification_manager.cancel(db_id.toString(), NOTIFICATION_ID) notification_manager.cancel(db_id.toString(), NOTIFICATION_ID)
@ -840,30 +850,17 @@ class PollingWorker private constructor(c : Context) {
t.start() t.start()
} }
while(true) { while(true) {
// 同じホスト名が重複しないようにSetに集める
val liveSet = TreeSet<String>() val liveSet = TreeSet<String>()
val it = thread_list.iterator() for(t in thread_list) {
while(it.hasNext()) { if(! t.isAlive) continue
val t = it.next() if(job.isJobCancelled) t.cancel()
if(! t.isAlive) {
it.remove()
continue
}
liveSet.add(t.account.host) liveSet.add(t.account.host)
if(job.isJobCancelled) {
t.cancel()
}
} }
val remain = thread_list.size if(liveSet.isEmpty()) break
if(remain <= 0) break
// job_status.set("waiting " + liveSet.joinToString(", "))
val sb = StringBuilder() job.waitWorkerThread(if(job.isJobCancelled) 100L else 1000L)
for(s in liveSet) {
if(sb.isNotEmpty()) sb.append(", ")
sb.append(s)
}
job_status.set("waiting " + sb.toString())
//
job.waitWorkerThread(if(job.isJobCancelled) 50L else 1000L)
} }
synchronized(error_instance) { synchronized(error_instance) {
@ -876,8 +873,8 @@ class PollingWorker private constructor(c : Context) {
log.trace(ex) log.trace(ex)
log.e(ex, "task execution failed.") log.e(ex, "task execution failed.")
} finally { } finally {
log.e(")runTask: taskId=%s", taskId) log.e(")runTask: taskId=$taskId")
job_status.set("end task " + taskId) job_status.set("end task $taskId")
} }
} }
@ -911,7 +908,7 @@ class PollingWorker private constructor(c : Context) {
} }
val request = Request.Builder() val request = Request.Builder()
.url(APP_SERVER + "/counter") .url("$APP_SERVER/counter")
.build() .build()
val call = App1.ok_http_client.newCall(request) val call = App1.ok_http_client.newCall(request)
@ -1121,7 +1118,7 @@ class PollingWorker private constructor(c : Context) {
+ "&tag=" + tag) + "&tag=" + tag)
val request = Request.Builder() val request = Request.Builder()
.url(APP_SERVER + "/unregister") .url("$APP_SERVER/unregister")
.post( .post(
RequestBody.create( RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED,
@ -1152,7 +1149,7 @@ class PollingWorker private constructor(c : Context) {
private fun registerDeviceToken() { private fun registerDeviceToken() {
try { try {
// 設定によってはデバイストークンやアクセストークンを送信しない // 設定によってはデバイストークンやアクセストークンを送信しない
if( ! Pref.bpSendAccessTokenToAppServer(Pref.pref(context))){ if(! Pref.bpSendAccessTokenToAppServer(Pref.pref(context))) {
log.d("registerDeviceToken: SendAccessTokenToAppServer is not set.") log.d("registerDeviceToken: SendAccessTokenToAppServer is not set.")
return return
} }
@ -1221,7 +1218,7 @@ class PollingWorker private constructor(c : Context) {
} }
val request = Request.Builder() val request = Request.Builder()
.url(APP_SERVER + "/register") .url("$APP_SERVER/register")
.post( .post(
RequestBody.create( RequestBody.create(
TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED, TootApiClient.MEDIA_TYPE_FORM_URL_ENCODED,
@ -1289,9 +1286,9 @@ class PollingWorker private constructor(c : Context) {
for(nTry in 0 .. 3) { for(nTry in 0 .. 3) {
if(job.isJobCancelled) return if(job.isJobCancelled) return
var path = PATH_NOTIFICATIONS val path = when {
if(nid_last_show != - 1L) { nid_last_show != - 1L -> "$PATH_NOTIFICATIONS?since_id=$nid_last_show"
path = path + "?since_id=" + nid_last_show else -> PATH_NOTIFICATIONS
} }
val result = client.request(path) val result = client.request(path)
@ -1312,7 +1309,7 @@ class PollingWorker private constructor(c : Context) {
break break
} else { } else {
log.d("error. %s", result.error) log.d("error. ${result.error}")
val sv = result.error val sv = result.error
if(sv?.contains("Timeout") == true && ! account.dont_show_timeout) { if(sv?.contains("Timeout") == true && ! account.dont_show_timeout) {
@ -1357,12 +1354,7 @@ class PollingWorker private constructor(c : Context) {
private fun update_sub(src : JSONObject) { private fun update_sub(src : JSONObject) {
if(nr.nid_read == 0L || nr.nid_show == 0L) { if(nr.nid_read == 0L || nr.nid_show == 0L) {
log.d( log.d("update_sub account_db_id=${account.db_id}, nid_read=${nr.nid_read}, nid_show=${nr.nid_show}")
"update_sub account_db_id=%s, nid_read=%s, nid_show=%s",
account.db_id,
nr.nid_read,
nr.nid_show
)
} }
val id = src.parseLong("id") val id = src.parseLong("id")
@ -1375,22 +1367,18 @@ class PollingWorker private constructor(c : Context) {
} }
if(id <= nr.nid_read) { if(id <= nr.nid_read) {
// warning.d("update_sub: ignore data that id=%s, <= read id %s ",id,nr.nid_read); // warning.d("update_sub: ignore data that id=${id}, <= read id ${nr.nid_read} ");
return return
} }
log.d("update_sub: found data that id=%s, > read id %s ", id, nr.nid_read) log.d("update_sub: found data that id=${id}, > read id ${nr.nid_read}")
if(id > nr.nid_show) { if(id > nr.nid_show) {
log.d( log.d("update_sub: found new data that id=${id}, greater than shown id ${nr.nid_show}")
"update_sub: found new data that id=%s, greater than shown id %s ",
id,
nr.nid_show
)
// 種別チェックより先に「表示済み」idの更新を行う // 種別チェックより先に「表示済み」idの更新を行う
nr.nid_show = id nr.nid_show = id
} }
val type = src.parseString("type") val type = src.parseString("type")
if(! account.notification_mention && TootNotification.TYPE_MENTION == type if(! account.notification_mention && TootNotification.TYPE_MENTION == type
|| ! account.notification_boost && TootNotification.TYPE_REBLOG == type || ! account.notification_boost && TootNotification.TYPE_REBLOG == type
@ -1408,11 +1396,11 @@ class PollingWorker private constructor(c : Context) {
} }
// ふぁぼ魔ミュート // ふぁぼ魔ミュート
when(type){ when(type) {
TootNotification.TYPE_REBLOG,TootNotification.TYPE_FAVOURITE,TootNotification.TYPE_FOLLOW ->{ TootNotification.TYPE_REBLOG, TootNotification.TYPE_FAVOURITE, TootNotification.TYPE_FOLLOW -> {
val who = notification.account val who = notification.account
if( who != null && favMuteSet.contains( account.getFullAcct(who) ) ){ if(who != null && favMuteSet.contains(account.getFullAcct(who))) {
log.d("%s is in favMuteSet.",account.getFullAcct(who)) log.d("${account.getFullAcct(who)} is in favMuteSet.")
return return
} }
} }
@ -1448,7 +1436,7 @@ class PollingWorker private constructor(c : Context) {
val notification_tag = account.db_id.toString() val notification_tag = account.db_id.toString()
if(data_list.isEmpty()) { if(data_list.isEmpty()) {
log.d("showNotification[%s] cancel notification.", account.acct) log.d("showNotification[${account.acct}] cancel notification.")
notification_manager.cancel(notification_tag, NOTIFICATION_ID) notification_manager.cancel(notification_tag, NOTIFICATION_ID)
return return
} }
@ -1468,18 +1456,14 @@ class PollingWorker private constructor(c : Context) {
// 先頭にあるデータが同じなら、通知を更新しない // 先頭にあるデータが同じなら、通知を更新しない
// このマーカーは端末再起動時にリセットされるので、再起動後は通知が出るはず // このマーカーは端末再起動時にリセットされるので、再起動後は通知が出るはず
log.d( log.d("showNotification[${account.acct}] id=${item.notification.id} is already shown.")
"showNotification[%s] id=%s is already shown.",
account.acct,
item.notification.id
)
return return
} }
nt.updatePost(item.notification.id, item.notification.time_created_at) nt.updatePost(item.notification.id, item.notification.time_created_at)
log.d("showNotification[%s] creating notification(1)", account.acct) log.d("showNotification[${account.acct}] creating notification(1)")
// 通知タップ時のPendingIntent // 通知タップ時のPendingIntent
val intent_click = Intent(context, ActCallback::class.java) val intent_click = Intent(context, ActCallback::class.java)
@ -1505,7 +1489,7 @@ class PollingWorker private constructor(c : Context) {
PendingIntent.FLAG_UPDATE_CURRENT PendingIntent.FLAG_UPDATE_CURRENT
) )
log.d("showNotification[%s] creating notification(2)", account.acct) log.d("showNotification[${account.acct}] creating notification(2)")
val builder = if(Build.VERSION.SDK_INT >= 26) { val builder = if(Build.VERSION.SDK_INT >= 26) {
// Android 8 から、通知のスタイルはユーザが管理することになった // Android 8 から、通知のスタイルはユーザが管理することになった
@ -1534,7 +1518,7 @@ class PollingWorker private constructor(c : Context) {
// アカウント別にグループキーを設定する // アカウント別にグループキーを設定する
builder.setGroup(context.packageName + ":" + account.acct) builder.setGroup(context.packageName + ":" + account.acct)
log.d("showNotification[%s] creating notification(3)", account.acct) log.d("showNotification[${account.acct}] creating notification(3)")
if(Build.VERSION.SDK_INT < 26) { if(Build.VERSION.SDK_INT < 26) {
@ -1577,24 +1561,24 @@ class PollingWorker private constructor(c : Context) {
} }
} }
log.d("showNotification[%s] creating notification(4)", account.acct) log.d("showNotification[${account.acct}] creating notification(4)")
if(Pref.bpNotificationVibration(pref)) { if(Pref.bpNotificationVibration(pref)) {
iv = iv or NotificationCompat.DEFAULT_VIBRATE iv = iv or NotificationCompat.DEFAULT_VIBRATE
} }
log.d("showNotification[%s] creating notification(5)", account.acct) log.d("showNotification[${account.acct}] creating notification(5)")
if(Pref.bpNotificationLED(pref)) { if(Pref.bpNotificationLED(pref)) {
iv = iv or NotificationCompat.DEFAULT_LIGHTS iv = iv or NotificationCompat.DEFAULT_LIGHTS
} }
log.d("showNotification[%s] creating notification(6)", account.acct) log.d("showNotification[${account.acct}] creating notification(6)")
builder.setDefaults(iv) builder.setDefaults(iv)
} }
log.d("showNotification[%s] creating notification(7)", account.acct) log.d("showNotification[${account.acct}] creating notification(7)")
var a = getNotificationLine( var a = getNotificationLine(
item.notification.type, item.notification.type,
@ -1624,7 +1608,7 @@ class PollingWorker private constructor(c : Context) {
builder.setStyle(style) builder.setStyle(style)
} }
log.d("showNotification[%s] set notification...", account.acct) log.d("showNotification[${account.acct}] set notification...")
notification_manager.notify(notification_tag, NOTIFICATION_ID, builder.build()) notification_manager.notify(notification_tag, NOTIFICATION_ID, builder.build())
} }
@ -1653,7 +1637,7 @@ class PollingWorker private constructor(c : Context) {
if(id != null) { if(id != null) {
dst_array.add(src) dst_array.add(src)
duplicate_check.add(id) duplicate_check.add(id)
log.d("add old. id=%s", id) log.d("add old. id=${id}")
} }
} }
} }
@ -1664,7 +1648,7 @@ class PollingWorker private constructor(c : Context) {
for(item in data.list) { for(item in data.list) {
try { try {
if(duplicate_check.contains(item.id)) { if(duplicate_check.contains(item.id)) {
log.d("skip duplicate. id=%s", item.id) log.d("skip duplicate. id=${item.id}")
continue continue
} }
duplicate_check.add(item.id) duplicate_check.add(item.id)
@ -1675,7 +1659,7 @@ class PollingWorker private constructor(c : Context) {
|| ! account.notification_boost && TootNotification.TYPE_REBLOG == type || ! account.notification_boost && TootNotification.TYPE_REBLOG == type
|| ! account.notification_favourite && TootNotification.TYPE_FAVOURITE == type || ! account.notification_favourite && TootNotification.TYPE_FAVOURITE == type
|| ! account.notification_follow && TootNotification.TYPE_FOLLOW == type) { || ! account.notification_follow && TootNotification.TYPE_FOLLOW == type) {
log.d("skip by setting. id=%s", item.id) log.d("skip by setting. id=${item.id}")
continue continue
} }
@ -1702,7 +1686,7 @@ class PollingWorker private constructor(c : Context) {
val d = JSONArray() val d = JSONArray()
for(i in 0 .. 9) { for(i in 0 .. 9) {
if(i >= dst_array.size) { if(i >= dst_array.size) {
log.d("inject %s data", i) log.d("inject $i data.")
break break
} }
d.put(dst_array[i]) d.put(dst_array[i])

View File

@ -101,7 +101,7 @@ object Pref {
editor.putString(key, v) editor.putString(key, v)
} }
fun optInt(pref : SharedPreferences) = invoke(pref).optInt() ?: defVal.optInt() fun toInt(pref : SharedPreferences) = invoke(pref).optInt() ?: defVal.toInt()
} }
// boolean // boolean
@ -344,6 +344,7 @@ object Pref {
val spMspUserToken = StringPref("mastodon_search_portal_user_token", "") val spMspUserToken = StringPref("mastodon_search_portal_user_token", "")
val spEmojiPickerRecent = StringPref("emoji_picker_recent", "") val spEmojiPickerRecent = StringPref("emoji_picker_recent", "")
val spRoundRatio = StringPref("round_ratio", "33") val spRoundRatio = StringPref("round_ratio", "33")
val spPullNotificationCheckInterval = StringPref("PullNotificationCheckInterval", "15")
// long // long

View File

@ -136,6 +136,27 @@
android:text="@string/send_access_token_to_app_server_desc" android:text="@string/send_access_token_to_app_server_desc"
/> />
</LinearLayout> </LinearLayout>
<View style="@style/setting_divider"/>
<TextView
style="@style/setting_row_label"
android:labelFor="@+id/etPullNotificationCheckInterval"
android:text="@string/pull_notification_check_interval"
/>
<LinearLayout style="@style/setting_row_form">
<EditText
android:id="@+id/etPullNotificationCheckInterval"
style="@style/setting_horizontal_stretch"
android:inputType="number"
/>
</LinearLayout>
<View style="@style/setting_divider"/> <View style="@style/setting_divider"/>
<!-- =============================================== --> <!-- =============================================== -->

View File

@ -634,6 +634,7 @@
<string name="send_access_token_to_app_server_desc">Normally you can use "pull" notification (with delay) without app server, because it works on your device. <string name="send_access_token_to_app_server_desc">Normally you can use "pull" notification (with delay) without app server, because it works on your device.
\nBut if you want to use "custom notification listener", or if you are the member of very limited instances that is supported by ST\'s app server, \nBut if you want to use "custom notification listener", or if you are the member of very limited instances that is supported by ST\'s app server,
\nyou can use "push" notifications, but you have to allow sending access tokens to the app server. (this is mastodon's defect)</string> \nyou can use "push" notifications, but you have to allow sending access tokens to the app server. (this is mastodon's defect)</string>
<string name="pull_notification_check_interval">Pull notification check interval (unit: minutes, default:15, min:15)</string>
<!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</string>--> <!--<string name="abc_action_bar_home_description">Revenir à l\'accueil</string>-->
<!--<string name="abc_action_bar_home_description_format">%1$s, %2$s</string>--> <!--<string name="abc_action_bar_home_description_format">%1$s, %2$s</string>-->

View File

@ -914,4 +914,5 @@
<string name="instance_local">インスタンス内のみ</string> <string name="instance_local">インスタンス内のみ</string>
<string name="send_access_token_to_app_server">アクセストークンをアプリサーバに送信する</string> <string name="send_access_token_to_app_server">アクセストークンをアプリサーバに送信する</string>
<string name="send_access_token_to_app_server_desc">通常は"pull"通知(すこし遅れる)を利用できます。それは端末上で動作するのでアプリサーバは必要ありません。しかしカスタム通知リスナを使う場合やSTがサポートする限られたインスタンスのユーザである場合は"push"通知を利用することができます。ただしアクセストークンをアプリサーバに送信する必要があります(これはマストドンの欠陥です)。</string> <string name="send_access_token_to_app_server_desc">通常は"pull"通知(すこし遅れる)を利用できます。それは端末上で動作するのでアプリサーバは必要ありません。しかしカスタム通知リスナを使う場合やSTがサポートする限られたインスタンスのユーザである場合は"push"通知を利用することができます。ただしアクセストークンをアプリサーバに送信する必要があります(これはマストドンの欠陥です)。</string>
<string name="pull_notification_check_interval">Pull通知の確認間隔 (単位:分, デフォルト:15, 最低: 15)</string>
</resources> </resources>

View File

@ -621,4 +621,5 @@
<string name="send_access_token_to_app_server_desc">Normally you can use "pull" notification (with delay) without app server, because it works on your device. <string name="send_access_token_to_app_server_desc">Normally you can use "pull" notification (with delay) without app server, because it works on your device.
\nBut if you want to use "custom notification listener", or if you are the member of very limited instances that is supported by ST\'s app server, \nBut if you want to use "custom notification listener", or if you are the member of very limited instances that is supported by ST\'s app server,
\nyou can use "push" notifications, but you have to allow sending access tokens to the app server. (this is mastodon's defect)</string> \nyou can use "push" notifications, but you have to allow sending access tokens to the app server. (this is mastodon's defect)</string>
<string name="pull_notification_check_interval">Pull notification check interval (unit: minutes, default:15, min:15)</string>
</resources> </resources>