241 lines
6.6 KiB
Kotlin
241 lines
6.6 KiB
Kotlin
package jp.juggler.subwaytooter.table
|
|
|
|
import android.content.ContentValues
|
|
import android.content.Context
|
|
import android.database.sqlite.SQLiteDatabase
|
|
|
|
import jp.juggler.subwaytooter.App1
|
|
import jp.juggler.subwaytooter.util.LogCategory
|
|
import android.support.v4.util.LruCache
|
|
import android.text.Spannable
|
|
import android.text.SpannableStringBuilder
|
|
import android.text.style.BackgroundColorSpan
|
|
import android.text.style.ForegroundColorSpan
|
|
import jp.juggler.subwaytooter.util.getIntOrNull
|
|
import jp.juggler.subwaytooter.util.getStringOrNull
|
|
import jp.juggler.subwaytooter.util.sanitizeBDI
|
|
|
|
import java.util.Locale
|
|
|
|
|
|
class AcctColor {
|
|
|
|
var acct : String
|
|
var color_fg : Int = 0
|
|
var color_bg : Int = 0
|
|
var nickname : String? = null
|
|
var notification_sound : String? = null
|
|
|
|
constructor(
|
|
acct : String,
|
|
nickname : String,
|
|
color_fg : Int,
|
|
color_bg : Int,
|
|
notification_sound : String?
|
|
) {
|
|
this.acct = acct
|
|
this.nickname = nickname
|
|
this.color_fg = color_fg
|
|
this.color_bg = color_bg
|
|
this.notification_sound = notification_sound
|
|
}
|
|
|
|
private constructor(acct : String) {
|
|
this.acct = acct
|
|
}
|
|
|
|
fun save(now : Long) {
|
|
|
|
acct = acct.toLowerCase(Locale.ENGLISH)
|
|
|
|
try {
|
|
val cv = ContentValues()
|
|
cv.put(COL_TIME_SAVE, now)
|
|
cv.put(COL_ACCT, acct)
|
|
cv.put(COL_COLOR_FG, color_fg)
|
|
cv.put(COL_COLOR_BG, color_bg)
|
|
cv.put(COL_NICKNAME, if(nickname == null) "" else nickname)
|
|
cv.put(
|
|
COL_NOTIFICATION_SOUND,
|
|
if(notification_sound == null) "" else notification_sound
|
|
)
|
|
App1.database.replace(table, null, cv)
|
|
mMemoryCache.remove(acct)
|
|
} catch(ex : Throwable) {
|
|
log.trace(ex)
|
|
log.e(ex, "save failed.")
|
|
}
|
|
|
|
}
|
|
|
|
companion object :TableCompanion {
|
|
|
|
private val log = LogCategory("AcctColor")
|
|
|
|
const val table = "acct_color"
|
|
private const val COL_TIME_SAVE = "time_save"
|
|
private const val COL_ACCT = "ac" //@who@host ascii文字の大文字小文字は(sqliteにより)同一視される
|
|
private const val COL_COLOR_FG = "cf" // 未設定なら0、それ以外は色
|
|
private const val COL_COLOR_BG = "cb" // 未設定なら0、それ以外は色
|
|
private const val COL_NICKNAME = "nick" // 未設定ならnullか空文字列
|
|
private const val COL_NOTIFICATION_SOUND = "notification_sound" // 未設定ならnullか空文字列
|
|
|
|
private const val CHAR_REPLACE : Char = 0x328A.toChar()
|
|
|
|
private const val load_where = "$COL_ACCT=?"
|
|
|
|
private val load_where_arg = object : ThreadLocal<Array<String?>>() {
|
|
override fun initialValue() : Array<String?> {
|
|
return arrayOfNulls(1)
|
|
}
|
|
}
|
|
|
|
private val mMemoryCache = LruCache<String, AcctColor>(2048)
|
|
|
|
override fun onDBCreate(db : SQLiteDatabase) {
|
|
log.d("onDBCreate!")
|
|
db.execSQL(
|
|
"create table if not exists " + table
|
|
+ "(_id INTEGER PRIMARY KEY"
|
|
+ "," + COL_TIME_SAVE + " integer not null"
|
|
+ "," + COL_ACCT + " text not null"
|
|
+ "," + COL_COLOR_FG + " integer"
|
|
+ "," + COL_COLOR_BG + " integer"
|
|
+ "," + COL_NICKNAME + " text "
|
|
+ "," + COL_NOTIFICATION_SOUND + " text default ''"
|
|
+ ")"
|
|
)
|
|
db.execSQL(
|
|
"create unique index if not exists " + table + "_acct on " + table + "(" + COL_ACCT + ")"
|
|
)
|
|
db.execSQL(
|
|
"create index if not exists " + table + "_time on " + table + "(" + COL_TIME_SAVE + ")"
|
|
)
|
|
}
|
|
|
|
override fun onDBUpgrade(db : SQLiteDatabase, oldVersion : Int, newVersion : Int) {
|
|
if(oldVersion < 9 && newVersion >= 9) {
|
|
onDBCreate(db)
|
|
return
|
|
}
|
|
|
|
if(oldVersion < 17 && newVersion >= 17) {
|
|
try {
|
|
db.execSQL("alter table $table add column $COL_NOTIFICATION_SOUND text default ''")
|
|
} catch(ex : Throwable) {
|
|
log.trace(ex)
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
fun load(acctArg : String) : AcctColor {
|
|
val acct = acctArg.toLowerCase(Locale.ENGLISH)
|
|
val cached : AcctColor? = mMemoryCache.get(acct)
|
|
if(cached != null) return cached
|
|
|
|
try {
|
|
val where_arg = load_where_arg.get() ?: arrayOfNulls<String?>(1)
|
|
where_arg[0] = acct
|
|
App1.database.query(table, null, load_where, where_arg, null, null, null)
|
|
.use { cursor ->
|
|
if(cursor.moveToNext()) {
|
|
|
|
val ac = AcctColor(acct)
|
|
|
|
ac.color_fg = cursor.getIntOrNull(COL_COLOR_FG) ?: 0
|
|
ac.color_bg = cursor.getIntOrNull(COL_COLOR_BG) ?: 0
|
|
ac.nickname = cursor.getStringOrNull(COL_NICKNAME)
|
|
ac.notification_sound = cursor.getStringOrNull(COL_NOTIFICATION_SOUND)
|
|
|
|
mMemoryCache.put(acct, ac)
|
|
return ac
|
|
}
|
|
|
|
}
|
|
} catch(ex : Throwable) {
|
|
log.trace(ex)
|
|
log.e(ex, "load failed.")
|
|
}
|
|
|
|
log.d(
|
|
"lruCache size=%s,hit=%s,miss=%s",
|
|
mMemoryCache.size(),
|
|
mMemoryCache.hitCount(),
|
|
mMemoryCache.missCount()
|
|
)
|
|
val ac = AcctColor(acct)
|
|
mMemoryCache.put(acct, ac)
|
|
return ac
|
|
}
|
|
|
|
fun getNickname(acct : String) : String {
|
|
val ac = load(acct)
|
|
val nickname = ac.nickname
|
|
return if(nickname != null && nickname.isNotEmpty()) nickname.sanitizeBDI() else acct
|
|
}
|
|
|
|
fun getNotificationSound(acct : String) : String? {
|
|
val ac = load(acct)
|
|
val notification_sound = ac.notification_sound
|
|
return if(notification_sound != null && notification_sound.isNotEmpty()) notification_sound else null
|
|
}
|
|
|
|
fun hasNickname(ac : AcctColor?) : Boolean {
|
|
val nickname = ac?.nickname
|
|
return nickname != null && nickname.isNotEmpty()
|
|
}
|
|
|
|
fun hasColorForeground(ac : AcctColor?) : Boolean {
|
|
return ac != null && ac.color_fg != 0
|
|
}
|
|
|
|
fun hasColorBackground(ac : AcctColor?) : Boolean {
|
|
return ac != null && ac.color_bg != 0
|
|
}
|
|
|
|
fun clearMemoryCache() {
|
|
mMemoryCache.evictAll()
|
|
}
|
|
|
|
fun getStringWithNickname(
|
|
context : Context,
|
|
string_id : Int,
|
|
acct : String
|
|
) : CharSequence {
|
|
val ac = load(acct)
|
|
val nickname = ac.nickname
|
|
val name = if(nickname == null || nickname.isEmpty()) acct else nickname.sanitizeBDI()
|
|
val sb = SpannableStringBuilder(
|
|
context.getString(
|
|
string_id,
|
|
String(charArrayOf(CHAR_REPLACE))
|
|
)
|
|
)
|
|
for(i in sb.length - 1 downTo 0) {
|
|
val c = sb[i]
|
|
if(c != CHAR_REPLACE) continue
|
|
sb.replace(i, i + 1, name)
|
|
if(ac.color_fg != 0) {
|
|
sb.setSpan(
|
|
ForegroundColorSpan(ac.color_fg),
|
|
i,
|
|
i + name.length,
|
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
)
|
|
}
|
|
if(ac.color_bg != 0) {
|
|
sb.setSpan(
|
|
BackgroundColorSpan(ac.color_bg),
|
|
i,
|
|
i + name.length,
|
|
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
)
|
|
}
|
|
}
|
|
return sb
|
|
}
|
|
}
|
|
|
|
}
|