アプリ設定に「見た目/Instance tickerを表示する」を追加.
This commit is contained in:
parent
1594391db3
commit
52fe206292
|
@ -483,7 +483,7 @@ class App1 : Application() {
|
|||
val response : Response
|
||||
|
||||
try {
|
||||
val request_builder = okhttp3.Request.Builder()
|
||||
val request_builder = Request.Builder()
|
||||
request_builder.url(url)
|
||||
request_builder.cacheControl(CACHE_5MIN)
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import jp.juggler.subwaytooter.api.*
|
|||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.table.*
|
||||
import jp.juggler.subwaytooter.util.BucketList
|
||||
import jp.juggler.subwaytooter.util.InstanceTicker
|
||||
import jp.juggler.subwaytooter.util.ScrollPosition
|
||||
import jp.juggler.subwaytooter.util.WordTrieTree
|
||||
import jp.juggler.util.*
|
||||
|
@ -222,6 +223,8 @@ class Column(
|
|||
internal const val TAB_FOLLOWING = 1
|
||||
internal const val TAB_FOLLOWERS = 2
|
||||
|
||||
internal var useInstanceTicker = false
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private inline fun <reified T> getParamAt(params : Array<out Any>, idx : Int) : T {
|
||||
return params[idx] as T
|
||||
|
@ -1892,7 +1895,9 @@ class Column(
|
|||
stopStreaming()
|
||||
|
||||
initFilter()
|
||||
|
||||
|
||||
useInstanceTicker = Pref.bpInstanceTicker( app_state.pref)
|
||||
|
||||
mRefreshLoadingErrorPopupState = 0
|
||||
mRefreshLoadingError = ""
|
||||
mInitialLoadingError = ""
|
||||
|
@ -2580,6 +2585,11 @@ class Column(
|
|||
override fun doInBackground(vararg unused : Void) : TootApiResult? {
|
||||
ctStarted.set(true)
|
||||
|
||||
if( Pref.bpInstanceTicker(app_state.pref)){
|
||||
InstanceTicker.load()
|
||||
}
|
||||
|
||||
|
||||
val client = TootApiClient(context, callback = object : TootApiCallback {
|
||||
override val isApiCancelled : Boolean
|
||||
get() = isCancelled || is_dispose.get()
|
||||
|
|
|
@ -2,6 +2,7 @@ package jp.juggler.subwaytooter
|
|||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.SystemClock
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v4.view.ViewCompat
|
||||
|
@ -11,6 +12,7 @@ import android.text.Spannable
|
|||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.TextUtils
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
|
@ -137,6 +139,11 @@ internal class ItemViewHolder(
|
|||
private lateinit var tvApplication : TextView
|
||||
|
||||
private lateinit var tvMessageHolder : TextView
|
||||
|
||||
private lateinit var llInstanceTicker:View
|
||||
private lateinit var ivInstanceTicker:MyNetworkImageView
|
||||
private lateinit var tvInstanceTicker:TextView
|
||||
|
||||
private lateinit var access_info : SavedAccount
|
||||
|
||||
private var buttons_for_status : StatusButtons? = null
|
||||
|
@ -378,6 +385,7 @@ internal class ItemViewHolder(
|
|||
this.viewRoot.setBackgroundColor(0)
|
||||
this.boostedAction = defaultBoostedAction
|
||||
|
||||
llInstanceTicker.visibility = View.GONE
|
||||
llBoosted.visibility = View.GONE
|
||||
llReply.visibility = View.GONE
|
||||
llFollow.visibility = View.GONE
|
||||
|
@ -1035,6 +1043,42 @@ internal class ItemViewHolder(
|
|||
)
|
||||
// }
|
||||
|
||||
if( Column.useInstanceTicker){
|
||||
try {
|
||||
val item = InstanceTicker.lastList[who.host]
|
||||
if( item != null) {
|
||||
tvInstanceTicker.text = item.name
|
||||
tvInstanceTicker.textColor = item.colorText
|
||||
val density = llInstanceTicker.resources.displayMetrics.density
|
||||
val lp = ivInstanceTicker.layoutParams
|
||||
lp.height = (density*16f+0.5f).toInt()
|
||||
lp.width = (density*item.imageWidth+0.5f).toInt()
|
||||
ivInstanceTicker.layoutParams = lp
|
||||
ivInstanceTicker.setImageUrl(activity.pref, 0f, item.image)
|
||||
val colorBg = item.colorBg
|
||||
when {
|
||||
colorBg.isEmpty() ->{
|
||||
tvInstanceTicker.background = null
|
||||
ivInstanceTicker.background = null
|
||||
}
|
||||
colorBg.size == 1 -> {
|
||||
tvInstanceTicker.setBackgroundColor(colorBg.first())
|
||||
ivInstanceTicker.setBackgroundColor(colorBg.first())
|
||||
}
|
||||
else -> {
|
||||
ivInstanceTicker.setBackgroundColor(colorBg.last())
|
||||
tvInstanceTicker.background = colorBg.getGradation()
|
||||
|
||||
}
|
||||
}
|
||||
llInstanceTicker.visibility = View.VISIBLE
|
||||
llInstanceTicker.requestLayout()
|
||||
}
|
||||
}catch(ex:Throwable){
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
var content = status.decoded_content
|
||||
|
||||
// ニコフレのアンケートの表示
|
||||
|
@ -1863,13 +1907,7 @@ internal class ItemViewHolder(
|
|||
|
||||
}
|
||||
|
||||
private fun ellipsize(src : String, limit : Int) : String {
|
||||
return if(src.codePointCount(0, src.length) <= limit) {
|
||||
src
|
||||
} else {
|
||||
"${src.substring(0, src.offsetByCodePoints(0, limit))}…"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun addLinkAndCaption(
|
||||
sb : StringBuilder,
|
||||
|
@ -2398,6 +2436,24 @@ internal class ItemViewHolder(
|
|||
tvName = textView {
|
||||
}.lparams(matchParent, wrapContent)
|
||||
|
||||
llInstanceTicker = linearLayout{
|
||||
lparams(matchParent, wrapContent)
|
||||
|
||||
ivInstanceTicker = myNetworkImageView{
|
||||
}.lparams(dip(16), dip(16)){
|
||||
isBaselineAligned = false
|
||||
}
|
||||
|
||||
tvInstanceTicker = textView{
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_DIP,10f)
|
||||
gravity=Gravity.CENTER_VERTICAL
|
||||
setPaddingStartEnd( dip(4f), dip(4f) )
|
||||
}.lparams(0,dip(16) ){
|
||||
isBaselineAligned = false
|
||||
weight=1f
|
||||
}
|
||||
}
|
||||
|
||||
llReply = linearLayout {
|
||||
lparams(matchParent, wrapContent) {
|
||||
bottomMargin = dip(3)
|
||||
|
|
|
@ -130,6 +130,7 @@ object Pref {
|
|||
return PreferenceManager.getDefaultSharedPreferences(context)
|
||||
}
|
||||
|
||||
|
||||
// キー名と設定項目のマップ。インポートやアプリ設定で使う
|
||||
val map = HashMap<String, BasePref<*>>()
|
||||
|
||||
|
@ -340,6 +341,12 @@ object Pref {
|
|||
R.id.swScrollTopFromColumnStrip
|
||||
)
|
||||
|
||||
val bpInstanceTicker = BooleanPref(
|
||||
"InstanceTicker",
|
||||
false,
|
||||
R.id.swInstanceTicker
|
||||
)
|
||||
|
||||
|
||||
// int
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package jp.juggler.subwaytooter.api
|
||||
|
||||
import android.content.Context
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.api.entity.*
|
||||
import jp.juggler.subwaytooter.table.UserRelation
|
||||
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
package jp.juggler.subwaytooter.util
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.os.SystemClock
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.util.LogCategory
|
||||
import jp.juggler.util.ellipsize
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.regex.Pattern
|
||||
|
||||
object InstanceTicker {
|
||||
|
||||
private val log = LogCategory("InstanceTicker")
|
||||
|
||||
private fun parseHex(group : String) : Int = group.toInt(16)
|
||||
|
||||
private val reColor6 =
|
||||
Pattern.compile("""#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})""", Pattern.CASE_INSENSITIVE)
|
||||
|
||||
private val reColor3 =
|
||||
Pattern.compile("""#([0-9a-f])([0-9a-f])([0-9a-f])\b""", Pattern.CASE_INSENSITIVE)
|
||||
|
||||
private fun parseColor(v : String) : Int? {
|
||||
var m = reColor6.matcher(v)
|
||||
if(m.find()) {
|
||||
return Color.rgb(
|
||||
parseHex(m.group(1)),
|
||||
parseHex(m.group(2)),
|
||||
parseHex(m.group(3))
|
||||
)
|
||||
}
|
||||
//
|
||||
m = reColor3.matcher(v)
|
||||
if(m.find()) {
|
||||
return Color.rgb(
|
||||
parseHex(m.group(1)) * 0x11,
|
||||
parseHex(m.group(2)) * 0x11,
|
||||
parseHex(m.group(3)) * 0x11
|
||||
)
|
||||
}
|
||||
if(v.isNotEmpty()) log.e("parseColor: can't parse $v")
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun color(v : String) : Int =
|
||||
parseColor(v) ?: error("not a color: $v")
|
||||
|
||||
enum class Type(
|
||||
val num : Int,
|
||||
val imageUrl : String,
|
||||
val imageWidth : Int,
|
||||
val colorText : Int,
|
||||
val colorBg : IntArray
|
||||
) {
|
||||
|
||||
GnuSocial(
|
||||
0,
|
||||
"https://cdn.weep.me/img/gnus.png",
|
||||
16,
|
||||
color("#fff"),
|
||||
intArrayOf(color("#000"), color("#a23"))
|
||||
),
|
||||
|
||||
MastodonJapan(
|
||||
1,
|
||||
"https://cdn.weep.me/img/mstdn.png",
|
||||
16,
|
||||
color("#fff"),
|
||||
intArrayOf(color("#000"), color("#27c"))
|
||||
),
|
||||
|
||||
MastodonAbroad(
|
||||
2,
|
||||
"https://cdn.weep.me/img/mstdn.png",
|
||||
16,
|
||||
color("#fff"),
|
||||
intArrayOf(color("#000"), color("#49c"))
|
||||
),
|
||||
|
||||
Pleroma(
|
||||
3,
|
||||
"https://cdn.weep.me/img/plrm.png",
|
||||
16,
|
||||
color("#da5"),
|
||||
intArrayOf(color("#000"), color("#123"))
|
||||
),
|
||||
|
||||
Misskey(
|
||||
4,
|
||||
"https://cdn.weep.me/img/msky2.png",
|
||||
36,
|
||||
color("#fff"),
|
||||
intArrayOf(color("#000"), color("#29b"))
|
||||
),
|
||||
|
||||
PeerTube(
|
||||
5,
|
||||
"https://cdn.weep.me/img/peertube2.png",
|
||||
16,
|
||||
color("#000"),
|
||||
intArrayOf(color("#000"), color("#fff"), color("#fff"), color("#fff"))
|
||||
),
|
||||
|
||||
// ロシアの大手マイクロブログ
|
||||
Juick(
|
||||
6,
|
||||
"https://cdn.weep.me/img/juick2.png",
|
||||
16,
|
||||
color("#fff"),
|
||||
intArrayOf(color("#000"), color("#000"))
|
||||
)
|
||||
;
|
||||
|
||||
}
|
||||
|
||||
private fun findType(num : Int) : Type? = Type.values().find { it.num == num }
|
||||
|
||||
class ColorBg(val array : IntArray) {
|
||||
companion object {
|
||||
val map = HashMap<String, GradientDrawable>()
|
||||
}
|
||||
|
||||
val key : String = array.joinToString(",") { it.toString() }
|
||||
|
||||
val size = array.size
|
||||
|
||||
fun isEmpty() = size == 0
|
||||
fun first() = array.first()
|
||||
fun last() = array.last()
|
||||
|
||||
fun getGradation() : Drawable? {
|
||||
var v = map[key]
|
||||
return if(v != null) {
|
||||
v
|
||||
} else {
|
||||
v = GradientDrawable(GradientDrawable.Orientation.RIGHT_LEFT, array)
|
||||
map[key] = v
|
||||
v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Item(type : Type, cols : List<String>) {
|
||||
val type : Type
|
||||
//[1]インスタンス名(ドメイン) (空文字列かもしれない)
|
||||
val name : String
|
||||
// [2]ドメイン
|
||||
val instance : String
|
||||
// [3]独自文字色
|
||||
val colorText : Int
|
||||
// [4]独自背景色
|
||||
val colorBg : ColorBg
|
||||
// [5]独自画像
|
||||
val image : String
|
||||
// [6]画像の横幅
|
||||
val imageWidth : Int
|
||||
|
||||
init {
|
||||
this.type = type
|
||||
this.instance = cols[2]
|
||||
|
||||
if(cols[5].isEmpty()) {
|
||||
// typeのデフォルト画像
|
||||
this.image = type.imageUrl
|
||||
this.imageWidth = type.imageWidth
|
||||
} else {
|
||||
// 独自画像
|
||||
this.image = "https://cdn.weep.me/img/${cols[5]}"
|
||||
// 画像の横幅は省略されてるかもしれない
|
||||
this.imageWidth = try {
|
||||
cols[6].toInt()
|
||||
} catch(ex : Throwable) {
|
||||
18 // デフォルト値
|
||||
}
|
||||
}
|
||||
|
||||
if(cols[1] == "閉鎖") {
|
||||
this.name = "閉鎖済み"
|
||||
this.colorText = Color.WHITE
|
||||
this.colorBg = ColorBg(intArrayOf(color("#666")))
|
||||
} else {
|
||||
this.name = ellipsize(
|
||||
when {
|
||||
imageWidth >= 60 -> "" // 空白を表示する
|
||||
cols[1].isNotEmpty() -> cols[1]
|
||||
else -> instance
|
||||
}, 36
|
||||
)
|
||||
|
||||
this.colorText = parseColor(cols[3]) ?: type.colorText
|
||||
|
||||
val ia = cols[4].split(',')
|
||||
.filter { it.isNotBlank() }
|
||||
.map { color(it) }
|
||||
.toIntArray()
|
||||
|
||||
this.colorBg = ColorBg(
|
||||
when {
|
||||
ia.isNotEmpty() -> ia
|
||||
else -> type.colorBg
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var lastList = ConcurrentHashMap<String, Item>()
|
||||
|
||||
private var timeNextLoad = 0L
|
||||
private val reLine = Pattern.compile("""([^\x0d\x0a]+)""")
|
||||
|
||||
fun load() {
|
||||
synchronized(this) {
|
||||
// 頻繁に読み直さない
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
if(timeNextLoad - now > 0) return
|
||||
timeNextLoad = now + 301000L
|
||||
|
||||
val text = App1.getHttpCachedString("https://cdn.weep.me/instance/tsv/")
|
||||
if(text?.isEmpty() != false) return
|
||||
|
||||
val list = ConcurrentHashMap<String, Item>()
|
||||
val m = reLine.matcher(text)
|
||||
while(m.find()) {
|
||||
try {
|
||||
val cols = m.group(1).split('\t')
|
||||
|
||||
val type = try {
|
||||
findType(cols[0].toInt())
|
||||
} catch(ignored : Throwable) {
|
||||
null
|
||||
} ?: Type.MastodonJapan
|
||||
|
||||
val item = Item(type, cols)
|
||||
|
||||
|
||||
if(item.instance.isNotEmpty()) list[item.instance] = item
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
if(list.isNotEmpty()) lastList = list
|
||||
}
|
||||
}
|
||||
}
|
|
@ -202,6 +202,13 @@ fun String?.filterNotEmpty() : String? = when {
|
|||
// return sb.toString()
|
||||
//}
|
||||
|
||||
fun ellipsize(src : String, limit : Int) : String =
|
||||
if(src.codePointCount(0, src.length) <= limit) {
|
||||
src
|
||||
} else {
|
||||
"${src.substring(0, src.offsetByCodePoints(0, limit))}…"
|
||||
}
|
||||
|
||||
fun String.sanitizeBDI() : String {
|
||||
|
||||
// 文字列をスキャンしてBDI制御文字をスタックに入れていく
|
||||
|
|
|
@ -457,5 +457,22 @@
|
|||
|
||||
<View style="@style/setting_divider"/>
|
||||
|
||||
<TextView
|
||||
style="@style/setting_row_label"
|
||||
android:text="@string/show_instance_ticker"
|
||||
/>
|
||||
|
||||
<LinearLayout style="@style/setting_row_form">
|
||||
|
||||
<Switch
|
||||
android:id="@+id/swInstanceTicker"
|
||||
style="@style/setting_horizontal_stretch"
|
||||
android:gravity="center"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View style="@style/setting_divider"/>
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
|
@ -808,5 +808,6 @@
|
|||
<string name="profile_directory">プロフィールディレクトリ</string>
|
||||
<string name="about_this_instance">このインスタンスについて</string>
|
||||
<string name="top_page">トップページ</string>
|
||||
<string name="show_instance_ticker">Instance tickerを表示する(カラムのリロードが必要)</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
@ -825,5 +825,6 @@
|
|||
<string name="profile_directory">Profile directory</string>
|
||||
<string name="about_this_instance">About this instance</string>
|
||||
<string name="top_page">Top page</string>
|
||||
<string name="show_instance_ticker">Show instance ticker (column reload required)</string>
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue