SubwayTooter-Android-App/app/src/main/java/jp/juggler/subwaytooter/Styler.kt

253 lines
8.0 KiB
Kotlin
Raw Normal View History

package jp.juggler.subwaytooter
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
import android.graphics.drawable.RippleDrawable
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.StateListDrawable
import android.graphics.drawable.shapes.RectShape
import android.os.Build
import android.support.v4.content.ContextCompat
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import android.widget.ImageView
import java.util.Locale
import jp.juggler.subwaytooter.api.entity.TootAccount
import jp.juggler.subwaytooter.api.entity.TootStatus
import jp.juggler.subwaytooter.table.UserRelation
import jp.juggler.subwaytooter.span.EmojiImageSpan
object Styler {
fun getAttributeColor(context : Context, attrId : Int) : Int {
val theme = context.theme
val a = theme.obtainStyledAttributes(intArrayOf(attrId))
val color = a.getColor(0, Color.BLACK)
a.recycle()
return color
}
fun getAttributeResourceId(context : Context, attrId : Int) : Int {
val theme = context.theme
val a = theme.obtainStyledAttributes(intArrayOf(attrId))
val resourceId = a.getResourceId(0, 0)
a.recycle()
if(resourceId == 0)
throw RuntimeException(
String.format(
Locale.JAPAN,
"attr not defined.attr_id=0x%x",
attrId
)
)
return resourceId
}
fun getAttributeDrawable(context : Context, attrId : Int) : Drawable {
val drawableId = getAttributeResourceId(context, attrId)
return ContextCompat.getDrawable(context, drawableId)
?: throw RuntimeException(
String.format(
Locale.JAPAN,
"getDrawable failed. drawableId=0x%x",
drawableId
)
)
}
// ImageViewにアイコンを設定する
fun setIconDefaultColor(context : Context, imageView : ImageView, iconAttrId : Int) {
imageView.setImageResource(getAttributeResourceId(context, iconAttrId))
}
// ImageViewにアイコンを設定する。色を変えてしまう
fun setIconCustomColor(
context : Context,
imageView : ImageView,
color : Int,
iconAttrId : Int
) {
val d = getAttributeDrawable(context, iconAttrId)
d.mutate() // 色指定が他のアイコンに影響しないようにする
d.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
imageView.setImageDrawable(d)
}
fun getVisibilityIconAttr(visibility : String?) : Int {
2018-01-11 10:31:25 +01:00
return when(visibility) {
null -> R.attr.ic_public
TootStatus.VISIBILITY_PUBLIC -> R.attr.ic_public
TootStatus.VISIBILITY_UNLISTED -> R.attr.ic_lock_open
TootStatus.VISIBILITY_PRIVATE -> R.attr.ic_lock
TootStatus.VISIBILITY_DIRECT -> R.attr.ic_mail
TootStatus.VISIBILITY_WEB_SETTING -> R.attr.ic_question
else -> R.attr.ic_question
2018-01-11 10:31:25 +01:00
}
}
fun getVisibilityIcon(context : Context, visibility : String?) : Int {
return getAttributeResourceId(context, getVisibilityIconAttr(visibility))
}
fun getVisibilityString(context : Context, visibility : String) : String {
return when(visibility) {
TootStatus.VISIBILITY_PUBLIC -> context.getString(R.string.visibility_public)
TootStatus.VISIBILITY_UNLISTED -> context.getString(R.string.visibility_unlisted)
TootStatus.VISIBILITY_PRIVATE -> context.getString(R.string.visibility_private)
TootStatus.VISIBILITY_DIRECT -> context.getString(R.string.visibility_direct)
TootStatus.VISIBILITY_WEB_SETTING -> context.getString(R.string.visibility_web_setting)
else -> "?"
}
}
// アイコン付きの装飾テキストを返す
fun getVisibilityCaption(context : Context, visibility : String) : CharSequence {
val sb = SpannableStringBuilder()
// アイコン部分
val start = sb.length
sb.append(visibility)
val end = sb.length
val icon_id = getVisibilityIcon(context, visibility)
sb.setSpan(EmojiImageSpan(context, icon_id), start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
// 文字列部分
sb.append(' ')
sb.append(getVisibilityString(context, visibility))
if(TootStatus.VISIBILITY_WEB_SETTING == visibility) {
sb.append("\n  (")
sb.append(context.getString(R.string.mastodon_1_6_later))
sb.append(")")
}
return sb
}
fun setFollowIcon(
context : Context
, ibFollow : ImageButton
, ivDot : ImageView
, relation : UserRelation
, who : TootAccount
) {
// 被フォロー状態
if(! relation.followed_by) {
ivDot.visibility = View.GONE
} else {
ivDot.visibility = View.VISIBLE
setIconDefaultColor(context, ivDot, R.attr.ic_followed_by)
// 被フォローリクエスト状態の時に followed_by が 真と偽の両方がありえるようなので
// Relationshipだけを見ても被フォローリクエスト状態は分からないっぽい
// 仕方ないので馬鹿正直に「 followed_byが真ならバッジをつける」しかできない
}
// フォローボタン
// follow button
val color_attr : Int
val icon_attr : Int
when {
relation.blocking -> {
icon_attr = R.attr.ic_block
color_attr = R.attr.colorImageButton
}
relation.muting -> {
icon_attr = R.attr.ic_mute
color_attr = R.attr.colorImageButton
}
relation.getFollowing(who) -> {
icon_attr = R.attr.ic_follow_cross
color_attr = R.attr.colorImageButtonAccent
}
relation.getRequested(who) -> {
icon_attr = R.attr.ic_follow_wait
color_attr = R.attr.colorRegexFilterError
}
else -> {
icon_attr = R.attr.ic_follow_plus
color_attr = R.attr.colorImageButton
}
}
val color = Styler.getAttributeColor(context, color_attr)
setIconCustomColor(context, ibFollow, color, icon_attr)
}
// 色を指定してRippleDrawableを生成する
fun getAdaptiveRippleDrawable(normalColor : Int, pressedColor : Int) : Drawable {
return if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
RippleDrawable(
ColorStateList.valueOf(pressedColor), getRectShape(normalColor), null
)
} else {
getStateListDrawable(normalColor, pressedColor)
}
}
// 色を指定してRectShapeを生成する
private fun getRectShape(color : Int) : Drawable {
val r = RectShape()
val shapeDrawable = ShapeDrawable(r)
shapeDrawable.paint.color = color
return shapeDrawable
}
// 後方互換用にボタン背景Drawableを生成する
private fun getStateListDrawable(normalColor : Int, pressedColor : Int) : StateListDrawable {
val states = StateListDrawable()
states.addState(intArrayOf(android.R.attr.state_pressed), ColorDrawable(pressedColor))
states.addState(intArrayOf(android.R.attr.state_focused), ColorDrawable(pressedColor))
states.addState(intArrayOf(android.R.attr.state_activated), ColorDrawable(pressedColor))
states.addState(intArrayOf(), ColorDrawable(normalColor))
return states
}
private fun getHorizontalPadding(v : View, delta_dp : Float) : Int {
val form_width_max = 420f
val dm = v.resources.displayMetrics
val screen_w = dm.widthPixels
val content_w = (0.5f + form_width_max * dm.density).toInt()
val pad_lr = (screen_w - content_w) / 2
return (if(pad_lr < 0) 0 else pad_lr) + (0.5f + delta_dp * dm.density).toInt()
}
fun fixHorizontalPadding(v : View) {
val pad_lr = getHorizontalPadding(v, 12f)
val pad_t = v.paddingTop
val pad_b = v.paddingBottom
v.setPaddingRelative(pad_lr, pad_t, pad_lr, pad_b)
}
fun fixHorizontalPadding2(v : View) {
val pad_lr = getHorizontalPadding(v, 0f)
val pad_t = v.paddingTop
val pad_b = v.paddingBottom
v.setPaddingRelative(pad_lr, pad_t, pad_lr, pad_b)
}
fun fixHorizontalMargin(v : View) {
val pad_lr = getHorizontalPadding(v, 0f)
val lp = v.layoutParams
if(lp is ViewGroup.MarginLayoutParams) {
lp.leftMargin = pad_lr
lp.rightMargin = pad_lr
}
}
}