Markdown対応の続き
This commit is contained in:
parent
605afa9323
commit
7bf9e6b40a
|
@ -1113,7 +1113,6 @@ class ActMain : AppCompatActivity()
|
|||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
sv = Pref.spTimelineFontBold(pref)
|
||||
|
@ -1124,13 +1123,12 @@ class ActMain : AppCompatActivity()
|
|||
log.trace(ex)
|
||||
}
|
||||
|
||||
} else if(timeline_font != null) {
|
||||
} else {
|
||||
try {
|
||||
timeline_font_bold = Typeface.create(timeline_font, Typeface.BOLD)
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun parseIconSize(stringPref : Pref.StringPref) : Int {
|
||||
|
|
|
@ -163,17 +163,15 @@ class ColumnViewHolder(
|
|||
|
||||
init {
|
||||
|
||||
if(ActMain.timeline_font != null) {
|
||||
viewRoot.scan { v ->
|
||||
try {
|
||||
if(v is Button) {
|
||||
// ボタンは触らない
|
||||
} else if(v is TextView) {
|
||||
v.typeface = ActMain.timeline_font
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
viewRoot.scan { v ->
|
||||
try {
|
||||
if(v is Button) {
|
||||
// ボタンは触らない
|
||||
} else if(v is TextView) {
|
||||
v.typeface = ActMain.timeline_font
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -242,36 +242,31 @@ internal class ItemViewHolder(
|
|||
|
||||
this.access_info = column.access_info
|
||||
|
||||
if(ActMain.timeline_font != null || ActMain.timeline_font_bold != null) {
|
||||
val font_bold = ActMain.timeline_font_bold ?: ActMain.timeline_font
|
||||
val font_normal = ActMain.timeline_font ?: ActMain.timeline_font_bold
|
||||
viewRoot.scan { v ->
|
||||
try {
|
||||
if(v is CountImageButton) {
|
||||
// ボタンは太字なので触らない
|
||||
} else if(v is Button) {
|
||||
// ボタンは太字なので触らない
|
||||
} else if(v is TextView) {
|
||||
val typeface = when {
|
||||
v === tvName ||
|
||||
v === tvFollowerName ||
|
||||
v === tvBoosted ||
|
||||
v === tvTrendTagCount ||
|
||||
v === tvTrendTagName ||
|
||||
v === tvFilterPhrase -> font_bold
|
||||
else -> font_normal
|
||||
}
|
||||
if(typeface != null) v.typeface = typeface
|
||||
val font_bold = ActMain.timeline_font_bold
|
||||
val font_normal = ActMain.timeline_font
|
||||
viewRoot.scan { v ->
|
||||
try {
|
||||
when(v) {
|
||||
// ボタンは太字なので触らない
|
||||
is CountImageButton -> {
|
||||
}
|
||||
// ボタンは太字なので触らない
|
||||
is Button -> {
|
||||
}
|
||||
|
||||
is TextView -> v.typeface = when {
|
||||
v === tvName ||
|
||||
v === tvFollowerName ||
|
||||
v === tvBoosted ||
|
||||
v === tvTrendTagCount ||
|
||||
v === tvTrendTagName ||
|
||||
v === tvFilterPhrase -> font_bold
|
||||
else -> font_normal
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
} catch(ex : Throwable) {
|
||||
log.trace(ex)
|
||||
}
|
||||
} else {
|
||||
tvName.typeface = Typeface.DEFAULT_BOLD
|
||||
tvFollowerName.typeface = Typeface.DEFAULT_BOLD
|
||||
tvBoosted.typeface = Typeface.DEFAULT_BOLD
|
||||
tvTrendTagCount.typeface = Typeface.DEFAULT_BOLD
|
||||
}
|
||||
|
||||
if(bSimpleList) {
|
||||
|
|
|
@ -24,9 +24,7 @@ internal abstract class ViewHolderHeaderBase(val activity : ActMain, val viewRoo
|
|||
if(v is Button) {
|
||||
// ボタンは太字なので触らない
|
||||
} else if(v is TextView) {
|
||||
if(ActMain.timeline_font != null) {
|
||||
v.typeface = ActMain.timeline_font
|
||||
}
|
||||
v.typeface = ActMain.timeline_font
|
||||
if(! activity.timeline_font_size_sp.isNaN()) {
|
||||
v.textSize = activity.timeline_font_size_sp
|
||||
}
|
||||
|
|
|
@ -262,8 +262,8 @@ internal class ViewHolderHeaderProfile(
|
|||
val content_color = column.content_color
|
||||
val c = if(content_color != 0) content_color else default_color
|
||||
|
||||
val nameTypeface = ActMain.timeline_font_bold ?: Typeface.DEFAULT_BOLD
|
||||
val valueTypeface = ActMain.timeline_font ?: Typeface.DEFAULT
|
||||
val nameTypeface = ActMain.timeline_font_bold
|
||||
val valueTypeface = ActMain.timeline_font
|
||||
|
||||
for(item in who.fields) {
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package jp.juggler.subwaytooter.span
|
||||
|
||||
|
||||
|
||||
interface AnimatableSpanInvalidator {
|
||||
val timeFromStart : Long
|
||||
fun delayInvalidate(delay : Long)
|
||||
}
|
||||
|
||||
interface AnimatableSpan{
|
||||
|
||||
fun setInvalidateCallback(
|
||||
draw_target_tag : Any,
|
||||
invalidate_callback : AnimatableSpanInvalidator
|
||||
)
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
package jp.juggler.subwaytooter.span
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Typeface
|
||||
import android.text.TextPaint
|
||||
import android.text.style.MetricAffectingSpan
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
class MisskeyBigSpan (
|
||||
private val typeface : Typeface
|
||||
): MetricAffectingSpan() ,AnimatableSpan{
|
||||
|
||||
private var invalidate_callback : AnimatableSpanInvalidator? = null
|
||||
private var refDrawTarget : WeakReference<Any>? = null
|
||||
|
||||
override fun setInvalidateCallback(
|
||||
draw_target_tag : Any,
|
||||
invalidate_callback : AnimatableSpanInvalidator
|
||||
) {
|
||||
this.refDrawTarget = WeakReference(draw_target_tag)
|
||||
this.invalidate_callback = invalidate_callback
|
||||
}
|
||||
|
||||
private val textScalingMax = 1.5f
|
||||
|
||||
override fun updateMeasureState(paint : TextPaint) {
|
||||
apply(paint,bDrawing=false)
|
||||
}
|
||||
|
||||
override fun updateDrawState(drawState : TextPaint) {
|
||||
apply(drawState,bDrawing=true)
|
||||
}
|
||||
|
||||
|
||||
private fun apply(paint : Paint,bDrawing:Boolean ) {
|
||||
|
||||
|
||||
val textScaling = when{
|
||||
!bDrawing -> textScalingMax
|
||||
else -> {
|
||||
invalidate_callback?.delayInvalidate(100L)
|
||||
val t = (invalidate_callback?.timeFromStart ?: 0L) /500f
|
||||
|
||||
textScalingMax * ( Math.sin(t.toDouble()).toFloat() * 0.1f + 0.9f)
|
||||
}
|
||||
}
|
||||
|
||||
paint.textSize = paint.textSize * textScaling
|
||||
|
||||
val oldTypeface = paint.typeface
|
||||
val oldStyle = oldTypeface?.style ?: 0
|
||||
val fakeStyle = oldStyle and typeface.style.inv()
|
||||
|
||||
if(fakeStyle and Typeface.BOLD != 0) {
|
||||
paint.isFakeBoldText = true
|
||||
}
|
||||
|
||||
if(fakeStyle and Typeface.ITALIC != 0) {
|
||||
paint.textSkewX = - 0.25f
|
||||
}
|
||||
|
||||
paint.typeface = typeface
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class MisskeyMotionSpan (
|
||||
private val typeface : Typeface
|
||||
): MetricAffectingSpan() ,AnimatableSpan{
|
||||
|
||||
private var invalidate_callback : AnimatableSpanInvalidator? = null
|
||||
private var refDrawTarget : WeakReference<Any>? = null
|
||||
|
||||
override fun setInvalidateCallback(
|
||||
draw_target_tag : Any,
|
||||
invalidate_callback : AnimatableSpanInvalidator
|
||||
) {
|
||||
this.refDrawTarget = WeakReference(draw_target_tag)
|
||||
this.invalidate_callback = invalidate_callback
|
||||
}
|
||||
|
||||
private val textScalingMax = 1.0f
|
||||
|
||||
override fun updateMeasureState(paint : TextPaint) {
|
||||
apply(paint,bDrawing=false)
|
||||
}
|
||||
|
||||
override fun updateDrawState(drawState : TextPaint) {
|
||||
apply(drawState,bDrawing=true)
|
||||
}
|
||||
|
||||
|
||||
private fun apply(paint : Paint,bDrawing:Boolean ) {
|
||||
|
||||
|
||||
val textScaling = when{
|
||||
!bDrawing -> textScalingMax
|
||||
else -> {
|
||||
invalidate_callback?.delayInvalidate(100L)
|
||||
val t = (invalidate_callback?.timeFromStart ?: 0L) /100f
|
||||
|
||||
textScalingMax * ( Math.sin(t.toDouble()).toFloat() * 0.1f + 0.9f)
|
||||
}
|
||||
}
|
||||
|
||||
paint.textSize = paint.textSize * textScaling
|
||||
|
||||
val oldTypeface = paint.typeface
|
||||
val oldStyle = oldTypeface?.style ?: 0
|
||||
val fakeStyle = oldStyle and typeface.style.inv()
|
||||
|
||||
if(fakeStyle and Typeface.BOLD != 0) {
|
||||
paint.isFakeBoldText = true
|
||||
}
|
||||
|
||||
if(fakeStyle and Typeface.ITALIC != 0) {
|
||||
paint.textSkewX = - 0.25f
|
||||
}
|
||||
|
||||
paint.typeface = typeface
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -13,7 +13,7 @@ import jp.juggler.subwaytooter.Pref
|
|||
import jp.juggler.subwaytooter.util.LogCategory
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class NetworkEmojiSpan internal constructor(private val url : String) : ReplacementSpan() {
|
||||
class NetworkEmojiSpan internal constructor(private val url : String) : ReplacementSpan(),AnimatableSpan {
|
||||
|
||||
companion object {
|
||||
|
||||
|
@ -27,9 +27,7 @@ class NetworkEmojiSpan internal constructor(private val url : String) : Replacem
|
|||
private val rect_src = Rect()
|
||||
private val rect_dst = RectF()
|
||||
|
||||
private var invalidate_callback : InvalidateCallback? = null
|
||||
private var refDrawTarget : WeakReference<Any>? = null
|
||||
|
||||
|
||||
// フレーム探索結果を格納する構造体を確保しておく
|
||||
private val mFrameFindResult = ApngFrames.FindFrameResult()
|
||||
|
||||
|
@ -37,18 +35,17 @@ class NetworkEmojiSpan internal constructor(private val url : String) : Replacem
|
|||
mPaint.isFilterBitmap = true
|
||||
}
|
||||
|
||||
interface InvalidateCallback {
|
||||
val timeFromStart : Long
|
||||
fun delayInvalidate(delay : Long)
|
||||
}
|
||||
private var invalidate_callback : AnimatableSpanInvalidator? = null
|
||||
private var refDrawTarget : WeakReference<Any>? = null
|
||||
|
||||
fun setInvalidateCallback(
|
||||
override fun setInvalidateCallback(
|
||||
draw_target_tag : Any,
|
||||
invalidate_callback : InvalidateCallback
|
||||
invalidate_callback : AnimatableSpanInvalidator
|
||||
) {
|
||||
this.refDrawTarget = WeakReference(draw_target_tag)
|
||||
this.invalidate_callback = invalidate_callback
|
||||
}
|
||||
|
||||
|
||||
override fun getSize(
|
||||
paint : Paint,
|
||||
|
|
|
@ -15,9 +15,7 @@ import jp.juggler.subwaytooter.ActMain
|
|||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.Pref
|
||||
import jp.juggler.subwaytooter.R
|
||||
import jp.juggler.subwaytooter.span.EmojiImageSpan
|
||||
import jp.juggler.subwaytooter.span.HighlightSpan
|
||||
import jp.juggler.subwaytooter.span.MyClickableSpan
|
||||
import jp.juggler.subwaytooter.span.*
|
||||
import jp.juggler.subwaytooter.table.HighlightWord
|
||||
import uk.co.chrisjenx.calligraphy.CalligraphyTypefaceSpan
|
||||
import java.util.regex.Pattern
|
||||
|
@ -656,6 +654,16 @@ object MisskeyMarkdownDecoder {
|
|||
generateMotionNodeParser(NodeType.Motion)
|
||||
)
|
||||
|
||||
|
||||
private val reStartEmptyLines = """\A(?:[ ]*?[\x0d\x0a]+)+""".toRegex()
|
||||
private val reEndEmptyLines = """[\s\x0d\x0a]+\z""".toRegex()
|
||||
private fun trimBlock(s:String?):String?{
|
||||
s?:return null
|
||||
return s
|
||||
.replace(reStartEmptyLines,"")
|
||||
.replace(reEndEmptyLines,"")
|
||||
}
|
||||
|
||||
private fun parse(source : String?) : ArrayList<Node> {
|
||||
val result = ArrayList<Node>()
|
||||
|
||||
|
@ -762,7 +770,7 @@ object MisskeyMarkdownDecoder {
|
|||
try {
|
||||
if(src != null) {
|
||||
|
||||
val font_bold = ActMain.timeline_font_bold ?: ActMain.timeline_font
|
||||
val font_bold = ActMain.timeline_font_bold
|
||||
|
||||
for( node in parse(src) ){
|
||||
val nodeSource = src.substring(node.sourceStart,node.sourceStart+node.sourceLength)
|
||||
|
@ -884,58 +892,13 @@ object MisskeyMarkdownDecoder {
|
|||
appendLink(acct,"https://${linkHelper}/$acct")
|
||||
}
|
||||
}
|
||||
|
||||
NodeType.Hashtag ->{
|
||||
val tag = data?.get(0)
|
||||
if(tag?.isNotEmpty()==true){
|
||||
appendLink("#$tag","https://misskey.m544.net/tags/"+tag.encodePercent())
|
||||
}
|
||||
}
|
||||
NodeType.Text->{
|
||||
appendText(nodeSource)
|
||||
}
|
||||
NodeType.Big ->{
|
||||
appendText(data?.get(0))
|
||||
setSpan(RelativeSizeSpan(1.5f))
|
||||
setSpan(CalligraphyTypefaceSpan(font_bold))
|
||||
// TODO アニメーション
|
||||
}
|
||||
|
||||
NodeType.Bold->{
|
||||
appendText(data?.get(0))
|
||||
setSpan(CalligraphyTypefaceSpan(font_bold))
|
||||
}
|
||||
NodeType.Title->{
|
||||
appendText(data?.get(0)?.trim{it<=' '})
|
||||
setSpan(AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER))
|
||||
// TODO 見出し背景に色をつける
|
||||
|
||||
// ブロック要素なので改行必須
|
||||
appendText("\n")
|
||||
}
|
||||
NodeType.CodeBlock->{
|
||||
appendTextCode(data?.get(0)?.trimEnd())
|
||||
setSpan(BackgroundColorSpan(0x40808080))
|
||||
setSpan(CalligraphyTypefaceSpan(Typeface.MONOSPACE))
|
||||
|
||||
// ブロック要素なので改行必須
|
||||
appendText("\n")
|
||||
|
||||
// TODO Syntax highlight
|
||||
}
|
||||
|
||||
NodeType.CodeInline->{
|
||||
appendTextCode(data?.get(0))
|
||||
setSpan(BackgroundColorSpan(0x40808080))
|
||||
setSpan(CalligraphyTypefaceSpan(Typeface.MONOSPACE))
|
||||
// TODO Syntax highlight
|
||||
}
|
||||
|
||||
NodeType.Quote->{
|
||||
appendText(data?.get(0)?.trim())
|
||||
setSpan(CalligraphyTypefaceSpan(Typeface.defaultFromStyle(Typeface.ITALIC)))
|
||||
// ブロック要素なので改行必須
|
||||
appendText("\n")
|
||||
}
|
||||
|
||||
NodeType.Emoji->{
|
||||
val code = data?.get(0)
|
||||
|
@ -944,23 +907,74 @@ object MisskeyMarkdownDecoder {
|
|||
}
|
||||
}
|
||||
|
||||
NodeType.Search->{
|
||||
val text = data?.get(0)
|
||||
if( text?.isNotEmpty()==true){
|
||||
appendText(text)
|
||||
start = sb.length
|
||||
appendLink(
|
||||
context.getString(R.string.search),
|
||||
"https://www.google.co.jp/search?q="+text.encodePercent()
|
||||
)
|
||||
// ブロック要素なので改行必須
|
||||
appendText("\n")
|
||||
}
|
||||
////////////////////////////////////////////
|
||||
// 装飾インライン要素
|
||||
|
||||
NodeType.Text->{
|
||||
appendText(nodeSource)
|
||||
}
|
||||
|
||||
NodeType.Big ->{
|
||||
appendText(data?.get(0))
|
||||
setSpan(MisskeyBigSpan(font_bold))
|
||||
}
|
||||
|
||||
NodeType.Motion->{
|
||||
val code = data?.get(0)
|
||||
appendText(code)
|
||||
// TODO なんかする
|
||||
setSpan(MisskeyMotionSpan(ActMain.timeline_font))
|
||||
}
|
||||
|
||||
NodeType.Bold->{
|
||||
appendText(data?.get(0))
|
||||
setSpan(CalligraphyTypefaceSpan(font_bold))
|
||||
}
|
||||
|
||||
NodeType.CodeInline->{
|
||||
appendTextCode(data?.get(0))
|
||||
setSpan(BackgroundColorSpan(0x40808080))
|
||||
setSpan(CalligraphyTypefaceSpan(Typeface.MONOSPACE))
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// ブロック要素
|
||||
|
||||
NodeType.Title->{
|
||||
|
||||
appendText(trimBlock(data?.get(0)))
|
||||
setSpan(AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER))
|
||||
setSpan(BackgroundColorSpan(0x20808080))
|
||||
setSpan(RelativeSizeSpan(1.5f))
|
||||
appendText("\n")
|
||||
}
|
||||
|
||||
NodeType.CodeBlock->{
|
||||
appendTextCode(trimBlock(data?.get(0)))
|
||||
setSpan(BackgroundColorSpan(0x40808080))
|
||||
setSpan(RelativeSizeSpan(0.7f))
|
||||
setSpan(CalligraphyTypefaceSpan(Typeface.MONOSPACE))
|
||||
appendText("\n")
|
||||
}
|
||||
|
||||
NodeType.Quote->{
|
||||
appendText(trimBlock(data?.get(0)))
|
||||
setSpan(BackgroundColorSpan(0x20808080))
|
||||
setSpan(CalligraphyTypefaceSpan(Typeface.defaultFromStyle(Typeface.ITALIC)))
|
||||
appendText("\n")
|
||||
}
|
||||
|
||||
NodeType.Search->{
|
||||
val text = data?.get(0)
|
||||
val kw_start = sb.length // キーワードの開始位置
|
||||
appendText(text)
|
||||
appendText(" ")
|
||||
start = sb.length // 検索リンクの開始位置
|
||||
appendLink(
|
||||
context.getString(R.string.search),
|
||||
"https://www.google.co.jp/search?q="+(text?:"Subway Tooter").encodePercent()
|
||||
)
|
||||
sb.setSpan(RelativeSizeSpan(1.5f), kw_start, sb.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
appendText("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,10 +8,11 @@ import android.view.View
|
|||
import java.util.ArrayList
|
||||
|
||||
import jp.juggler.subwaytooter.App1
|
||||
import jp.juggler.subwaytooter.span.NetworkEmojiSpan
|
||||
import jp.juggler.subwaytooter.span.AnimatableSpan
|
||||
import jp.juggler.subwaytooter.span.AnimatableSpanInvalidator
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class NetworkEmojiInvalidator(internal val handler : Handler, internal val view : View) : Runnable, NetworkEmojiSpan.InvalidateCallback {
|
||||
class NetworkEmojiInvalidator(internal val handler : Handler, internal val view : View) : Runnable, AnimatableSpanInvalidator {
|
||||
|
||||
private val draw_target_list = ArrayList<WeakReference<Any>>()
|
||||
|
||||
|
@ -41,7 +42,7 @@ class NetworkEmojiInvalidator(internal val handler : Handler, internal val view
|
|||
draw_target_list.clear()
|
||||
|
||||
if(dst != null) {
|
||||
for(span in dst.getSpans(0, dst.length, NetworkEmojiSpan::class.java)) {
|
||||
for(span in dst.getSpans(0, dst.length, AnimatableSpan::class.java)) {
|
||||
val tag = WeakReference(Any())
|
||||
draw_target_list.add(tag)
|
||||
span.setInvalidateCallback(tag, this)
|
||||
|
|
Loading…
Reference in New Issue