2018-01-04 19:52:25 +01:00
|
|
|
package jp.juggler.subwaytooter.table
|
|
|
|
|
|
|
|
import android.content.ContentValues
|
|
|
|
import android.database.sqlite.SQLiteDatabase
|
|
|
|
import android.provider.BaseColumns
|
|
|
|
import jp.juggler.subwaytooter.App1
|
2018-08-30 08:51:32 +02:00
|
|
|
import jp.juggler.subwaytooter.api.entity.EntityId
|
|
|
|
import jp.juggler.subwaytooter.api.entity.putMayNull
|
2018-12-01 00:02:18 +01:00
|
|
|
import jp.juggler.util.LogCategory
|
|
|
|
import jp.juggler.util.getLong
|
2020-12-11 00:25:55 +01:00
|
|
|
import jp.juggler.util.minComparable
|
2020-12-11 18:30:10 +01:00
|
|
|
import java.util.concurrent.ConcurrentHashMap
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
class NotificationTracking {
|
2020-12-11 18:30:10 +01:00
|
|
|
|
|
|
|
private var dirty = false
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
private var id = - 1L
|
2020-12-11 18:30:10 +01:00
|
|
|
set(value){ dirty=true; field=value}
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
private var account_db_id : Long = 0
|
2020-12-11 18:30:10 +01:00
|
|
|
set(value){ dirty=true; field=value}
|
|
|
|
|
2019-10-22 20:21:03 +02:00
|
|
|
private var notificationType: String =""
|
2020-12-11 18:30:10 +01:00
|
|
|
set(value){ dirty=true; field=value}
|
2019-10-22 20:21:03 +02:00
|
|
|
|
2018-08-30 08:51:32 +02:00
|
|
|
var nid_read : EntityId? = null
|
2020-12-11 18:30:10 +01:00
|
|
|
set(value){ dirty=true; field=value}
|
|
|
|
|
2018-08-30 08:51:32 +02:00
|
|
|
var nid_show : EntityId? = null
|
2020-12-11 18:30:10 +01:00
|
|
|
set(value){ dirty=true; field=value}
|
|
|
|
|
2018-08-30 08:51:32 +02:00
|
|
|
var post_id : EntityId? = null
|
2020-12-11 18:30:10 +01:00
|
|
|
set(value){ dirty=true; field=value}
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
var post_time : Long = 0
|
2020-12-11 18:30:10 +01:00
|
|
|
set(value){ dirty=true; field=value}
|
|
|
|
|
2020-12-11 00:25:55 +01:00
|
|
|
fun save(acct:String) {
|
2019-10-22 20:21:03 +02:00
|
|
|
|
2020-12-11 18:30:10 +01:00
|
|
|
if(dirty){
|
|
|
|
try {
|
|
|
|
val cv = ContentValues()
|
|
|
|
cv.put(COL_ACCOUNT_DB_ID, account_db_id)
|
|
|
|
cv.put(COL_NOTIFICATION_TYPE,notificationType)
|
|
|
|
nid_read.putMayNull(cv, COL_NID_READ)
|
|
|
|
nid_show.putMayNull(cv, COL_NID_SHOW)
|
|
|
|
post_id.putMayNull(cv, COL_POST_ID)
|
|
|
|
cv.put(COL_POST_TIME, post_time)
|
|
|
|
|
|
|
|
val rv = App1.database.replaceOrThrow(table,null,cv)
|
|
|
|
if( rv != -1L && id == -1L) id = rv
|
2020-12-11 00:25:55 +01:00
|
|
|
|
2020-12-11 18:30:10 +01:00
|
|
|
log.d( "${acct}/${notificationType} save. post=(${post_id},${post_time})" )
|
|
|
|
dirty=false
|
|
|
|
clearCache(account_db_id,notificationType)
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.e(ex, "save failed.")
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-30 08:51:32 +02:00
|
|
|
fun updatePost(post_id : EntityId, post_time : Long) {
|
2018-01-04 19:52:25 +01:00
|
|
|
this.post_id = post_id
|
|
|
|
this.post_time = post_time
|
2020-12-11 18:30:10 +01:00
|
|
|
if(dirty) {
|
|
|
|
try {
|
|
|
|
val cv = ContentValues()
|
|
|
|
post_id.putTo(cv, COL_POST_ID)
|
|
|
|
cv.put(COL_POST_TIME, post_time)
|
|
|
|
val rows = App1.database.update(table, cv, WHERE_AID, arrayOf(account_db_id.toString(),notificationType))
|
2021-06-13 13:48:48 +02:00
|
|
|
log.d("updatePost account_db_id=${account_db_id}, nt=${notificationType}, post=${post_id},${post_time} update_rows=${rows}")
|
2020-12-11 18:30:10 +01:00
|
|
|
dirty=false
|
|
|
|
clearCache(account_db_id,notificationType)
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.e(ex, "updatePost failed.")
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-30 08:51:32 +02:00
|
|
|
companion object : TableCompanion {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
private val log = LogCategory("NotificationTracking")
|
|
|
|
|
|
|
|
private const val table = "noti_trac"
|
|
|
|
|
|
|
|
private const val COL_ID = BaseColumns._ID
|
|
|
|
|
|
|
|
// アカウントDBの行ID。 サーバ側のIDではない
|
|
|
|
private const val COL_ACCOUNT_DB_ID = "a"
|
|
|
|
|
|
|
|
// 通知ID。ここまで既読
|
|
|
|
private const val COL_NID_READ = "nr"
|
|
|
|
|
|
|
|
// 通知ID。もっとも最近取得したもの
|
|
|
|
private const val COL_NID_SHOW = "ns"
|
|
|
|
|
|
|
|
// 最後に表示した通知のID
|
|
|
|
private const val COL_POST_ID = "pi"
|
|
|
|
|
|
|
|
// 最後に表示した通知の作成時刻
|
|
|
|
private const val COL_POST_TIME = "pt"
|
|
|
|
|
2019-10-22 20:21:03 +02:00
|
|
|
// 返信だけ通知グループを分ける
|
|
|
|
private const val COL_NOTIFICATION_TYPE ="nt"
|
|
|
|
|
2018-03-16 15:13:34 +01:00
|
|
|
override fun onDBCreate(db : SQLiteDatabase) {
|
2018-01-04 19:52:25 +01:00
|
|
|
|
|
|
|
db.execSQL(
|
2018-08-30 08:51:32 +02:00
|
|
|
"""
|
|
|
|
create table if not exists $table
|
|
|
|
($COL_ID INTEGER PRIMARY KEY
|
|
|
|
,$COL_ACCOUNT_DB_ID integer not null
|
|
|
|
,$COL_NID_READ text
|
|
|
|
,$COL_NID_SHOW text
|
|
|
|
,$COL_POST_ID text
|
|
|
|
,$COL_POST_TIME integer default 0
|
2019-10-22 20:21:03 +02:00
|
|
|
,$COL_NOTIFICATION_TYPE text default ''
|
2018-08-30 08:51:32 +02:00
|
|
|
)
|
|
|
|
"""
|
2018-01-04 19:52:25 +01:00
|
|
|
)
|
|
|
|
db.execSQL(
|
2019-10-22 20:21:03 +02:00
|
|
|
"create unique index if not exists ${table}_b on $table ($COL_ACCOUNT_DB_ID,$COL_NOTIFICATION_TYPE)"
|
2018-01-04 19:52:25 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2018-03-16 15:13:34 +01:00
|
|
|
override fun onDBUpgrade(db : SQLiteDatabase, oldVersion : Int, newVersion : Int) {
|
2019-10-22 20:21:03 +02:00
|
|
|
|
|
|
|
if( newVersion < oldVersion ) {
|
2018-08-30 08:51:32 +02:00
|
|
|
try {
|
|
|
|
db.execSQL("drop table if exists $table")
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex, "delete DB failed.")
|
|
|
|
}
|
|
|
|
onDBCreate(db)
|
2019-10-22 20:21:03 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if(oldVersion < 2 && newVersion >= 2) {
|
|
|
|
onDBCreate(db)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if(oldVersion < 40 && newVersion >= 40) {
|
|
|
|
try {
|
|
|
|
db.execSQL("drop index if exists ${table}_a")
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
db.execSQL("alter table $table add column $COL_NOTIFICATION_TYPE text default ''")
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
db.execSQL(
|
|
|
|
"create unique index if not exists ${table}_b on $table ($COL_ACCOUNT_DB_ID,$COL_NOTIFICATION_TYPE)"
|
|
|
|
)
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.trace(ex)
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
2020-12-11 18:30:10 +01:00
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
private val cache = ConcurrentHashMap<Long,ConcurrentHashMap<String,NotificationTracking>>()
|
|
|
|
|
|
|
|
private fun<K:Any,V:Any> ConcurrentHashMap<K,V>.getOrCreate(key:K, creator:()->V):V{
|
|
|
|
var v = this[key]
|
|
|
|
if(v==null) v= creator().also{ this[key]=it}
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun loadCache(account_db_id : Long,notificationType:String):NotificationTracking? =
|
|
|
|
cache[account_db_id]?.get(notificationType)
|
|
|
|
|
|
|
|
private fun clearCache(account_db_id : Long,notificationType:String):NotificationTracking? =
|
|
|
|
cache[account_db_id]?.remove(notificationType)
|
|
|
|
|
|
|
|
private fun saveCache(account_db_id : Long,notificationType:String,nt: NotificationTracking){
|
|
|
|
cache.getOrCreate(account_db_id){
|
|
|
|
ConcurrentHashMap<String,NotificationTracking>()
|
|
|
|
}[notificationType] = nt
|
|
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
2019-10-22 20:21:03 +02:00
|
|
|
private const val WHERE_AID = "$COL_ACCOUNT_DB_ID=? and $COL_NOTIFICATION_TYPE=?"
|
2018-01-04 19:52:25 +01:00
|
|
|
|
2020-12-11 00:25:55 +01:00
|
|
|
fun load(acct:String, account_db_id : Long,notificationType:String) : NotificationTracking {
|
2020-12-11 18:30:10 +01:00
|
|
|
loadCache(account_db_id,notificationType)?.let{ dst->
|
|
|
|
if(!dst.dirty){
|
|
|
|
log.d(
|
|
|
|
"${acct}/${notificationType} load-cached. post=(${dst.post_id},${dst.post_time}), read=${dst.nid_read}, show=${dst.nid_show}"
|
|
|
|
)
|
|
|
|
return dst
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-04 19:52:25 +01:00
|
|
|
val dst = NotificationTracking()
|
|
|
|
dst.account_db_id = account_db_id
|
2019-10-22 20:21:03 +02:00
|
|
|
dst.notificationType = notificationType
|
2018-01-04 19:52:25 +01:00
|
|
|
try {
|
2018-08-30 08:51:32 +02:00
|
|
|
App1.database.query(
|
|
|
|
table,
|
|
|
|
null,
|
|
|
|
WHERE_AID,
|
2019-10-22 20:21:03 +02:00
|
|
|
arrayOf(account_db_id.toString(),notificationType),
|
2018-08-30 08:51:32 +02:00
|
|
|
null,
|
|
|
|
null,
|
|
|
|
null
|
|
|
|
)?.use { cursor ->
|
|
|
|
if(cursor.moveToFirst()) {
|
2018-10-30 20:29:00 +01:00
|
|
|
dst.id = cursor.getLong(COL_ID)
|
2020-12-11 00:25:55 +01:00
|
|
|
|
2018-08-30 08:51:32 +02:00
|
|
|
dst.post_id = EntityId.from(cursor, COL_POST_ID)
|
2018-10-30 20:29:00 +01:00
|
|
|
dst.post_time = cursor.getLong(COL_POST_TIME)
|
2020-12-11 00:25:55 +01:00
|
|
|
|
|
|
|
val show = EntityId.from(cursor, COL_NID_SHOW)
|
|
|
|
if( show == null){
|
|
|
|
dst.nid_show = null
|
|
|
|
dst.nid_read = null
|
|
|
|
}else{
|
|
|
|
dst.nid_show = show
|
|
|
|
val read = EntityId.from(cursor, COL_NID_READ)
|
|
|
|
if( read==null){
|
|
|
|
dst.nid_read = null
|
|
|
|
}else{
|
|
|
|
val r2 = minComparable(show,read)
|
|
|
|
dst.nid_read = r2
|
|
|
|
if(r2 != read){
|
2021-05-27 04:15:59 +02:00
|
|
|
log.i("${acct}/${notificationType} read>show! clip to $show")
|
2020-12-11 00:25:55 +01:00
|
|
|
val cv = ContentValues()
|
|
|
|
show.putTo(cv, COL_NID_READ) //変数名とキー名が異なるのに注意
|
|
|
|
val where_args = arrayOf(account_db_id.toString(),notificationType)
|
|
|
|
App1.database.update(table, cv, WHERE_AID, where_args)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-30 08:51:32 +02:00
|
|
|
log.d(
|
2020-12-11 00:25:55 +01:00
|
|
|
"${acct}/${notificationType} load. post=(${dst.post_id},${dst.post_time}), read=${dst.nid_read}, show=${dst.nid_show}"
|
2018-08-30 08:51:32 +02:00
|
|
|
)
|
2020-12-11 18:30:10 +01:00
|
|
|
saveCache(account_db_id ,notificationType,dst)
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
2018-08-30 08:51:32 +02:00
|
|
|
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
} catch(ex : Throwable) {
|
2018-08-30 08:51:32 +02:00
|
|
|
log.trace(ex, "load failed.")
|
2020-12-11 18:30:10 +01:00
|
|
|
}finally{
|
|
|
|
dst.dirty = false
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return dst
|
|
|
|
}
|
|
|
|
|
2019-10-22 20:21:03 +02:00
|
|
|
fun updateRead(account_db_id : Long,notificationType:String) {
|
2018-01-04 19:52:25 +01:00
|
|
|
try {
|
2019-10-22 20:21:03 +02:00
|
|
|
val where_args = arrayOf(account_db_id.toString(),notificationType)
|
2018-08-30 08:51:32 +02:00
|
|
|
App1.database.query(
|
|
|
|
table,
|
|
|
|
arrayOf(COL_NID_SHOW, COL_NID_READ),
|
|
|
|
WHERE_AID,
|
|
|
|
where_args,
|
|
|
|
null,
|
|
|
|
null,
|
|
|
|
null
|
|
|
|
)?.use { cursor ->
|
|
|
|
when {
|
2019-10-22 20:21:03 +02:00
|
|
|
! cursor.moveToFirst() -> log.e("updateRead[$account_db_id,$notificationType]: can't find the data row.")
|
2018-08-30 08:51:32 +02:00
|
|
|
|
|
|
|
else -> {
|
|
|
|
val nid_show = EntityId.from(cursor, COL_NID_SHOW)
|
|
|
|
val nid_read = EntityId.from(cursor, COL_NID_READ)
|
|
|
|
when {
|
|
|
|
nid_show == null ->
|
2020-12-11 00:25:55 +01:00
|
|
|
log.e("updateRead[$account_db_id,$notificationType]: nid_show is null.")
|
2019-10-22 20:21:03 +02:00
|
|
|
|
2018-08-30 08:51:32 +02:00
|
|
|
nid_read != null && nid_read >= nid_show ->
|
2020-12-11 00:25:55 +01:00
|
|
|
log.e("updateRead[$account_db_id,$notificationType]: nid_read already updated.")
|
2018-08-30 08:51:32 +02:00
|
|
|
|
|
|
|
else -> {
|
2021-05-27 04:15:59 +02:00
|
|
|
log.i("updateRead[$account_db_id,$notificationType]: update nid_read as $nid_show...")
|
2018-08-30 08:51:32 +02:00
|
|
|
val cv = ContentValues()
|
|
|
|
nid_show.putTo(cv, COL_NID_READ) //変数名とキー名が異なるのに注意
|
|
|
|
App1.database.update(table, cv, WHERE_AID, where_args)
|
2020-12-11 18:30:10 +01:00
|
|
|
clearCache(account_db_id,notificationType)
|
2018-08-30 08:51:32 +02:00
|
|
|
}
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
2018-08-30 08:51:32 +02:00
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
} catch(ex : Throwable) {
|
2018-08-30 08:51:32 +02:00
|
|
|
log.e(ex, "updateRead[${account_db_id}] failed.")
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fun resetPostAll() {
|
|
|
|
try {
|
|
|
|
val cv = ContentValues()
|
2018-08-30 08:51:32 +02:00
|
|
|
cv.putNull(COL_POST_ID)
|
2018-01-04 19:52:25 +01:00
|
|
|
cv.put(COL_POST_TIME, 0)
|
|
|
|
App1.database.update(table, cv, null, null)
|
2020-12-11 18:30:10 +01:00
|
|
|
cache.clear()
|
2018-01-04 19:52:25 +01:00
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.e(ex, "resetPostAll failed.")
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2020-12-11 18:30:10 +01:00
|
|
|
|
|
|
|
// アカウント設定から手動で呼ばれる
|
|
|
|
fun resetTrackingState( account_db_id: Long?) {
|
|
|
|
account_db_id ?: return
|
|
|
|
try {
|
|
|
|
App1.database.delete(table, "$COL_ACCOUNT_DB_ID=?",arrayOf(account_db_id.toString()))
|
|
|
|
cache.remove( account_db_id)
|
|
|
|
} catch(ex : Throwable) {
|
|
|
|
log.e(ex, "resetTrackingState failed.")
|
|
|
|
}
|
|
|
|
}
|
2018-01-04 19:52:25 +01:00
|
|
|
}
|
|
|
|
}
|