more split...
This commit is contained in:
parent
9395774a48
commit
3eccf01edd
|
@ -14,6 +14,8 @@ import org.jetbrains.anko.backgroundDrawable
|
|||
import kotlin.math.abs
|
||||
import kotlin.math.min
|
||||
|
||||
private val log = LogCategory("ActMainColumns")
|
||||
|
||||
// スマホモードなら現在のカラムを、タブレットモードなら-1Lを返す
|
||||
// (カラム一覧画面のデフォルト選択位置に使われる)
|
||||
val ActMain.currentColumn: Int
|
||||
|
@ -348,13 +350,13 @@ fun ActMain.scrollToColumn(index: Int, smoothScroll: Boolean = true) {
|
|||
|
||||
// スマホはスムーススクロール基本ありだがたまにしない
|
||||
{ env ->
|
||||
ActMain.log.d("ipLastColumnPos beforeScroll=${env.pager.currentItem}")
|
||||
log.d("ipLastColumnPos beforeScroll=${env.pager.currentItem}")
|
||||
env.pager.setCurrentItem(index, smoothScroll)
|
||||
},
|
||||
|
||||
// タブレットでスムーススクロールさせると頻繁にオーバーランするので絶対しない
|
||||
{ env ->
|
||||
ActMain.log.d("ipLastColumnPos beforeScroll=${env.visibleColumnsIndices.first}")
|
||||
log.d("ipLastColumnPos beforeScroll=${env.visibleColumnsIndices.first}")
|
||||
env.tabletPager.scrollToPosition(index)
|
||||
}
|
||||
)
|
||||
|
@ -371,7 +373,7 @@ fun ActMain.resizeColumnWidth(views: TabletViews) {
|
|||
columnWMinDp = iv
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
ActMain.log.trace(ex)
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,10 +6,7 @@ import android.util.JsonReader
|
|||
import android.view.WindowManager
|
||||
import androidx.annotation.WorkerThread
|
||||
import jp.juggler.subwaytooter.notification.PollingWorker
|
||||
import jp.juggler.util.launchMain
|
||||
import jp.juggler.util.runOnMainLooper
|
||||
import jp.juggler.util.runWithProgress
|
||||
import jp.juggler.util.showToast
|
||||
import jp.juggler.util.*
|
||||
import kotlinx.coroutines.delay
|
||||
import org.apache.commons.io.IOUtils
|
||||
import java.io.File
|
||||
|
@ -19,6 +16,8 @@ import java.io.InputStreamReader
|
|||
import java.util.ArrayList
|
||||
import java.util.zip.ZipInputStream
|
||||
|
||||
private val log = LogCategory("ActMainImportAppData")
|
||||
|
||||
@WorkerThread
|
||||
fun ActMain.importAppData(uri: Uri) {
|
||||
launchMain {
|
||||
|
@ -72,7 +71,7 @@ fun ActMain.importAppData(uri: Uri) {
|
|||
PollingWorker.queueAppDataImportBefore(this@importAppData)
|
||||
while (PollingWorker.mBusyAppDataImportBefore.get()) {
|
||||
delay(1000L)
|
||||
ActMain.log.d("syncing polling task...")
|
||||
log.d("syncing polling task...")
|
||||
}
|
||||
|
||||
// データを読み込む
|
||||
|
@ -109,7 +108,7 @@ fun ActMain.importAppData(uri: Uri) {
|
|||
}
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
ActMain.log.trace(ex)
|
||||
log.trace(ex)
|
||||
if (zipEntryCount != 0) {
|
||||
showToast(ex, "importAppData failed.")
|
||||
}
|
||||
|
|
|
@ -376,7 +376,7 @@ class ActPost : AppCompatActivity(),
|
|||
override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
val rv = super.onKeyUp(keyCode, event)
|
||||
if (event?.isCtrlPressed == true) {
|
||||
ActMain.log.d("onKeyUp code=$keyCode rv=$rv")
|
||||
log.d("onKeyUp code=$keyCode rv=$rv")
|
||||
when (keyCode) {
|
||||
KeyEvent.KEYCODE_T -> btnPost.performClick()
|
||||
}
|
||||
|
|
|
@ -177,6 +177,10 @@ object AppDataExporter {
|
|||
reader.beginObject()
|
||||
while (reader.hasNext()) {
|
||||
val name = reader.nextName()
|
||||
if (name == null) {
|
||||
reader.skipValue()
|
||||
continue
|
||||
}
|
||||
|
||||
if (BaseColumns._ID == name) {
|
||||
old_id = reader.nextLong()
|
||||
|
@ -191,9 +195,9 @@ object AppDataExporter {
|
|||
}
|
||||
|
||||
// リアルタイム通知に関連する項目は読み飛ばす
|
||||
if (SavedAccount.COL_NOTIFICATION_TAG == name ||
|
||||
SavedAccount.COL_REGISTER_KEY == name ||
|
||||
SavedAccount.COL_REGISTER_TIME == name
|
||||
if (SavedAccount.COL_NOTIFICATION_TAG.name == name ||
|
||||
SavedAccount.COL_REGISTER_KEY.name == name ||
|
||||
SavedAccount.COL_REGISTER_TIME.name == name
|
||||
) {
|
||||
reader.skipValue()
|
||||
continue
|
||||
|
|
|
@ -700,7 +700,7 @@ fun ItemViewHolder.showScheduled(item: TootScheduled) {
|
|||
tvContentWarning.text = decodedSpoilerText
|
||||
spoilerInvalidator.register(decodedSpoilerText)
|
||||
val cwShown = ContentWarning.isShown(item.uri, accessInfo.expand_cw)
|
||||
showContent(cwShown)
|
||||
setContentVisibility(cwShown)
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
|
|
@ -179,7 +179,8 @@ private fun ItemViewHolder.showSpoilerTextAndContent(status: TootStatus) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun ItemViewHolder.setContentVisibility(shown: Boolean) {
|
||||
// 予約投稿でも使う
|
||||
fun ItemViewHolder.setContentVisibility(shown: Boolean) {
|
||||
llContents.visibility = if (shown) View.VISIBLE else View.GONE
|
||||
btnContentWarning.setText(if (shown) R.string.hide else R.string.show)
|
||||
statusShowing?.let { status ->
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
package jp.juggler.subwaytooter.api.entity
|
||||
|
||||
import jp.juggler.subwaytooter.api.TootParser
|
||||
import jp.juggler.subwaytooter.table.b2i
|
||||
import jp.juggler.util.JsonObject
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.asciiPattern
|
||||
import jp.juggler.util.groupEx
|
||||
import jp.juggler.util.*
|
||||
import java.util.*
|
||||
|
||||
class TootList(parser: TootParser, src: JsonObject) : TimelineItem(), Comparable<TootList> {
|
||||
|
|
|
@ -44,121 +44,281 @@ class TaskRunner(
|
|||
val notificationManager = pollingWorker.notificationManager
|
||||
val pref = pollingWorker.pref
|
||||
|
||||
val threadList = LinkedList<AccountRunner>()
|
||||
val errorInstance = ArrayList<String>()
|
||||
|
||||
private fun createErrorNotification(instanceList: ArrayList<String>) {
|
||||
if (instanceList.isEmpty()) return
|
||||
|
||||
// 通知タップ時のPendingIntent
|
||||
val clickIntent = Intent(context, ActCallback::class.java)
|
||||
// FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY を付与してはいけない
|
||||
clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
val clickPi = PendingIntent.getActivity(
|
||||
context,
|
||||
3,
|
||||
clickIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or (if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_IMMUTABLE else 0)
|
||||
)
|
||||
|
||||
val builder = if (Build.VERSION.SDK_INT >= 26) {
|
||||
// Android 8 から、通知のスタイルはユーザが管理することになった
|
||||
// NotificationChannel を端末に登録しておけば、チャネルごとに管理画面が作られる
|
||||
val channel = NotificationHelper.createNotificationChannel(
|
||||
context,
|
||||
"ErrorNotification",
|
||||
"Error",
|
||||
null,
|
||||
2 /* NotificationManager.IMPORTANCE_LOW */
|
||||
)
|
||||
NotificationCompat.Builder(context, channel.id)
|
||||
} else {
|
||||
NotificationCompat.Builder(context, "not_used")
|
||||
}
|
||||
|
||||
builder
|
||||
.setContentIntent(clickPi)
|
||||
.setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_notification) // ここは常に白テーマのアイコンを使う
|
||||
.setColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.Light_colorAccent
|
||||
)
|
||||
) // ここは常に白テーマの色を使う
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setGroup(context.packageName + ":" + "Error")
|
||||
|
||||
val header = context.getString(R.string.error_notification_title)
|
||||
val summary = context.getString(R.string.error_notification_summary)
|
||||
|
||||
builder
|
||||
.setContentTitle(header)
|
||||
.setContentText(summary + ": " + instanceList[0])
|
||||
|
||||
val style = NotificationCompat.InboxStyle()
|
||||
.setBigContentTitle(header)
|
||||
.setSummaryText(summary)
|
||||
for (i in 0..4) {
|
||||
if (i >= instanceList.size) break
|
||||
style.addLine(instanceList[i])
|
||||
}
|
||||
builder.setStyle(style)
|
||||
|
||||
notificationManager.notify(PollingWorker.NOTIFICATION_ID_ERROR, builder.build())
|
||||
}
|
||||
|
||||
private fun NotificationData.getNotificationLine(): String {
|
||||
|
||||
val name = when (PrefB.bpShowAcctInSystemNotification(pref)) {
|
||||
false -> notification.accountRef?.decoded_display_name
|
||||
|
||||
true -> {
|
||||
val acctPretty = notification.accountRef?.get()?.acct?.pretty
|
||||
if (acctPretty?.isNotEmpty() == true) {
|
||||
"@$acctPretty"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
} ?: "?"
|
||||
|
||||
return "- " + when (notification.type) {
|
||||
TootNotification.TYPE_MENTION,
|
||||
TootNotification.TYPE_REPLY,
|
||||
->
|
||||
context.getString(R.string.display_name_replied_by, name)
|
||||
|
||||
TootNotification.TYPE_RENOTE,
|
||||
TootNotification.TYPE_REBLOG,
|
||||
->
|
||||
context.getString(R.string.display_name_boosted_by, name)
|
||||
|
||||
TootNotification.TYPE_QUOTE ->
|
||||
context.getString(R.string.display_name_quoted_by, name)
|
||||
|
||||
TootNotification.TYPE_STATUS ->
|
||||
context.getString(R.string.display_name_posted_by, name)
|
||||
|
||||
TootNotification.TYPE_FOLLOW ->
|
||||
context.getString(R.string.display_name_followed_by, name)
|
||||
|
||||
TootNotification.TYPE_UNFOLLOW ->
|
||||
context.getString(R.string.display_name_unfollowed_by, name)
|
||||
|
||||
TootNotification.TYPE_FAVOURITE ->
|
||||
context.getString(R.string.display_name_favourited_by, name)
|
||||
|
||||
TootNotification.TYPE_EMOJI_REACTION_PLEROMA,
|
||||
TootNotification.TYPE_EMOJI_REACTION,
|
||||
TootNotification.TYPE_REACTION,
|
||||
->
|
||||
context.getString(R.string.display_name_reaction_by, name)
|
||||
|
||||
TootNotification.TYPE_VOTE,
|
||||
TootNotification.TYPE_POLL_VOTE_MISSKEY,
|
||||
->
|
||||
context.getString(R.string.display_name_voted_by, name)
|
||||
|
||||
TootNotification.TYPE_FOLLOW_REQUEST,
|
||||
TootNotification.TYPE_FOLLOW_REQUEST_MISSKEY,
|
||||
->
|
||||
context.getString(R.string.display_name_follow_request_by, name)
|
||||
|
||||
TootNotification.TYPE_FOLLOW_REQUEST_ACCEPTED_MISSKEY ->
|
||||
context.getString(R.string.display_name_follow_request_accepted_by, name)
|
||||
|
||||
TootNotification.TYPE_POLL ->
|
||||
context.getString(R.string.end_of_polling_from, name)
|
||||
|
||||
else -> "?"
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteCacheData(dbId: Long?) {
|
||||
if (dbId != null) {
|
||||
log.d("Notification clear! db_id=$dbId")
|
||||
SavedAccount.loadAccount(context, dbId) ?: return
|
||||
NotificationCache.deleteCache(dbId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun beforePolling(): Boolean {
|
||||
// タスクによってはポーリング前にすることがある
|
||||
@Suppress("NON_EXHAUSTIVE_WHEN")
|
||||
when (taskId) {
|
||||
|
||||
TaskId.BootCompleted ->
|
||||
NotificationTracking.resetPostAll()
|
||||
|
||||
TaskId.PackageReplaced ->
|
||||
NotificationTracking.resetPostAll()
|
||||
|
||||
TaskId.DataInjected ->
|
||||
pollingWorker.processInjectedData(job.injectedAccounts)
|
||||
|
||||
TaskId.ResetTrackingState ->
|
||||
NotificationTracking.resetTrackingState(taskData.long(PollingWorker.EXTRA_DB_ID))
|
||||
|
||||
// プッシュ通知が届いた
|
||||
TaskId.FcmMessage -> {
|
||||
var bDone = false
|
||||
val tag = taskData.string(PollingWorker.EXTRA_TAG)
|
||||
if (tag != null) {
|
||||
if (tag.startsWith("acct<>")) {
|
||||
val acct = tag.substring(6)
|
||||
val sa = SavedAccount.loadAccountByAcct(context, acct)
|
||||
if (sa != null) {
|
||||
NotificationCache.resetLastLoad(sa.db_id)
|
||||
job.injectedAccounts.add(sa.db_id)
|
||||
bDone = true
|
||||
}
|
||||
}
|
||||
if (!bDone) {
|
||||
for (sa in SavedAccount.loadByTag(context, tag)) {
|
||||
NotificationCache.resetLastLoad(sa.db_id)
|
||||
job.injectedAccounts.add(sa.db_id)
|
||||
bDone = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bDone) {
|
||||
// タグにマッチする情報がなかった場合、全部読み直す
|
||||
NotificationCache.resetLastLoad()
|
||||
}
|
||||
}
|
||||
|
||||
TaskId.Clear -> {
|
||||
deleteCacheData(taskData.long(PollingWorker.EXTRA_DB_ID))
|
||||
}
|
||||
|
||||
TaskId.NotificationDelete -> {
|
||||
val dbId = taskData.long(PollingWorker.EXTRA_DB_ID)
|
||||
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=$dbId,type=$type,id=$id")
|
||||
if (dbId != null) {
|
||||
NotificationTracking.updateRead(dbId, typeName)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
TaskId.NotificationClick -> {
|
||||
val dbId = taskData.long(PollingWorker.EXTRA_DB_ID)
|
||||
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=$dbId,type=$type,id=$id")
|
||||
if (dbId != null) {
|
||||
// 通知をキャンセル
|
||||
val notificationTag = when (typeName) {
|
||||
"" -> "$dbId/_"
|
||||
else -> "$dbId/$typeName"
|
||||
}
|
||||
if (id != null) {
|
||||
val itemTag = "$notificationTag/$id"
|
||||
notificationManager.cancel(itemTag, PollingWorker.NOTIFICATION_ID)
|
||||
} else {
|
||||
notificationManager.cancel(
|
||||
notificationTag,
|
||||
PollingWorker.NOTIFICATION_ID
|
||||
)
|
||||
}
|
||||
// DB更新処理
|
||||
NotificationTracking.updateRead(dbId, typeName)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private suspend fun prepareInstallId() {
|
||||
// インストールIDを生成する
|
||||
// インストールID生成時にSavedAccountテーブルを操作することがあるので
|
||||
// アカウントリストの取得より先に行う
|
||||
if (job.installId == null) {
|
||||
PollingWorker.workerStatus = "make install id"
|
||||
job.installId = PollingWorker.prepareInstallId(context, job)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun startForAccount(sa: SavedAccount) {
|
||||
if (sa.isPseudo) return
|
||||
threadList.add(AccountRunner(sa).apply { start() })
|
||||
}
|
||||
|
||||
private suspend fun waitAllAccounts() {
|
||||
while (true) {
|
||||
// 同じホスト名が重複しないようにSetに集める
|
||||
val liveSet = TreeSet<Host>()
|
||||
for (t in threadList) {
|
||||
if (!t.isActive) continue
|
||||
if (job.isJobCancelled) t.cancel()
|
||||
liveSet.add(t.account.apiHost)
|
||||
}
|
||||
if (liveSet.isEmpty()) break
|
||||
PollingWorker.workerStatus =
|
||||
"waiting ${liveSet.joinToString(", ") { it.pretty }}"
|
||||
delay(if (job.isJobCancelled) 100L else 1000L)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun runTask() {
|
||||
workerStatus = "start task $taskId"
|
||||
|
||||
coroutineScope {
|
||||
try {
|
||||
// タスクによってはポーリング前にすることがある
|
||||
@Suppress("NON_EXHAUSTIVE_WHEN")
|
||||
when (taskId) {
|
||||
if (!beforePolling()) return@coroutineScope
|
||||
|
||||
TaskId.BootCompleted ->
|
||||
NotificationTracking.resetPostAll()
|
||||
|
||||
TaskId.PackageReplaced ->
|
||||
NotificationTracking.resetPostAll()
|
||||
|
||||
TaskId.DataInjected ->
|
||||
pollingWorker.processInjectedData(job.injectedAccounts)
|
||||
|
||||
TaskId.ResetTrackingState ->
|
||||
NotificationTracking.resetTrackingState(taskData.long(PollingWorker.EXTRA_DB_ID))
|
||||
|
||||
// プッシュ通知が届いた
|
||||
TaskId.FcmMessage -> {
|
||||
var bDone = false
|
||||
val tag = taskData.string(PollingWorker.EXTRA_TAG)
|
||||
if (tag != null) {
|
||||
if (tag.startsWith("acct<>")) {
|
||||
val acct = tag.substring(6)
|
||||
val sa = SavedAccount.loadAccountByAcct(context, acct)
|
||||
if (sa != null) {
|
||||
NotificationCache.resetLastLoad(sa.db_id)
|
||||
job.injectedAccounts.add(sa.db_id)
|
||||
bDone = true
|
||||
}
|
||||
}
|
||||
if (!bDone) {
|
||||
for (sa in SavedAccount.loadByTag(context, tag)) {
|
||||
NotificationCache.resetLastLoad(sa.db_id)
|
||||
job.injectedAccounts.add(sa.db_id)
|
||||
bDone = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bDone) {
|
||||
// タグにマッチする情報がなかった場合、全部読み直す
|
||||
NotificationCache.resetLastLoad()
|
||||
}
|
||||
}
|
||||
|
||||
TaskId.Clear -> {
|
||||
deleteCacheData(taskData.long(PollingWorker.EXTRA_DB_ID))
|
||||
}
|
||||
|
||||
TaskId.NotificationDelete -> {
|
||||
val dbId = taskData.long(PollingWorker.EXTRA_DB_ID)
|
||||
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=$dbId,type=$type,id=$id")
|
||||
if (dbId != null) {
|
||||
NotificationTracking.updateRead(dbId, typeName)
|
||||
}
|
||||
return@coroutineScope
|
||||
}
|
||||
|
||||
TaskId.NotificationClick -> {
|
||||
val dbId = taskData.long(PollingWorker.EXTRA_DB_ID)
|
||||
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=$dbId,type=$type,id=$id")
|
||||
if (dbId != null) {
|
||||
// 通知をキャンセル
|
||||
val notificationTag = when (typeName) {
|
||||
"" -> "$dbId/_"
|
||||
else -> "$dbId/$typeName"
|
||||
}
|
||||
if (id != null) {
|
||||
val itemTag = "$notificationTag/$id"
|
||||
notificationManager.cancel(itemTag, PollingWorker.NOTIFICATION_ID)
|
||||
} else {
|
||||
notificationManager.cancel(
|
||||
notificationTag,
|
||||
PollingWorker.NOTIFICATION_ID
|
||||
)
|
||||
}
|
||||
// DB更新処理
|
||||
NotificationTracking.updateRead(dbId, typeName)
|
||||
}
|
||||
return@coroutineScope
|
||||
}
|
||||
}
|
||||
|
||||
// インストールIDを生成する
|
||||
// インストールID生成時にSavedAccountテーブルを操作することがあるので
|
||||
// アカウントリストの取得より先に行う
|
||||
if (job.installId == null) {
|
||||
PollingWorker.workerStatus = "make install id"
|
||||
job.installId = PollingWorker.prepareInstallId(context, job)
|
||||
}
|
||||
prepareInstallId()
|
||||
|
||||
// アカウント別に処理スレッドを作る
|
||||
PollingWorker.workerStatus = "create account threads"
|
||||
|
||||
val threadList = LinkedList<AccountRunner>()
|
||||
|
||||
suspend fun startForAccount(sa: SavedAccount) {
|
||||
if (sa.isPseudo) return
|
||||
threadList.add(AccountRunner(sa).apply { start() })
|
||||
}
|
||||
|
||||
if (job.injectedAccounts.isNotEmpty()) {
|
||||
// 更新対象アカウントが限られているなら、そのdb_idだけ処理する
|
||||
job.injectedAccounts.forEach { dbId ->
|
||||
|
@ -169,19 +329,7 @@ class TaskRunner(
|
|||
SavedAccount.loadAccountList(context).forEach { startForAccount(it) }
|
||||
}
|
||||
|
||||
while (true) {
|
||||
// 同じホスト名が重複しないようにSetに集める
|
||||
val liveSet = TreeSet<Host>()
|
||||
for (t in threadList) {
|
||||
if (!t.isActive) continue
|
||||
if (job.isJobCancelled) t.cancel()
|
||||
liveSet.add(t.account.apiHost)
|
||||
}
|
||||
if (liveSet.isEmpty()) break
|
||||
PollingWorker.workerStatus =
|
||||
"waiting ${liveSet.joinToString(", ") { it.pretty }}"
|
||||
delay(if (job.isJobCancelled) 100L else 1000L)
|
||||
}
|
||||
waitAllAccounts()
|
||||
|
||||
synchronized(errorInstance) {
|
||||
createErrorNotification(errorInstance)
|
||||
|
@ -254,6 +402,15 @@ class TaskRunner(
|
|||
|
||||
private val favMuteSet: HashSet<Acct> get() = job.favMuteSet
|
||||
|
||||
private val onError: (TootApiResult) -> Unit = { result ->
|
||||
val sv = result.error
|
||||
if (sv?.contains("Timeout") == true && !account.dont_show_timeout) {
|
||||
synchronized(errorInstance) {
|
||||
if (!errorInstance.any { it == sv }) errorInstance.add(sv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun cancel() {
|
||||
try {
|
||||
currentCall?.get()?.cancel()
|
||||
|
@ -270,15 +427,6 @@ class TaskRunner(
|
|||
}
|
||||
}
|
||||
|
||||
private val onError: (TootApiResult) -> Unit = { result ->
|
||||
val sv = result.error
|
||||
if (sv?.contains("Timeout") == true && !account.dont_show_timeout) {
|
||||
synchronized(errorInstance) {
|
||||
if (!errorInstance.any { it == sv }) errorInstance.add(sv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun runSuspend() {
|
||||
try {
|
||||
// 疑似アカウントはチェック対象外
|
||||
|
@ -708,140 +856,4 @@ class TaskRunner(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createErrorNotification(instanceList: ArrayList<String>) {
|
||||
if (instanceList.isEmpty()) return
|
||||
|
||||
// 通知タップ時のPendingIntent
|
||||
val clickIntent = Intent(context, ActCallback::class.java)
|
||||
// FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY を付与してはいけない
|
||||
clickIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
val clickPi = PendingIntent.getActivity(
|
||||
context,
|
||||
3,
|
||||
clickIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT or (if (Build.VERSION.SDK_INT >= 23) PendingIntent.FLAG_IMMUTABLE else 0)
|
||||
)
|
||||
|
||||
val builder = if (Build.VERSION.SDK_INT >= 26) {
|
||||
// Android 8 から、通知のスタイルはユーザが管理することになった
|
||||
// NotificationChannel を端末に登録しておけば、チャネルごとに管理画面が作られる
|
||||
val channel = NotificationHelper.createNotificationChannel(
|
||||
context,
|
||||
"ErrorNotification",
|
||||
"Error",
|
||||
null,
|
||||
2 /* NotificationManager.IMPORTANCE_LOW */
|
||||
)
|
||||
NotificationCompat.Builder(context, channel.id)
|
||||
} else {
|
||||
NotificationCompat.Builder(context, "not_used")
|
||||
}
|
||||
|
||||
builder
|
||||
.setContentIntent(clickPi)
|
||||
.setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_notification) // ここは常に白テーマのアイコンを使う
|
||||
.setColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.Light_colorAccent
|
||||
)
|
||||
) // ここは常に白テーマの色を使う
|
||||
.setWhen(System.currentTimeMillis())
|
||||
.setGroup(context.packageName + ":" + "Error")
|
||||
|
||||
val header = context.getString(R.string.error_notification_title)
|
||||
val summary = context.getString(R.string.error_notification_summary)
|
||||
|
||||
builder
|
||||
.setContentTitle(header)
|
||||
.setContentText(summary + ": " + instanceList[0])
|
||||
|
||||
val style = NotificationCompat.InboxStyle()
|
||||
.setBigContentTitle(header)
|
||||
.setSummaryText(summary)
|
||||
for (i in 0..4) {
|
||||
if (i >= instanceList.size) break
|
||||
style.addLine(instanceList[i])
|
||||
}
|
||||
builder.setStyle(style)
|
||||
|
||||
notificationManager.notify(PollingWorker.NOTIFICATION_ID_ERROR, builder.build())
|
||||
}
|
||||
|
||||
private fun NotificationData.getNotificationLine(): String {
|
||||
|
||||
val name = when (PrefB.bpShowAcctInSystemNotification(pref)) {
|
||||
false -> notification.accountRef?.decoded_display_name
|
||||
|
||||
true -> {
|
||||
val acctPretty = notification.accountRef?.get()?.acct?.pretty
|
||||
if (acctPretty?.isNotEmpty() == true) {
|
||||
"@$acctPretty"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
} ?: "?"
|
||||
|
||||
return "- " + when (notification.type) {
|
||||
TootNotification.TYPE_MENTION,
|
||||
TootNotification.TYPE_REPLY,
|
||||
->
|
||||
context.getString(R.string.display_name_replied_by, name)
|
||||
|
||||
TootNotification.TYPE_RENOTE,
|
||||
TootNotification.TYPE_REBLOG,
|
||||
->
|
||||
context.getString(R.string.display_name_boosted_by, name)
|
||||
|
||||
TootNotification.TYPE_QUOTE ->
|
||||
context.getString(R.string.display_name_quoted_by, name)
|
||||
|
||||
TootNotification.TYPE_STATUS ->
|
||||
context.getString(R.string.display_name_posted_by, name)
|
||||
|
||||
TootNotification.TYPE_FOLLOW ->
|
||||
context.getString(R.string.display_name_followed_by, name)
|
||||
|
||||
TootNotification.TYPE_UNFOLLOW ->
|
||||
context.getString(R.string.display_name_unfollowed_by, name)
|
||||
|
||||
TootNotification.TYPE_FAVOURITE ->
|
||||
context.getString(R.string.display_name_favourited_by, name)
|
||||
|
||||
TootNotification.TYPE_EMOJI_REACTION_PLEROMA,
|
||||
TootNotification.TYPE_EMOJI_REACTION,
|
||||
TootNotification.TYPE_REACTION,
|
||||
->
|
||||
context.getString(R.string.display_name_reaction_by, name)
|
||||
|
||||
TootNotification.TYPE_VOTE,
|
||||
TootNotification.TYPE_POLL_VOTE_MISSKEY,
|
||||
->
|
||||
context.getString(R.string.display_name_voted_by, name)
|
||||
|
||||
TootNotification.TYPE_FOLLOW_REQUEST,
|
||||
TootNotification.TYPE_FOLLOW_REQUEST_MISSKEY,
|
||||
->
|
||||
context.getString(R.string.display_name_follow_request_by, name)
|
||||
|
||||
TootNotification.TYPE_FOLLOW_REQUEST_ACCEPTED_MISSKEY ->
|
||||
context.getString(R.string.display_name_follow_request_accepted_by, name)
|
||||
|
||||
TootNotification.TYPE_POLL ->
|
||||
context.getString(R.string.end_of_polling_from, name)
|
||||
|
||||
else -> "?"
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteCacheData(dbId: Long?) {
|
||||
if (dbId != null) {
|
||||
log.d("Notification clear! db_id=$dbId")
|
||||
SavedAccount.loadAccount(context, dbId) ?: return
|
||||
NotificationCache.deleteCache(dbId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import java.util.ArrayList
|
|||
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.TableCompanion
|
||||
|
||||
object AcctSet : TableCompanion {
|
||||
|
||||
|
|
|
@ -3,10 +3,7 @@ package jp.juggler.subwaytooter.table
|
|||
import android.content.ContentValues
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.util.JsonObject
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.getString
|
||||
import jp.juggler.util.decodeJsonObject
|
||||
import jp.juggler.util.*
|
||||
|
||||
object ClientInfo : TableCompanion {
|
||||
private val log = LogCategory("ClientInfo")
|
||||
|
|
|
@ -6,6 +6,8 @@ import android.database.sqlite.SQLiteDatabase
|
|||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.TableCompanion
|
||||
import jp.juggler.util.b2i
|
||||
import jp.juggler.util.getInt
|
||||
|
||||
object ContentWarning : TableCompanion {
|
||||
|
|
|
@ -7,6 +7,7 @@ import android.database.sqlite.SQLiteDatabase
|
|||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.api.entity.Acct
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.TableCompanion
|
||||
|
||||
object FavMute : TableCompanion {
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package jp.juggler.subwaytooter.table
|
||||
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
import jp.juggler.util.TableCompanion
|
||||
|
||||
object LogData : TableCompanion {
|
||||
// private const val TAG = "SubwayTooter"
|
||||
|
|
|
@ -5,6 +5,8 @@ import android.database.sqlite.SQLiteDatabase
|
|||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.api.entity.TootStatus
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.TableCompanion
|
||||
import jp.juggler.util.b2i
|
||||
import jp.juggler.util.getInt
|
||||
|
||||
object MediaShown : TableCompanion {
|
||||
|
|
|
@ -8,6 +8,7 @@ import java.util.HashSet
|
|||
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.TableCompanion
|
||||
|
||||
object MutedApp : TableCompanion {
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.database.sqlite.SQLiteDatabase
|
|||
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.TableCompanion
|
||||
import jp.juggler.util.WordTrieTree
|
||||
|
||||
object MutedWord : TableCompanion {
|
||||
|
|
|
@ -7,6 +7,7 @@ import jp.juggler.subwaytooter.App1
|
|||
import jp.juggler.subwaytooter.api.entity.EntityId
|
||||
import jp.juggler.subwaytooter.api.entity.putMayNull
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.TableCompanion
|
||||
import jp.juggler.util.getLong
|
||||
import jp.juggler.util.minComparable
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
|
|
@ -5,10 +5,7 @@ import android.database.Cursor
|
|||
import android.database.sqlite.SQLiteDatabase
|
||||
import android.provider.BaseColumns
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.util.JsonObject
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.digestSHA256Hex
|
||||
import jp.juggler.util.decodeJsonObject
|
||||
import jp.juggler.util.*
|
||||
|
||||
class PostDraft {
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import jp.juggler.subwaytooter.notification.PollingWorker
|
|||
import jp.juggler.subwaytooter.util.LinkHelper
|
||||
import jp.juggler.util.*
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.max
|
||||
|
||||
class SavedAccount(
|
||||
|
@ -195,7 +196,7 @@ class SavedAccount(
|
|||
this.token_info = token_info
|
||||
|
||||
val cv = ContentValues()
|
||||
cv.put(COL_TOKEN, token_info.toString())
|
||||
COL_TOKEN.putTo(cv, token_info.toString())
|
||||
App1.database.update(table, cv, "$COL_ID=?", arrayOf(db_id.toString()))
|
||||
}
|
||||
|
||||
|
@ -204,40 +205,40 @@ class SavedAccount(
|
|||
if (db_id == INVALID_DB_ID) error("saveSetting: missing db_id")
|
||||
|
||||
val cv = ContentValues()
|
||||
cv.put(COL_VISIBILITY, visibility.id.toString())
|
||||
cv.put(COL_CONFIRM_BOOST, confirm_boost.b2i())
|
||||
cv.put(COL_CONFIRM_FAVOURITE, confirm_favourite.b2i())
|
||||
cv.put(COL_CONFIRM_UNBOOST, confirm_unboost.b2i())
|
||||
cv.put(COL_CONFIRM_UNFAVOURITE, confirm_unfavourite.b2i())
|
||||
COL_VISIBILITY.putTo(cv, visibility.id.toString())
|
||||
COL_CONFIRM_BOOST.putTo(cv, confirm_boost.b2i())
|
||||
COL_CONFIRM_FAVOURITE.putTo(cv, confirm_favourite.b2i())
|
||||
COL_CONFIRM_UNBOOST.putTo(cv, confirm_unboost.b2i())
|
||||
COL_CONFIRM_UNFAVOURITE.putTo(cv, confirm_unfavourite.b2i())
|
||||
|
||||
cv.put(COL_DONT_HIDE_NSFW, dont_hide_nsfw.b2i())
|
||||
cv.put(COL_DONT_SHOW_TIMEOUT, dont_show_timeout.b2i())
|
||||
cv.put(COL_NOTIFICATION_MENTION, notification_mention.b2i())
|
||||
cv.put(COL_NOTIFICATION_BOOST, notification_boost.b2i())
|
||||
cv.put(COL_NOTIFICATION_FAVOURITE, notification_favourite.b2i())
|
||||
cv.put(COL_NOTIFICATION_FOLLOW, notification_follow.b2i())
|
||||
cv.put(COL_NOTIFICATION_FOLLOW_REQUEST, notification_follow_request.b2i())
|
||||
cv.put(COL_NOTIFICATION_REACTION, notification_reaction.b2i())
|
||||
cv.put(COL_NOTIFICATION_VOTE, notification_vote.b2i())
|
||||
cv.put(COL_NOTIFICATION_POST, notification_post.b2i())
|
||||
COL_DONT_HIDE_NSFW.putTo(cv, dont_hide_nsfw.b2i())
|
||||
COL_DONT_SHOW_TIMEOUT.putTo(cv, dont_show_timeout.b2i())
|
||||
COL_NOTIFICATION_MENTION.putTo(cv, notification_mention.b2i())
|
||||
COL_NOTIFICATION_BOOST.putTo(cv, notification_boost.b2i())
|
||||
COL_NOTIFICATION_FAVOURITE.putTo(cv, notification_favourite.b2i())
|
||||
COL_NOTIFICATION_FOLLOW.putTo(cv, notification_follow.b2i())
|
||||
COL_NOTIFICATION_FOLLOW_REQUEST.putTo(cv, notification_follow_request.b2i())
|
||||
COL_NOTIFICATION_REACTION.putTo(cv, notification_reaction.b2i())
|
||||
COL_NOTIFICATION_VOTE.putTo(cv, notification_vote.b2i())
|
||||
COL_NOTIFICATION_POST.putTo(cv, notification_post.b2i())
|
||||
|
||||
cv.put(COL_CONFIRM_FOLLOW, confirm_follow.b2i())
|
||||
cv.put(COL_CONFIRM_FOLLOW_LOCKED, confirm_follow_locked.b2i())
|
||||
cv.put(COL_CONFIRM_UNFOLLOW, confirm_unfollow.b2i())
|
||||
cv.put(COL_CONFIRM_POST, confirm_post.b2i())
|
||||
cv.put(COL_CONFIRM_REACTION, confirm_reaction.b2i())
|
||||
COL_CONFIRM_FOLLOW.putTo(cv, confirm_follow.b2i())
|
||||
COL_CONFIRM_FOLLOW_LOCKED.putTo(cv, confirm_follow_locked.b2i())
|
||||
COL_CONFIRM_UNFOLLOW.putTo(cv, confirm_unfollow.b2i())
|
||||
COL_CONFIRM_POST.putTo(cv, confirm_post.b2i())
|
||||
COL_CONFIRM_REACTION.putTo(cv, confirm_reaction.b2i())
|
||||
|
||||
cv.put(COL_SOUND_URI, sound_uri)
|
||||
cv.put(COL_DEFAULT_TEXT, default_text)
|
||||
COL_SOUND_URI.putTo(cv, sound_uri)
|
||||
COL_DEFAULT_TEXT.putTo(cv, default_text)
|
||||
|
||||
cv.put(COL_DEFAULT_SENSITIVE, default_sensitive.b2i())
|
||||
cv.put(COL_EXPAND_CW, expand_cw.b2i())
|
||||
cv.put(COL_MAX_TOOT_CHARS, max_toot_chars)
|
||||
COL_DEFAULT_SENSITIVE.putTo(cv, default_sensitive.b2i())
|
||||
COL_EXPAND_CW.putTo(cv, expand_cw.b2i())
|
||||
COL_MAX_TOOT_CHARS.putTo(cv, max_toot_chars)
|
||||
|
||||
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)
|
||||
COL_IMAGE_RESIZE.putTo(cv, image_resize)
|
||||
COL_IMAGE_MAX_MEGABYTES.putTo(cv, image_max_megabytes)
|
||||
COL_MOVIE_MAX_MEGABYTES.putTo(cv, movie_max_megabytes)
|
||||
COL_PUSH_POLICY.putTo(cv, push_policy)
|
||||
|
||||
// UIからは更新しない
|
||||
// notification_tag
|
||||
|
@ -346,70 +347,74 @@ class SavedAccount(
|
|||
|
||||
const val table = "access_info"
|
||||
|
||||
private const val COL_ID = BaseColumns._ID
|
||||
private const val COL_HOST = "h"
|
||||
private const val COL_DOMAIN = "d"
|
||||
private const val COL_USER = "u"
|
||||
private const val COL_ACCOUNT = "a"
|
||||
private const val COL_TOKEN = "t"
|
||||
private val columnList = ColumnMeta.List(table)
|
||||
|
||||
private const val COL_VISIBILITY = "visibility"
|
||||
private const val COL_CONFIRM_BOOST = "confirm_boost"
|
||||
private const val COL_DONT_HIDE_NSFW = "dont_hide_nsfw"
|
||||
private val COL_ID = ColumnMeta(columnList, 0, BaseColumns._ID, "INTEGER PRIMARY KEY", primary = true)
|
||||
private val COL_HOST = ColumnMeta(columnList, 0, "h", "text not null")
|
||||
private val COL_DOMAIN = ColumnMeta(columnList, 56, "d", "text")
|
||||
private val COL_USER = ColumnMeta(columnList, 0, "u", "text not null")
|
||||
private val COL_ACCOUNT = ColumnMeta(columnList, 0, "a", "text not null")
|
||||
private val COL_TOKEN = ColumnMeta(columnList, 0, "t", "text not null")
|
||||
|
||||
private const val COL_NOTIFICATION_MENTION = "notification_mention" // スキーマ2
|
||||
private const val COL_NOTIFICATION_BOOST = "notification_boost" // スキーマ2
|
||||
private const val COL_NOTIFICATION_FAVOURITE = "notification_favourite" // スキーマ2
|
||||
private const val COL_NOTIFICATION_FOLLOW = "notification_follow" // スキーマ2
|
||||
private const val COL_NOTIFICATION_FOLLOW_REQUEST = "notification_follow_request" // スキーマ44
|
||||
private const val COL_NOTIFICATION_REACTION = "notification_reaction" // スキーマ33
|
||||
private const val COL_NOTIFICATION_VOTE = "notification_vote" // スキーマ33
|
||||
private const val COL_NOTIFICATION_POST = "notification_post" // スキーマ57
|
||||
private val COL_VISIBILITY = ColumnMeta(columnList, 0, "visibility", "text")
|
||||
private val COL_CONFIRM_BOOST = ColumnMeta(columnList, 0, "confirm_boost", ColumnMeta.TS_TRUE)
|
||||
private val COL_DONT_HIDE_NSFW = ColumnMeta(columnList, 0, "dont_hide_nsfw", ColumnMeta.TS_ZERO)
|
||||
|
||||
private const val COL_CONFIRM_FOLLOW = "confirm_follow" // スキーマ10
|
||||
private const val COL_CONFIRM_FOLLOW_LOCKED = "confirm_follow_locked" // スキーマ10
|
||||
private const val COL_CONFIRM_UNFOLLOW = "confirm_unfollow" // スキーマ10
|
||||
private const val COL_CONFIRM_POST = "confirm_post" // スキーマ10
|
||||
private const val COL_CONFIRM_FAVOURITE = "confirm_favourite" // スキーマ23
|
||||
private const val COL_CONFIRM_UNBOOST = "confirm_unboost" // スキーマ24
|
||||
private const val COL_CONFIRM_UNFAVOURITE = "confirm_unfavourite" // スキーマ24
|
||||
private const val COL_CONFIRM_REACTION = "confirm_reaction" // スキーマ61
|
||||
private val COL_NOTIFICATION_MENTION = ColumnMeta(columnList, 2, "notification_mention", ColumnMeta.TS_TRUE)
|
||||
private val COL_NOTIFICATION_BOOST = ColumnMeta(columnList, 2, "notification_boost", ColumnMeta.TS_TRUE)
|
||||
private val COL_NOTIFICATION_FAVOURITE = ColumnMeta(columnList, 2, "notification_favourite", ColumnMeta.TS_TRUE)
|
||||
private val COL_NOTIFICATION_FOLLOW = ColumnMeta(columnList, 2, "notification_follow", ColumnMeta.TS_TRUE)
|
||||
private val COL_NOTIFICATION_FOLLOW_REQUEST =
|
||||
ColumnMeta(columnList, 44, "notification_follow_request", ColumnMeta.TS_TRUE)
|
||||
private val COL_NOTIFICATION_REACTION = ColumnMeta(columnList, 33, "notification_reaction", ColumnMeta.TS_TRUE)
|
||||
private val COL_NOTIFICATION_VOTE = ColumnMeta(columnList, 33, "notification_vote", ColumnMeta.TS_TRUE)
|
||||
private val COL_NOTIFICATION_POST = ColumnMeta(columnList, 57, "notification_post", ColumnMeta.TS_TRUE)
|
||||
|
||||
private val COL_CONFIRM_FOLLOW = ColumnMeta(columnList, 10, "confirm_follow", ColumnMeta.TS_TRUE)
|
||||
private val COL_CONFIRM_FOLLOW_LOCKED = ColumnMeta(columnList, 10, "confirm_follow_locked", ColumnMeta.TS_TRUE)
|
||||
private val COL_CONFIRM_UNFOLLOW = ColumnMeta(columnList, 10, "confirm_unfollow", ColumnMeta.TS_TRUE)
|
||||
private val COL_CONFIRM_POST = ColumnMeta(columnList, 10, "confirm_post", ColumnMeta.TS_TRUE)
|
||||
private val COL_CONFIRM_FAVOURITE = ColumnMeta(columnList, 23, "confirm_favourite", ColumnMeta.TS_TRUE)
|
||||
private val COL_CONFIRM_UNBOOST = ColumnMeta(columnList, 24, "confirm_unboost", ColumnMeta.TS_TRUE)
|
||||
private val COL_CONFIRM_UNFAVOURITE = ColumnMeta(columnList, 24, "confirm_unfavourite", ColumnMeta.TS_TRUE)
|
||||
private val COL_CONFIRM_REACTION = ColumnMeta(columnList, 61, "confirm_reaction", ColumnMeta.TS_TRUE)
|
||||
|
||||
// スキーマ13から
|
||||
const val COL_NOTIFICATION_TAG = "notification_server"
|
||||
val COL_NOTIFICATION_TAG = ColumnMeta(columnList, 13, "notification_server", ColumnMeta.TS_EMPTY)
|
||||
|
||||
// スキーマ14から
|
||||
const val COL_REGISTER_KEY = "register_key"
|
||||
const val COL_REGISTER_TIME = "register_time"
|
||||
val COL_REGISTER_KEY = ColumnMeta(columnList, 14, "register_key", ColumnMeta.TS_EMPTY)
|
||||
val COL_REGISTER_TIME = ColumnMeta(columnList, 14, "register_time", ColumnMeta.TS_ZERO)
|
||||
|
||||
// スキーマ16から
|
||||
private const val COL_SOUND_URI = "sound_uri"
|
||||
private val COL_SOUND_URI = ColumnMeta(columnList, 16, "sound_uri", ColumnMeta.TS_EMPTY)
|
||||
|
||||
// スキーマ18から
|
||||
private const val COL_DONT_SHOW_TIMEOUT = "dont_show_timeout"
|
||||
private val COL_DONT_SHOW_TIMEOUT = ColumnMeta(columnList, 18, "dont_show_timeout", ColumnMeta.TS_ZERO)
|
||||
|
||||
// スキーマ27から
|
||||
private const val COL_DEFAULT_TEXT = "default_text"
|
||||
private val COL_DEFAULT_TEXT = ColumnMeta(columnList, 27, "default_text", ColumnMeta.TS_EMPTY)
|
||||
|
||||
// スキーマ28から
|
||||
private const val COL_MISSKEY_VERSION = "is_misskey" // カラム名がおかしいのは、昔はboolean扱いだったから
|
||||
private val COL_MISSKEY_VERSION = ColumnMeta(columnList, 28, "is_misskey", ColumnMeta.TS_ZERO)
|
||||
// カラム名がおかしいのは、昔はboolean扱いだったから
|
||||
// 0: not misskey
|
||||
// 1: old(v10) misskey
|
||||
// 11: misskey v11
|
||||
|
||||
private const val COL_DEFAULT_SENSITIVE = "default_sensitive"
|
||||
private const val COL_EXPAND_CW = "expand_cw"
|
||||
private const val COL_MAX_TOOT_CHARS = "max_toot_chars"
|
||||
private val COL_DEFAULT_SENSITIVE = ColumnMeta(columnList, 38, "default_sensitive", ColumnMeta.TS_ZERO)
|
||||
private val COL_EXPAND_CW = ColumnMeta(columnList, 38, "expand_cw", ColumnMeta.TS_ZERO)
|
||||
private val COL_MAX_TOOT_CHARS = ColumnMeta(columnList, 39, "max_toot_chars", ColumnMeta.TS_ZERO)
|
||||
|
||||
private const val COL_LAST_NOTIFICATION_ERROR = "last_notification_error" // スキーマ42
|
||||
private const val COL_LAST_SUBSCRIPTION_ERROR = "last_subscription_error" // スキーマ45
|
||||
private const val COL_LAST_PUSH_ENDPOINT = "last_push_endpoint" // スキーマ46
|
||||
private val COL_LAST_NOTIFICATION_ERROR = ColumnMeta(columnList, 42, "last_notification_error", "text")
|
||||
private val COL_LAST_SUBSCRIPTION_ERROR = ColumnMeta(columnList, 45, "last_subscription_error", "text")
|
||||
private val COL_LAST_PUSH_ENDPOINT = ColumnMeta(columnList, 46, "last_push_endpoint", "text")
|
||||
|
||||
private const val COL_IMAGE_RESIZE = "image_resize" // スキーマ59
|
||||
private const val COL_IMAGE_MAX_MEGABYTES = "image_max_megabytes" // スキーマ59
|
||||
private const val COL_MOVIE_MAX_MEGABYTES = "movie_max_megabytes" // スキーマ59
|
||||
private val COL_IMAGE_RESIZE = ColumnMeta(columnList, 59, "image_resize", "text default null")
|
||||
private val COL_IMAGE_MAX_MEGABYTES = ColumnMeta(columnList, 59, "image_max_megabytes", "text default null")
|
||||
private val COL_MOVIE_MAX_MEGABYTES = ColumnMeta(columnList, 59, "movie_max_megabytes", "text default null")
|
||||
|
||||
private const val COL_PUSH_POLICY = "push_policy" // スキーマ60
|
||||
private val COL_PUSH_POLICY = ColumnMeta(columnList, 60, "push_policy", "text default null")
|
||||
|
||||
/////////////////////////////////
|
||||
// login information
|
||||
|
@ -425,299 +430,13 @@ class SavedAccount(
|
|||
}
|
||||
|
||||
override fun onDBCreate(db: SQLiteDatabase) {
|
||||
db.execSQL(
|
||||
"""create table if not exists $table
|
||||
($COL_ID INTEGER PRIMARY KEY
|
||||
,$COL_USER text not null
|
||||
,$COL_HOST text not null
|
||||
,$COL_ACCOUNT text not null
|
||||
,$COL_TOKEN text not null
|
||||
,$COL_VISIBILITY text
|
||||
,$COL_CONFIRM_BOOST integer default 1
|
||||
,$COL_DONT_HIDE_NSFW integer default 0
|
||||
,$COL_NOTIFICATION_MENTION integer default 1
|
||||
,$COL_NOTIFICATION_BOOST integer default 1
|
||||
,$COL_NOTIFICATION_FAVOURITE integer default 1
|
||||
,$COL_NOTIFICATION_FOLLOW integer default 1
|
||||
,$COL_CONFIRM_FOLLOW integer default 1
|
||||
,$COL_CONFIRM_FOLLOW_LOCKED integer default 1
|
||||
,$COL_CONFIRM_UNFOLLOW integer default 1
|
||||
,$COL_CONFIRM_POST integer default 1
|
||||
,$COL_NOTIFICATION_TAG text default ''
|
||||
,$COL_REGISTER_KEY text default ''
|
||||
,$COL_REGISTER_TIME integer default 0
|
||||
,$COL_SOUND_URI text default ''
|
||||
,$COL_DONT_SHOW_TIMEOUT integer default 0
|
||||
,$COL_CONFIRM_FAVOURITE integer default 1
|
||||
,$COL_CONFIRM_UNBOOST integer default 1
|
||||
,$COL_CONFIRM_UNFAVOURITE integer default 1
|
||||
,$COL_DEFAULT_TEXT text default ''
|
||||
,$COL_MISSKEY_VERSION integer default 0
|
||||
,$COL_NOTIFICATION_REACTION integer default 1
|
||||
,$COL_NOTIFICATION_VOTE integer default 1
|
||||
,$COL_DEFAULT_SENSITIVE integer default 0
|
||||
,$COL_EXPAND_CW integer default 0
|
||||
,$COL_MAX_TOOT_CHARS integer default 0
|
||||
,$COL_LAST_NOTIFICATION_ERROR text
|
||||
,$COL_NOTIFICATION_FOLLOW_REQUEST integer default 1
|
||||
,$COL_LAST_SUBSCRIPTION_ERROR text
|
||||
,$COL_LAST_PUSH_ENDPOINT text
|
||||
,$COL_DOMAIN text
|
||||
,$COL_NOTIFICATION_POST integer default 1
|
||||
,$COL_IMAGE_RESIZE text default null
|
||||
,$COL_IMAGE_MAX_MEGABYTES text default null
|
||||
,$COL_MOVIE_MAX_MEGABYTES text default null
|
||||
,$COL_PUSH_POLICY text default null
|
||||
,$COL_CONFIRM_REACTION integer default 1
|
||||
)""".trimIndent()
|
||||
)
|
||||
db.execSQL("create table if not exists $table (${columnList.createParams()})")
|
||||
db.execSQL("create index if not exists ${table}_user on $table(u)")
|
||||
db.execSQL("create index if not exists ${table}_host on $table(h,u)")
|
||||
}
|
||||
|
||||
override fun onDBUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
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) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
try {
|
||||
db.execSQL("alter table $table add column notification_boost integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
try {
|
||||
db.execSQL("alter table $table add column notification_favourite integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
try {
|
||||
db.execSQL("alter table $table add column notification_follow integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(10) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_CONFIRM_FOLLOW integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_CONFIRM_FOLLOW_LOCKED integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_CONFIRM_UNFOLLOW integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_CONFIRM_POST integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
isUpgraded(13) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_NOTIFICATION_TAG text default ''")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
isUpgraded(14) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_REGISTER_KEY text default ''")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_REGISTER_TIME integer default 0")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
isUpgraded(16) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_SOUND_URI text default ''")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
isUpgraded(18) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_DONT_SHOW_TIMEOUT integer default 0")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(23) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_CONFIRM_FAVOURITE integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(24) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_CONFIRM_UNFAVOURITE integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_CONFIRM_UNBOOST integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(27) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_DEFAULT_TEXT text default ''")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(28) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_MISSKEY_VERSION integer default 0")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(33) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_NOTIFICATION_REACTION integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_NOTIFICATION_VOTE integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(38) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_DEFAULT_SENSITIVE integer default 0")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_EXPAND_CW integer default 0")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(39) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_MAX_TOOT_CHARS integer default 0")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(42) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_LAST_NOTIFICATION_ERROR text")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(44) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_NOTIFICATION_FOLLOW_REQUEST integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(45) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_LAST_SUBSCRIPTION_ERROR text")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(46) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_LAST_PUSH_ENDPOINT text")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(56) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_DOMAIN text")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(57) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_NOTIFICATION_POST integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(59) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_IMAGE_RESIZE text default null")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_IMAGE_MAX_MEGABYTES text default null")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_MOVIE_MAX_MEGABYTES text default null")
|
||||
} catch (ex: Throwable) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
isUpgraded(61) {
|
||||
try {
|
||||
db.execSQL("alter table $table add column $COL_CONFIRM_REACTION integer default 1")
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
columnList.addColumns(db, oldVersion, newVersion)
|
||||
}
|
||||
|
||||
val defaultResizeConfig = ResizeConfig(ResizeType.LongSide, 1280)
|
||||
|
@ -777,12 +496,12 @@ class SavedAccount(
|
|||
): Long {
|
||||
try {
|
||||
val cv = ContentValues()
|
||||
cv.put(COL_USER, acct)
|
||||
cv.put(COL_HOST, host)
|
||||
cv.putOrNull(COL_DOMAIN, domain)
|
||||
cv.put(COL_ACCOUNT, account.toString())
|
||||
cv.put(COL_TOKEN, token.toString())
|
||||
cv.put(COL_MISSKEY_VERSION, misskeyVersion)
|
||||
COL_USER.putTo(cv, acct)
|
||||
COL_HOST.putTo(cv, host)
|
||||
COL_DOMAIN.putTo(cv, domain)
|
||||
COL_ACCOUNT.putTo(cv, account.toString())
|
||||
COL_TOKEN.putTo(cv, token.toString())
|
||||
COL_MISSKEY_VERSION.putTo(cv, misskeyVersion)
|
||||
return App1.database.insert(table, null, cv)
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex)
|
||||
|
@ -794,8 +513,8 @@ class SavedAccount(
|
|||
|
||||
fun clearRegistrationCache() {
|
||||
val cv = ContentValues()
|
||||
cv.put(COL_REGISTER_KEY, REGISTER_KEY_UNREGISTERED)
|
||||
cv.put(COL_REGISTER_TIME, 0L)
|
||||
COL_REGISTER_KEY.putTo(cv, REGISTER_KEY_UNREGISTERED)
|
||||
COL_REGISTER_TIME.putTo(cv, 0L)
|
||||
App1.database.update(table, cv, null, null)
|
||||
}
|
||||
|
||||
|
@ -1019,7 +738,7 @@ class SavedAccount(
|
|||
null
|
||||
).use { cursor ->
|
||||
while (cursor.moveToNext()) {
|
||||
list.add(cursor.getLong(COL_ID))
|
||||
list.add(COL_ID.getLong(cursor))
|
||||
}
|
||||
}
|
||||
} catch (ex: Throwable) {
|
||||
|
@ -1106,7 +825,7 @@ class SavedAccount(
|
|||
if (ta != null) {
|
||||
this.loginAccount = ta
|
||||
val cv = ContentValues()
|
||||
cv.put(COL_ACCOUNT, result.jsonObject.toString())
|
||||
COL_ACCOUNT.putTo(cv, result.jsonObject.toString())
|
||||
App1.database.update(table, cv, "$COL_ID=?", arrayOf(db_id.toString()))
|
||||
PollingWorker.queueUpdateNotification(context)
|
||||
}
|
||||
|
@ -1119,40 +838,27 @@ class SavedAccount(
|
|||
}
|
||||
}
|
||||
|
||||
fun updateNotificationError(text: String?) {
|
||||
this.lastNotificationError = text
|
||||
private fun updateSingleString(col: ColumnMeta, value: String?) {
|
||||
if (db_id != INVALID_DB_ID) {
|
||||
val cv = ContentValues()
|
||||
when (text) {
|
||||
null -> cv.putNull(COL_LAST_NOTIFICATION_ERROR)
|
||||
else -> cv.put(COL_LAST_NOTIFICATION_ERROR, text)
|
||||
}
|
||||
col.putTo(cv, value)
|
||||
App1.database.update(table, cv, "$COL_ID=?", arrayOf(db_id.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
fun updateNotificationError(text: String?) {
|
||||
this.lastNotificationError = text
|
||||
updateSingleString(COL_LAST_NOTIFICATION_ERROR, text)
|
||||
}
|
||||
|
||||
fun updateSubscriptionError(text: String?) {
|
||||
this.last_subscription_error = text
|
||||
if (db_id != INVALID_DB_ID) {
|
||||
val cv = ContentValues()
|
||||
when (text) {
|
||||
null -> cv.putNull(COL_LAST_SUBSCRIPTION_ERROR)
|
||||
else -> cv.put(COL_LAST_SUBSCRIPTION_ERROR, text)
|
||||
}
|
||||
App1.database.update(table, cv, "$COL_ID=?", arrayOf(db_id.toString()))
|
||||
}
|
||||
updateSingleString(COL_LAST_SUBSCRIPTION_ERROR, text)
|
||||
}
|
||||
|
||||
fun updateLastPushEndpoint(text: String?) {
|
||||
this.last_push_endpoint = text
|
||||
if (db_id != INVALID_DB_ID) {
|
||||
val cv = ContentValues()
|
||||
when (text) {
|
||||
null -> cv.putNull(COL_LAST_PUSH_ENDPOINT)
|
||||
else -> cv.put(COL_LAST_PUSH_ENDPOINT, text)
|
||||
}
|
||||
App1.database.update(table, cv, "$COL_ID=?", arrayOf(db_id.toString()))
|
||||
}
|
||||
updateSingleString(COL_LAST_PUSH_ENDPOINT, text)
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean =
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.provider.BaseColumns
|
|||
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.TableCompanion
|
||||
import jp.juggler.util.getString
|
||||
|
||||
object SubscriptionServerKey : TableCompanion {
|
||||
|
|
|
@ -1,27 +0,0 @@
|
|||
package jp.juggler.subwaytooter.table
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
|
||||
// SQLite にBooleanをそのまま保存することはできないのでInt型との変換が必要になる
|
||||
|
||||
// boolean to integer
|
||||
fun Boolean.b2i() = if (this) 1 else 0
|
||||
|
||||
// integer to boolean
|
||||
fun Int.i2b() = this != 0
|
||||
|
||||
fun Cursor.getBoolean(keyIdx: Int) =
|
||||
getInt(keyIdx).i2b()
|
||||
|
||||
fun Cursor.getBoolean(key: String) =
|
||||
getBoolean(getColumnIndex(key))
|
||||
|
||||
interface TableCompanion {
|
||||
fun onDBCreate(db: SQLiteDatabase)
|
||||
fun onDBUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int)
|
||||
}
|
||||
|
||||
fun ContentValues.putOrNull(key: String, value: String?) =
|
||||
if (value == null) putNull(key) else put(key, value)
|
|
@ -7,6 +7,7 @@ import java.util.ArrayList
|
|||
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.TableCompanion
|
||||
|
||||
object TagSet : TableCompanion {
|
||||
|
||||
|
|
|
@ -15,8 +15,8 @@ import android.provider.BaseColumns
|
|||
import com.caverock.androidsvg.SVG
|
||||
import jp.juggler.apng.ApngFrames
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.table.TableCompanion
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.TableCompanion
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.lang.ref.WeakReference
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package jp.juggler.util
|
||||
|
||||
import org.intellij.lang.annotations.Language
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/*
|
||||
|
@ -32,10 +33,10 @@ http://userguide.icu-project.org/strings/regexp
|
|||
|
||||
*/
|
||||
|
||||
fun String.asciiPattern(flags: Int = 0): Pattern =
|
||||
fun @receiver:Language("RegExp") String.asciiPattern(flags: Int = 0): Pattern =
|
||||
Pattern.compile(this.asciiPatternString(), flags)
|
||||
|
||||
fun String.asciiPatternString(): String {
|
||||
fun @receiver:Language("RegExp") String.asciiPatternString(): String {
|
||||
val dst = StringBuilder()
|
||||
dst.ensureCapacity(this.length)
|
||||
var escaped = false
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package jp.juggler.subwaytooter
|
||||
package jp.juggler.util
|
||||
|
||||
import jp.juggler.subwaytooter.BuildConfig
|
||||
|
||||
import android.os.SystemClock
|
||||
|
||||
private val log = LogCategory("Benchmark")
|
||||
|
||||
val benchmarkLimitDefault = if (BuildConfig.DEBUG) 10L else 100L
|
||||
fun <T : Any?> benchmark(
|
||||
caption: String,
|
||||
|
@ -11,6 +15,6 @@ fun <T : Any?> benchmark(
|
|||
val start = SystemClock.elapsedRealtime()
|
||||
val rv = block()
|
||||
val duration = SystemClock.elapsedRealtime() - start
|
||||
if (duration >= limit) ActMain.log.w("benchmark: ${duration}ms : $caption")
|
||||
if (duration >= limit) log.w("benchmark: ${duration}ms : $caption")
|
||||
return rv
|
||||
}
|
|
@ -0,0 +1,149 @@
|
|||
package jp.juggler.util
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.database.sqlite.SQLiteDatabase
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// SQLite にBooleanをそのまま保存することはできないのでInt型との変換が必要になる
|
||||
|
||||
// boolean to integer
|
||||
fun Boolean.b2i() = if (this) 1 else 0
|
||||
|
||||
// integer to boolean
|
||||
fun Int.i2b() = this != 0
|
||||
|
||||
fun Cursor.getBoolean(keyIdx: Int) =
|
||||
getInt(keyIdx).i2b()
|
||||
|
||||
fun Cursor.getBoolean(key: String) =
|
||||
getBoolean(getColumnIndex(key))
|
||||
|
||||
fun Cursor.getInt(key: String) =
|
||||
getInt(getColumnIndex(key))
|
||||
|
||||
fun Cursor.getIntOrNull(idx: Int) =
|
||||
if (isNull(idx)) null else getInt(idx)
|
||||
|
||||
fun Cursor.getIntOrNull(key: String) =
|
||||
getIntOrNull(getColumnIndex(key))
|
||||
|
||||
fun Cursor.getLong(key: String) =
|
||||
getLong(getColumnIndex(key))
|
||||
|
||||
//fun Cursor.getLongOrNull(idx:Int) =
|
||||
// if(isNull(idx)) null else getLong(idx)
|
||||
|
||||
//fun Cursor.getLongOrNull(key:String) =
|
||||
// getLongOrNull(getColumnIndex(key))
|
||||
|
||||
fun Cursor.getString(key: String): String =
|
||||
getString(getColumnIndex(key))
|
||||
|
||||
fun Cursor.getStringOrNull(keyIdx: Int) =
|
||||
if (isNull(keyIdx)) null else getString(keyIdx)
|
||||
|
||||
fun Cursor.getStringOrNull(key: String) =
|
||||
getStringOrNull(getColumnIndex(key))
|
||||
|
||||
fun ContentValues.putOrNull(key: String, value: String?) =
|
||||
if (value == null) putNull(key) else put(key, value)
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
|
||||
interface TableCompanion {
|
||||
fun onDBCreate(db: SQLiteDatabase)
|
||||
fun onDBUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int)
|
||||
}
|
||||
|
||||
class ColumnMeta(
|
||||
list: List,
|
||||
val version: Int,
|
||||
val name: String,
|
||||
val typeSpec: String,
|
||||
val primary: Boolean = false,
|
||||
) : Comparable<ColumnMeta> {
|
||||
companion object {
|
||||
private val log = LogCategory("ColumnMeta")
|
||||
|
||||
const val TS_EMPTY = "text default ''"
|
||||
const val TS_ZERO = "integer default 0"
|
||||
const val TS_TRUE = "integer default 1"
|
||||
}
|
||||
|
||||
class List(val table: String) : ArrayList<ColumnMeta>() {
|
||||
fun createParams(): String =
|
||||
sorted().joinToString(",") { "${it.name} ${it.typeSpec}" }
|
||||
|
||||
fun addColumns(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
|
||||
for (column in this) {
|
||||
if (oldVersion < column.version && newVersion >= column.version) {
|
||||
val sql = "alter table $table add column ${column.name} ${column.typeSpec}"
|
||||
try {
|
||||
db.execSQL(sql)
|
||||
} catch (ex: Throwable) {
|
||||
log.trace(ex, "addColumns $table ${column.name} failed. $sql")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// テーブル作成時のソート
|
||||
override fun compareTo(other: ColumnMeta): Int {
|
||||
val ia = if (this.primary) 1 else 0
|
||||
val ib = if (other.primary) 1 else 0
|
||||
return ia.compareTo(ib).notZero() ?: name.compareTo(other.name)
|
||||
}
|
||||
|
||||
override fun toString(): String = name
|
||||
override fun hashCode(): Int = name.hashCode()
|
||||
|
||||
override fun equals(other: Any?): Boolean = when (other) {
|
||||
is ColumnMeta -> name == other.name
|
||||
else -> name == other
|
||||
}
|
||||
|
||||
init {
|
||||
list.add(this)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
fun putNullTo(cv: ContentValues) = cv.putNull(name)
|
||||
fun putTo(cv: ContentValues, v: Boolean?) = cv.put(name, v)
|
||||
fun putTo(cv: ContentValues, v: String?) = cv.put(name, v)
|
||||
fun putTo(cv: ContentValues, v: Byte?) = cv.put(name, v)
|
||||
fun putTo(cv: ContentValues, v: Short?) = cv.put(name, v)
|
||||
fun putTo(cv: ContentValues, v: Int?) = cv.put(name, v)
|
||||
fun putTo(cv: ContentValues, v: Long?) = cv.put(name, v)
|
||||
fun putTo(cv: ContentValues, v: Float?) = cv.put(name, v)
|
||||
fun putTo(cv: ContentValues, v: Double?) = cv.put(name, v)
|
||||
fun putTo(cv: ContentValues, v: ByteArray?) = cv.put(name, v)
|
||||
|
||||
fun getIndex(cursor: Cursor) = cursor.getColumnIndex(name)
|
||||
fun getLong(cursor: Cursor) = cursor.getLong(getIndex(cursor))
|
||||
}
|
||||
|
||||
fun ContentValues.putNull(key: ColumnMeta) = putNull(key.name)
|
||||
fun ContentValues.put(key: ColumnMeta, v: Boolean?) = put(key.name, v)
|
||||
fun ContentValues.put(key: ColumnMeta, v: String?) = put(key.name, v)
|
||||
fun ContentValues.put(key: ColumnMeta, v: Byte?) = put(key.name, v)
|
||||
fun ContentValues.put(key: ColumnMeta, v: Short?) = put(key.name, v)
|
||||
fun ContentValues.put(key: ColumnMeta, v: Int?) = put(key.name, v)
|
||||
fun ContentValues.put(key: ColumnMeta, v: Long?) = put(key.name, v)
|
||||
fun ContentValues.put(key: ColumnMeta, v: Float?) = put(key.name, v)
|
||||
fun ContentValues.put(key: ColumnMeta, v: Double?) = put(key.name, v)
|
||||
fun ContentValues.put(key: ColumnMeta, v: ByteArray?) = put(key.name, v)
|
||||
|
||||
fun Cursor.getInt(key: ColumnMeta) = getInt(getColumnIndex(key.name))
|
||||
|
||||
fun Cursor.getBoolean(key: ColumnMeta) = getBoolean(getColumnIndex(key.name))
|
||||
fun Cursor.getLong(key: ColumnMeta) = getLong(getColumnIndex(key.name))
|
||||
|
||||
@Suppress("unused")
|
||||
fun Cursor.getIntOrNull(key: ColumnMeta) = getIntOrNull(getColumnIndex(key.name))
|
||||
fun Cursor.getString(key: ColumnMeta): String = getString(getColumnIndex(key.name))
|
||||
fun Cursor.getStringOrNull(key: ColumnMeta): String? {
|
||||
val idx = key.getIndex(this)
|
||||
return if (isNull(idx)) null else getString(idx)
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
package jp.juggler.util
|
||||
|
||||
import android.database.Cursor
|
||||
|
||||
fun Cursor.getInt(key: String) =
|
||||
getInt(getColumnIndex(key))
|
||||
|
||||
fun Cursor.getIntOrNull(idx: Int) =
|
||||
if (isNull(idx)) null else getInt(idx)
|
||||
|
||||
fun Cursor.getIntOrNull(key: String) =
|
||||
getIntOrNull(getColumnIndex(key))
|
||||
|
||||
fun Cursor.getLong(key: String) =
|
||||
getLong(getColumnIndex(key))
|
||||
|
||||
//fun Cursor.getLongOrNull(idx:Int) =
|
||||
// if(isNull(idx)) null else getLong(idx)
|
||||
|
||||
//fun Cursor.getLongOrNull(key:String) =
|
||||
// getLongOrNull(getColumnIndex(key))
|
||||
|
||||
fun Cursor.getString(key: String): String =
|
||||
getString(getColumnIndex(key))
|
||||
|
||||
fun Cursor.getStringOrNull(keyIdx: Int) =
|
||||
if (isNull(keyIdx)) null else getString(keyIdx)
|
||||
|
||||
fun Cursor.getStringOrNull(key: String) =
|
||||
getStringOrNull(getColumnIndex(key))
|
Loading…
Reference in New Issue