mirror of
https://github.com/k0shk0sh/FastHub.git
synced 2025-12-08 19:05:54 +00:00
add emoji support for markwon
This commit is contained in:
parent
ee2c95dec4
commit
5313b35fa2
@ -1,19 +1,32 @@
|
||||
package com.fastaccess.github.di.modules
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import com.fastaccess.data.storage.FastHubSharedPreference
|
||||
import com.fastaccess.github.R
|
||||
import com.fastaccess.github.base.engine.ThemeEngine
|
||||
import com.fastaccess.github.di.annotations.ForApplication
|
||||
import com.fastaccess.github.di.scopes.PerFragment
|
||||
import com.fastaccess.github.extensions.getDrawableCompat
|
||||
import com.fastaccess.github.platform.mentions.MentionsPresenter
|
||||
import com.fastaccess.github.ui.modules.issue.fragment.IssueFragment
|
||||
import com.fastaccess.github.usecase.search.FilterSearchUsersUseCase
|
||||
import com.fastaccess.markdown.R
|
||||
import com.fastaccess.markdown.spans.*
|
||||
import com.fastaccess.github.utils.extensions.theme
|
||||
import com.fastaccess.markdown.GrammarLocatorDef
|
||||
import com.fastaccess.markdown.extension.markwon.emoji.EmojiPlugin
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import net.nightwhistler.htmlspanner.HtmlSpanner
|
||||
import net.nightwhistler.htmlspanner.handlers.StyledTextHandler
|
||||
import net.nightwhistler.htmlspanner.style.Style
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.ext.latex.JLatexMathPlugin
|
||||
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
|
||||
import io.noties.markwon.ext.tables.TablePlugin
|
||||
import io.noties.markwon.ext.tasklist.TaskListPlugin
|
||||
import io.noties.markwon.html.HtmlPlugin
|
||||
import io.noties.markwon.image.glide.GlideImagesPlugin
|
||||
import io.noties.markwon.linkify.LinkifyPlugin
|
||||
import io.noties.markwon.syntax.Prism4jThemeDarkula
|
||||
import io.noties.markwon.syntax.Prism4jThemeDefault
|
||||
import io.noties.markwon.syntax.SyntaxHighlightPlugin
|
||||
import io.noties.prism4j.Prism4j
|
||||
|
||||
/**
|
||||
* Created by Kosh on 02.02.19.
|
||||
@ -23,36 +36,29 @@ class FragmentModule {
|
||||
|
||||
@PerFragment @Provides fun provideContext(fragment: IssueFragment) = fragment.requireContext()
|
||||
|
||||
@PerFragment @Provides fun provideHtmlSpanner(@ForApplication context: Context): HtmlSpanner {
|
||||
val mySpanner = HtmlSpanner()
|
||||
mySpanner.isStripExtraWhiteSpace = true
|
||||
val checked = context.getDrawableCompat(R.drawable.ic_checkbox_small)
|
||||
val unchecked = context.getDrawableCompat(R.drawable.ic_checkbox_empty_small)
|
||||
mySpanner.registerHandler("li", ListsHandler(checked, unchecked))
|
||||
mySpanner.registerHandler("g-emoji", EmojiHandler())
|
||||
mySpanner.registerHandler("b", StyledTextHandler(Style().setFontWeight(Style.FontWeight.BOLD)))
|
||||
mySpanner.registerHandler("strong", StyledTextHandler(Style().setFontWeight(Style.FontWeight.BOLD)))
|
||||
mySpanner.registerHandler("i", ItalicHandler())
|
||||
mySpanner.registerHandler("em", ItalicHandler())
|
||||
mySpanner.registerHandler("ul", MarginHandler())
|
||||
mySpanner.registerHandler("ol", MarginHandler())
|
||||
mySpanner.registerHandler("u", UnderlineHandler())
|
||||
mySpanner.registerHandler("strike", StrikethroughHandler())
|
||||
mySpanner.registerHandler("ins", UnderlineHandler())
|
||||
mySpanner.registerHandler("del", StrikethroughHandler())
|
||||
mySpanner.registerHandler("sub", SubScriptHandler())
|
||||
mySpanner.registerHandler("sup", SuperScriptHandler())
|
||||
mySpanner.registerHandler("a", LinkHandler())
|
||||
mySpanner.registerHandler("emoji", EmojiHandler())
|
||||
mySpanner.registerHandler("mention", LinkHandler())
|
||||
mySpanner.registerHandler("h1", HeaderHandler(1.5f))
|
||||
mySpanner.registerHandler("h2", HeaderHandler(1.4f))
|
||||
mySpanner.registerHandler("h3", HeaderHandler(1.3f))
|
||||
mySpanner.registerHandler("h4", HeaderHandler(1.2f))
|
||||
mySpanner.registerHandler("h5", HeaderHandler(1.1f))
|
||||
mySpanner.registerHandler("h6", HeaderHandler(1.0f))
|
||||
return mySpanner
|
||||
}
|
||||
@SuppressLint("PrivateResource")
|
||||
@PerFragment @Provides fun provideMarkwon(
|
||||
@ForApplication context: Context,
|
||||
preference: FastHubSharedPreference
|
||||
): Markwon = Markwon.builder(context)
|
||||
.usePlugin(JLatexMathPlugin.create(context.resources.getDimension(R.dimen.abc_text_size_subhead_material)))
|
||||
.usePlugin(TaskListPlugin.create(context))
|
||||
.usePlugin(HtmlPlugin.create())
|
||||
.usePlugin(GlideImagesPlugin.create(context))
|
||||
.usePlugin(TablePlugin.create(context))
|
||||
.usePlugin(StrikethroughPlugin.create())
|
||||
.usePlugin(LinkifyPlugin.create())
|
||||
.usePlugin(
|
||||
SyntaxHighlightPlugin.create(
|
||||
Prism4j(GrammarLocatorDef()), if (ThemeEngine.isLightTheme(preference.theme)) {
|
||||
Prism4jThemeDefault.create()
|
||||
} else {
|
||||
Prism4jThemeDarkula.create()
|
||||
}
|
||||
)
|
||||
)
|
||||
.usePlugin(EmojiPlugin.create())
|
||||
.build()
|
||||
|
||||
@PerFragment @Provides fun provideMentionsPresenter(
|
||||
context: Context,
|
||||
|
||||
@ -44,20 +44,34 @@ class NetworkModule {
|
||||
|
||||
@Singleton @Provides fun provideInterceptor() = AuthenticationInterceptor()
|
||||
|
||||
@Singleton @Provides fun provideHttpClient(auth: AuthenticationInterceptor): OkHttpClient = OkHttpClient
|
||||
@Singleton @Provides fun provideHttpLogging() = HttpLoggingInterceptor().apply {
|
||||
level = if (BuildConfig.DEBUG) {
|
||||
HttpLoggingInterceptor.Level.BODY
|
||||
} else {
|
||||
HttpLoggingInterceptor.Level.NONE
|
||||
}
|
||||
}
|
||||
|
||||
@Singleton @Provides fun provideHttpClient(
|
||||
auth: AuthenticationInterceptor,
|
||||
httpLoggingInterceptor: HttpLoggingInterceptor
|
||||
): OkHttpClient = OkHttpClient
|
||||
.Builder()
|
||||
.addInterceptor(ContentTypeInterceptor())
|
||||
.addInterceptor(auth)
|
||||
.addInterceptor(PaginationInterceptor())
|
||||
.addInterceptor(Pandora.get().interceptor)
|
||||
.addInterceptor(HttpLoggingInterceptor())
|
||||
.addInterceptor(httpLoggingInterceptor)
|
||||
.build()
|
||||
|
||||
@Named("apolloClient") @Singleton @Provides fun provideHttpClientForApollo(auth: AuthenticationInterceptor): OkHttpClient = OkHttpClient
|
||||
@Named("apolloClient") @Singleton @Provides fun provideHttpClientForApollo(
|
||||
auth: AuthenticationInterceptor,
|
||||
httpLoggingInterceptor: HttpLoggingInterceptor
|
||||
): OkHttpClient = OkHttpClient
|
||||
.Builder()
|
||||
.addInterceptor(auth)
|
||||
.addInterceptor(Pandora.get().interceptor)
|
||||
.addInterceptor(HttpLoggingInterceptor())
|
||||
.addInterceptor(httpLoggingInterceptor)
|
||||
.build()
|
||||
|
||||
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
package com.fastaccess.github.platform.markwon.hashtag
|
||||
|
||||
import org.commonmark.node.CustomNode
|
||||
import org.commonmark.node.Delimited
|
||||
|
||||
|
||||
/**
|
||||
* Created by kosh on 20/08/2017.
|
||||
*/
|
||||
|
||||
class HashTag : CustomNode(), Delimited {
|
||||
|
||||
var url: String? = null
|
||||
get() = DELIMITER + field!!
|
||||
|
||||
override fun getOpeningDelimiter(): String {
|
||||
return DELIMITER
|
||||
}
|
||||
|
||||
override fun getClosingDelimiter(): String {
|
||||
return " "
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DELIMITER = "#"
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
package com.fastaccess.github.platform.markwon.hashtag
|
||||
|
||||
|
||||
import com.fastaccess.github.platform.markwon.hashtag.internal.HashTagDelimiterProcessor
|
||||
import com.fastaccess.github.platform.markwon.hashtag.internal.HashTagNodeRenderer
|
||||
import org.commonmark.Extension
|
||||
import org.commonmark.parser.Parser
|
||||
import org.commonmark.renderer.html.HtmlRenderer
|
||||
|
||||
/**
|
||||
* Created by kosh on 20/08/2017.
|
||||
*/
|
||||
|
||||
class HashTagExtension private constructor() : Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension {
|
||||
|
||||
override fun extend(parserBuilder: Parser.Builder) {
|
||||
parserBuilder.customDelimiterProcessor(HashTagDelimiterProcessor())
|
||||
}
|
||||
|
||||
override fun extend(rendererBuilder: HtmlRenderer.Builder) {
|
||||
rendererBuilder.nodeRendererFactory { HashTagNodeRenderer(it) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create(): Extension {
|
||||
return HashTagExtension()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
package com.fastaccess.github.platform.markwon.hashtag.internal
|
||||
|
||||
|
||||
import com.fastaccess.github.platform.markwon.hashtag.HashTag
|
||||
import org.commonmark.node.Node
|
||||
import org.commonmark.node.Text
|
||||
import org.commonmark.parser.delimiter.DelimiterProcessor
|
||||
import org.commonmark.parser.delimiter.DelimiterRun
|
||||
|
||||
class HashTagDelimiterProcessor : DelimiterProcessor {
|
||||
|
||||
override fun getOpeningCharacter(): Char = '#'
|
||||
override fun getClosingCharacter(): Char = ' '
|
||||
override fun getMinLength(): Int = 1
|
||||
|
||||
override fun getDelimiterUse(
|
||||
opener: DelimiterRun,
|
||||
closer: DelimiterRun
|
||||
): Int = if (opener.length() >= 1 && closer.length() >= 1) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
override fun process(
|
||||
opener: Text,
|
||||
closer: Text,
|
||||
delimiterCount: Int
|
||||
) {
|
||||
val hashTag = HashTag()
|
||||
var tmp: Node? = opener.next
|
||||
if (tmp != null) {
|
||||
hashTag.url = (tmp as Text).literal
|
||||
}
|
||||
while (tmp != null && tmp !== closer) {
|
||||
val next = tmp.next
|
||||
hashTag.appendChild(tmp)
|
||||
tmp = next
|
||||
}
|
||||
opener.insertAfter(hashTag)
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
package com.fastaccess.github.platform.markwon.hashtag.internal
|
||||
|
||||
import com.fastaccess.github.platform.markwon.hashtag.HashTag
|
||||
import org.commonmark.node.Node
|
||||
import org.commonmark.renderer.NodeRenderer
|
||||
import org.commonmark.renderer.html.HtmlNodeRendererContext
|
||||
import org.commonmark.renderer.html.HtmlWriter
|
||||
|
||||
class HashTagNodeRenderer(private val context: HtmlNodeRendererContext) : NodeRenderer {
|
||||
private val html: HtmlWriter = context.writer
|
||||
|
||||
override fun getNodeTypes(): Set<Class<out Node>> {
|
||||
return setOf<Class<out Node>>(HashTag::class.java)
|
||||
}
|
||||
|
||||
override fun render(node: Node) {
|
||||
val attributes = context.extendAttributes(node, "hashtag", emptyMap())
|
||||
html.tag("hashtag", attributes)
|
||||
renderChildren(node)
|
||||
html.tag("/hashtag")
|
||||
}
|
||||
|
||||
private fun renderChildren(parent: Node) {
|
||||
var node: Node? = parent.firstChild
|
||||
while (node != null) {
|
||||
val next = node.next
|
||||
context.render(node)
|
||||
node = next
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
package com.fastaccess.github.platform.markwon.mention
|
||||
|
||||
import com.fastaccess.github.utils.GITHUB_LINK
|
||||
import org.commonmark.node.CustomNode
|
||||
import org.commonmark.node.Delimited
|
||||
|
||||
|
||||
/**
|
||||
* Created by kosh on 20/08/2017.
|
||||
*/
|
||||
|
||||
class Mention : CustomNode(), Delimited {
|
||||
|
||||
var url: String? = null
|
||||
get() = BASE_URL + field!!
|
||||
|
||||
override fun getOpeningDelimiter(): String = DELIMITER
|
||||
override fun getClosingDelimiter(): String = " "
|
||||
|
||||
companion object {
|
||||
private const val BASE_URL = GITHUB_LINK
|
||||
private const val DELIMITER = "@"
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
package com.fastaccess.github.platform.markwon.mention
|
||||
|
||||
|
||||
import com.fastaccess.github.platform.markwon.mention.internal.MentionDelimiterProcessor
|
||||
import com.fastaccess.github.platform.markwon.mention.internal.MentionNodeRenderer
|
||||
import org.commonmark.Extension
|
||||
import org.commonmark.parser.Parser
|
||||
import org.commonmark.renderer.html.HtmlNodeRendererFactory
|
||||
import org.commonmark.renderer.html.HtmlRenderer
|
||||
|
||||
/**
|
||||
* Created by kosh on 20/08/2017.
|
||||
*/
|
||||
|
||||
class MentionExtension private constructor() : Parser.ParserExtension, HtmlRenderer.HtmlRendererExtension {
|
||||
|
||||
override fun extend(parserBuilder: Parser.Builder) {
|
||||
parserBuilder.customDelimiterProcessor(MentionDelimiterProcessor())
|
||||
}
|
||||
|
||||
override fun extend(rendererBuilder: HtmlRenderer.Builder) {
|
||||
rendererBuilder.nodeRendererFactory(HtmlNodeRendererFactory { MentionNodeRenderer(it) })
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
fun create(): Extension {
|
||||
return MentionExtension()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
package com.fastaccess.github.platform.markwon.mention.internal
|
||||
|
||||
|
||||
import com.fastaccess.github.platform.markwon.mention.Mention
|
||||
import org.commonmark.node.Node
|
||||
import org.commonmark.node.Text
|
||||
import org.commonmark.parser.delimiter.DelimiterProcessor
|
||||
import org.commonmark.parser.delimiter.DelimiterRun
|
||||
|
||||
class MentionDelimiterProcessor : DelimiterProcessor {
|
||||
|
||||
override fun getOpeningCharacter(): Char = '@'
|
||||
|
||||
override fun getClosingCharacter(): Char = ' '
|
||||
|
||||
override fun getMinLength(): Int = 1
|
||||
|
||||
override fun getDelimiterUse(
|
||||
opener: DelimiterRun,
|
||||
closer: DelimiterRun
|
||||
): Int = if (opener.length() >= 1 && closer.length() >= 1) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
|
||||
override fun process(
|
||||
opener: Text,
|
||||
closer: Text,
|
||||
delimiterCount: Int
|
||||
) {
|
||||
val mention = Mention()
|
||||
var tmp: Node? = opener.next
|
||||
if (tmp != null) {
|
||||
mention.url = (tmp as Text).literal
|
||||
}
|
||||
while (tmp != null && tmp !== closer) {
|
||||
val next = tmp.next
|
||||
mention.appendChild(tmp)
|
||||
tmp = next
|
||||
}
|
||||
opener.insertAfter(mention)
|
||||
}
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
package com.fastaccess.github.platform.markwon.mention.internal
|
||||
|
||||
import com.fastaccess.github.platform.markwon.mention.Mention
|
||||
import org.commonmark.node.Node
|
||||
import org.commonmark.renderer.NodeRenderer
|
||||
import org.commonmark.renderer.html.HtmlNodeRendererContext
|
||||
import org.commonmark.renderer.html.HtmlWriter
|
||||
|
||||
class MentionNodeRenderer(private val context: HtmlNodeRendererContext) : NodeRenderer {
|
||||
private val html: HtmlWriter = context.writer
|
||||
|
||||
override fun getNodeTypes(): Set<Class<out Node>> {
|
||||
return setOf<Class<out Node>>(Mention::class.java)
|
||||
}
|
||||
|
||||
override fun render(node: Node) {
|
||||
val attributes = context.extendAttributes(node, "mention", emptyMap())
|
||||
html.tag("mention", attributes)
|
||||
renderChildren(node)
|
||||
html.tag("/mention")
|
||||
}
|
||||
|
||||
private fun renderChildren(parent: Node) {
|
||||
var node: Node? = parent.firstChild
|
||||
while (node != null) {
|
||||
val next = node.next
|
||||
context.render(node)
|
||||
node = next
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,13 +10,13 @@ import com.fastaccess.github.ui.adapter.base.BaseViewHolder
|
||||
import com.fastaccess.github.ui.adapter.viewholder.CommentViewHolder
|
||||
import com.fastaccess.github.ui.adapter.viewholder.IssueContentViewHolder
|
||||
import com.fastaccess.github.ui.adapter.viewholder.LoadingViewHolder
|
||||
import net.nightwhistler.htmlspanner.HtmlSpanner
|
||||
import io.noties.markwon.Markwon
|
||||
|
||||
/**
|
||||
* Created by Kosh on 20.01.19.
|
||||
*/
|
||||
class IssueTimelineAdapter(
|
||||
private val htmlSpanner: HtmlSpanner,
|
||||
private val markwon: Markwon,
|
||||
private val theme: Int
|
||||
) : ListAdapter<TimelineModel, RecyclerView.ViewHolder>(DIFF_CALLBACK) {
|
||||
|
||||
@ -38,7 +38,7 @@ class IssueTimelineAdapter(
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
COMMENT -> CommentViewHolder(parent, htmlSpanner, theme, notifyCallback)
|
||||
COMMENT -> CommentViewHolder(parent, markwon, theme, notifyCallback)
|
||||
CONTENT -> IssueContentViewHolder(parent)
|
||||
else -> LoadingViewHolder<Any>(parent).apply { itemView.isVisible = false }
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
package com.fastaccess.github.ui.adapter.viewholder
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.text.util.Linkify
|
||||
import android.util.Patterns
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
@ -12,9 +14,9 @@ import com.fastaccess.github.extensions.timeAgo
|
||||
import com.fastaccess.github.ui.adapter.base.BaseViewHolder
|
||||
import com.fastaccess.github.utils.extensions.popupEmoji
|
||||
import com.fastaccess.markdown.MarkdownProvider
|
||||
import com.fastaccess.markdown.spans.drawable.DrawableGetter
|
||||
import io.noties.markwon.Markwon
|
||||
import kotlinx.android.synthetic.main.comment_row_item.view.*
|
||||
import net.nightwhistler.htmlspanner.HtmlSpanner
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
||||
/**
|
||||
@ -23,7 +25,7 @@ import net.nightwhistler.htmlspanner.HtmlSpanner
|
||||
|
||||
class CommentViewHolder(
|
||||
parent: ViewGroup,
|
||||
private val htmlSpanner: HtmlSpanner,
|
||||
private val markwon: Markwon,
|
||||
private val theme: Int,
|
||||
private val callback: (position: Int) -> Unit
|
||||
) : BaseViewHolder<CommentModel?>(
|
||||
@ -33,7 +35,7 @@ class CommentViewHolder(
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun bind(item: CommentModel?) {
|
||||
val model = item ?: kotlin.run {
|
||||
val model = item ?: run {
|
||||
itemView.isVisible = false
|
||||
return
|
||||
}
|
||||
@ -46,8 +48,23 @@ class CommentViewHolder(
|
||||
"${model.authorAssociation?.value?.toLowerCase()?.replace("_", "")} ${model.updatedAt?.timeAgo()}"
|
||||
}
|
||||
|
||||
MarkdownProvider.loadIntoTextView(htmlSpanner, description, model.body ?: "", ThemeEngine.getCodeBackground(theme),
|
||||
ThemeEngine.isLightTheme(theme))
|
||||
MarkdownProvider.loadIntoTextView(
|
||||
markwon, description, model.body ?: "", ThemeEngine.getCodeBackground(theme),
|
||||
ThemeEngine.isLightTheme(theme)
|
||||
)
|
||||
|
||||
val filter = Linkify.TransformFilter { match, _ -> match.group() }
|
||||
|
||||
val mentionPattern = Pattern.compile("@([A-Za-z0-9_-]+)")
|
||||
val mentionScheme = "https://www.github.com/"
|
||||
Linkify.addLinks(description, mentionPattern, mentionScheme, null, filter)
|
||||
|
||||
val hashtagPattern = Pattern.compile("#([A-Za-z0-9_-]+)")
|
||||
val hashtagScheme = "https://www.github.com/"
|
||||
Linkify.addLinks(description, hashtagPattern, hashtagScheme, null, filter)
|
||||
|
||||
val urlPattern = Patterns.WEB_URL
|
||||
Linkify.addLinks(description, urlPattern, null, null, filter)
|
||||
|
||||
addEmoji.setOnClickListener {
|
||||
it.popupEmoji(requireNotNull(model.id), model.reactionGroups) {
|
||||
@ -72,14 +89,4 @@ class CommentViewHolder(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetached() {
|
||||
super.onDetached()
|
||||
itemView.description?.let {
|
||||
if (it.tag is DrawableGetter) {
|
||||
val target = it.tag as DrawableGetter
|
||||
target.clear(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -48,10 +48,10 @@ import com.otaliastudios.autocomplete.CharPolicy
|
||||
import github.type.CommentAuthorAssociation
|
||||
import github.type.IssueState
|
||||
import github.type.LockReason
|
||||
import io.noties.markwon.Markwon
|
||||
import kotlinx.android.synthetic.main.empty_state_layout.*
|
||||
import kotlinx.android.synthetic.main.issue_header_row_item.*
|
||||
import kotlinx.android.synthetic.main.issue_pr_fragment_layout.*
|
||||
import net.nightwhistler.htmlspanner.HtmlSpanner
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
@ -63,7 +63,7 @@ class IssueFragment : BaseFragment(), LockUnlockFragment.OnLockReasonSelected,
|
||||
MilestoneFragment.OnMilestoneChanged {
|
||||
|
||||
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
|
||||
@Inject lateinit var htmlSpanner: HtmlSpanner
|
||||
@Inject lateinit var markwon: Markwon
|
||||
@Inject lateinit var preference: FastHubSharedPreference
|
||||
@Inject lateinit var mentionsPresenter: MentionsPresenter
|
||||
|
||||
@ -71,7 +71,7 @@ class IssueFragment : BaseFragment(), LockUnlockFragment.OnLockReasonSelected,
|
||||
private val login by lazy { arguments?.getString(EXTRA) ?: "" }
|
||||
private val repo by lazy { arguments?.getString(EXTRA_TWO) ?: "" }
|
||||
private val number by lazy { arguments?.getInt(EXTRA_THREE) ?: 0 }
|
||||
private val adapter by lazy { IssueTimelineAdapter(htmlSpanner, preference.theme) }
|
||||
private val adapter by lazy { IssueTimelineAdapter(markwon, preference.theme) }
|
||||
|
||||
override fun layoutRes(): Int = R.layout.issue_pr_fragment_layout
|
||||
override fun viewModel(): BaseViewModel? = viewModel
|
||||
@ -240,7 +240,7 @@ class IssueFragment : BaseFragment(), LockUnlockFragment.OnLockReasonSelected,
|
||||
"${model.authorAssociation?.toLowerCase()?.replace("_", "")} ${model.updatedAt?.timeAgo()}"
|
||||
}
|
||||
MarkdownProvider.loadIntoTextView(
|
||||
htmlSpanner, description, model.body ?: "", ThemeEngine.getCodeBackground(theme),
|
||||
markwon, description, model.body ?: "", ThemeEngine.getCodeBackground(theme),
|
||||
ThemeEngine.isLightTheme(theme)
|
||||
)
|
||||
state.text = model.state?.toLowerCase()
|
||||
|
||||
@ -33,6 +33,8 @@ class CloseOpenIssuePrUseCase @Inject constructor(
|
||||
.observeOn(schedulerProvider.uiThread())
|
||||
.flatMapObservable { issue ->
|
||||
issuePrService.editIssue(login, repo, number, IssueRequestModel(state = if ("closed".equals(issue.state, true)) "open" else "closed"))
|
||||
.subscribeOn(schedulerProvider.ioThread())
|
||||
.observeOn(schedulerProvider.uiThread())
|
||||
.map {
|
||||
issue.state = it.issueState
|
||||
issueRepositoryProvider.upsert(issue)
|
||||
|
||||
@ -121,25 +121,21 @@ ext {
|
||||
]
|
||||
|
||||
markdown = [
|
||||
'com.github.NightWhistler:HtmlSpanner:-SNAPSHOT',
|
||||
'net.sourceforge.htmlcleaner:htmlcleaner:2.22',
|
||||
"com.atlassian.commonmark:commonmark:${commonmark}",
|
||||
"com.atlassian.commonmark:commonmark-ext-autolink:${commonmark}",
|
||||
"com.atlassian.commonmark:commonmark-ext-gfm-strikethrough:${commonmark}",
|
||||
"com.atlassian.commonmark:commonmark-ext-gfm-tables:${commonmark}",
|
||||
"com.atlassian.commonmark:commonmark-ext-ins:${commonmark}",
|
||||
"com.atlassian.commonmark:commonmark-ext-yaml-front-matter:${commonmark}",
|
||||
"io.noties.markwon:core:$markwon_version",
|
||||
"io.noties.markwon:ext-latex:$markwon_version",
|
||||
"io.noties.markwon:ext-strikethrough:$markwon_version",
|
||||
"io.noties.markwon:ext-tables:$markwon_version",
|
||||
"io.noties.markwon:ext-tasklist:$markwon_version",
|
||||
"io.noties.markwon:html:$markwon_version",
|
||||
"io.noties.markwon:image:$markwon_version",
|
||||
"io.noties.markwon:image-glide:$markwon_version",
|
||||
"io.noties.markwon:recycler:$markwon_version",
|
||||
"io.noties.markwon:recycler-table:$markwon_version",
|
||||
"io.noties.markwon:simple-ext:$markwon_version",
|
||||
"io.noties.markwon:syntax-highlight:$markwon_version",
|
||||
"io.noties.markwon:linkify:$markwon_version",
|
||||
"com.caverock:androidsvg:1.4",
|
||||
"pl.droidsonroids.gif:android-gif-drawable:1.2.14",
|
||||
"io.noties:prism4j:2.0.0"
|
||||
]
|
||||
|
||||
|
||||
@ -1,26 +1,14 @@
|
||||
package com.fastaccess.markdown
|
||||
|
||||
import android.text.method.ScrollingMovementMethod
|
||||
import android.widget.TextView
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.core.view.doOnPreDraw
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.ext.latex.JLatexMathPlugin
|
||||
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
|
||||
import io.noties.markwon.ext.tables.TablePlugin
|
||||
import io.noties.markwon.ext.tasklist.TaskListPlugin
|
||||
import io.noties.markwon.html.HtmlPlugin
|
||||
import io.noties.markwon.image.glide.GlideImagesPlugin
|
||||
import io.noties.markwon.movement.MovementMethodPlugin
|
||||
import io.noties.markwon.syntax.Prism4jThemeDarkula
|
||||
import io.noties.markwon.syntax.Prism4jThemeDefault
|
||||
import io.noties.markwon.syntax.SyntaxHighlightPlugin
|
||||
import io.noties.prism4j.Prism4j
|
||||
import io.noties.prism4j.annotations.PrismBundle
|
||||
import net.nightwhistler.htmlspanner.HtmlSpanner
|
||||
import org.commonmark.parser.Parser
|
||||
import org.commonmark.renderer.html.HtmlRenderer
|
||||
|
||||
|
||||
/**
|
||||
* Created by Kosh on 02.02.19.
|
||||
*/
|
||||
@ -40,7 +28,7 @@ object MarkdownProvider {
|
||||
private const val PARAGRAPH_END = "</p>"
|
||||
|
||||
fun loadIntoTextView(
|
||||
htmlSpanner: HtmlSpanner,
|
||||
markwon: Markwon,
|
||||
textView: TextView,
|
||||
html: String,
|
||||
windowBackground: Int,
|
||||
@ -49,10 +37,10 @@ object MarkdownProvider {
|
||||
) {
|
||||
val width = textView.measuredWidth
|
||||
if (width > 0) {
|
||||
initTextView(width, htmlSpanner, textView, html, windowBackground, isLightTheme, onLinkClicked)
|
||||
initTextView(width, markwon, textView, html, windowBackground, isLightTheme, onLinkClicked)
|
||||
} else {
|
||||
textView.doOnPreDraw {
|
||||
initTextView(textView.width, htmlSpanner, textView, html, windowBackground, isLightTheme, onLinkClicked)
|
||||
initTextView(textView.width, markwon, textView, html, windowBackground, isLightTheme, onLinkClicked)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -67,94 +55,13 @@ object MarkdownProvider {
|
||||
|
||||
private fun initTextView(
|
||||
width: Int,
|
||||
htmlSpanner: HtmlSpanner,
|
||||
markwon: Markwon,
|
||||
textView: TextView,
|
||||
html: String,
|
||||
windowBackground: Int,
|
||||
isLightTheme: Boolean,
|
||||
onLinkClicked: ((link: String) -> Unit)? = null
|
||||
) {
|
||||
val context = textView.context
|
||||
Markwon.builder(context)
|
||||
.usePlugin(JLatexMathPlugin.create(textView.textSize - 20))
|
||||
.usePlugin(TaskListPlugin.create(context))
|
||||
.usePlugin(HtmlPlugin.create())
|
||||
.usePlugin(GlideImagesPlugin.create(context))
|
||||
.usePlugin(TablePlugin.create(context))
|
||||
.usePlugin(StrikethroughPlugin.create())
|
||||
.usePlugin(MovementMethodPlugin.create(ScrollingMovementMethod.getInstance()))
|
||||
.usePlugin(
|
||||
SyntaxHighlightPlugin.create(
|
||||
Prism4j(GrammarLocatorDef()), if (isLightTheme) {
|
||||
Prism4jThemeDefault.create()
|
||||
} else {
|
||||
Prism4jThemeDarkula.create()
|
||||
}
|
||||
)
|
||||
)
|
||||
.build()
|
||||
.setMarkdown(textView, html)
|
||||
}
|
||||
|
||||
//https://github.com/k0shk0sh/GitHubSdk/blob/master/library/src/main/java/com/meisolsson/githubsdk/core/HtmlUtils.java
|
||||
private fun format(html: String): CharSequence {
|
||||
if (html.isEmpty()) return ""
|
||||
val formatted = StringBuilder(html)
|
||||
strip(formatted, TOGGLE_START, TOGGLE_END)
|
||||
strip(formatted, SIGNATURE_START, SIGNATURE_END)
|
||||
strip(formatted, REPLY_START, REPLY_END)
|
||||
strip(formatted, HIDDEN_REPLY_START, HIDDEN_REPLY_END)
|
||||
if (replace(formatted, PARAGRAPH_START, BREAK)) replace(formatted, PARAGRAPH_END, BREAK)
|
||||
trim(formatted)
|
||||
return formatted
|
||||
}
|
||||
|
||||
private fun strip(
|
||||
input: StringBuilder,
|
||||
prefix: String,
|
||||
suffix: String
|
||||
) {
|
||||
var start = input.indexOf(prefix)
|
||||
while (start != -1) {
|
||||
var end = input.indexOf(suffix, start + prefix.length)
|
||||
if (end == -1)
|
||||
end = input.length
|
||||
input.delete(start, end + suffix.length)
|
||||
start = input.indexOf(prefix, start)
|
||||
}
|
||||
}
|
||||
|
||||
private fun replace(
|
||||
input: StringBuilder,
|
||||
from: String,
|
||||
to: String
|
||||
): Boolean {
|
||||
var start = input.indexOf(from)
|
||||
if (start == -1) return false
|
||||
val fromLength = from.length
|
||||
val toLength = to.length
|
||||
while (start != -1) {
|
||||
input.replace(start, start + fromLength, to)
|
||||
start = input.indexOf(from, start + toLength)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun trim(input: StringBuilder) {
|
||||
var length = input.length
|
||||
val breakLength = BREAK.length
|
||||
while (length > 0) {
|
||||
if (input.indexOf(BREAK) == 0)
|
||||
input.delete(0, breakLength)
|
||||
else if (length >= breakLength && input.lastIndexOf(BREAK) == length - breakLength)
|
||||
input.delete(length - breakLength, length)
|
||||
else if (Character.isWhitespace(input[0]))
|
||||
input.deleteCharAt(0)
|
||||
else if (Character.isWhitespace(input[length - 1]))
|
||||
input.deleteCharAt(length - 1)
|
||||
else
|
||||
break
|
||||
length = input.length
|
||||
}
|
||||
markwon.setMarkdown(textView, html)
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
package com.fastaccess.markdown.extension
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
import net.nightwhistler.htmlspanner.spans.FontFamilySpan
|
||||
|
||||
/**
|
||||
* Created by Kosh on 02.02.19.
|
||||
*/
|
||||
|
||||
fun TagNodeHandler.getFontFamilySpan(builder: SpannableStringBuilder, start: Int, end: Int): FontFamilySpan? {
|
||||
val spans = builder.getSpans(start, end, FontFamilySpan::class.java) as Array<FontFamilySpan>
|
||||
return if (spans.isNotEmpty()) spans[spans.size - 1] else null
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package com.fastaccess.github.platform.markwon.emoji
|
||||
package com.fastaccess.markdown.extension.markwon.emoji
|
||||
|
||||
import org.commonmark.node.CustomNode
|
||||
import org.commonmark.node.Delimited
|
||||
@ -14,6 +14,8 @@ class Emoji : CustomNode(), Delimited {
|
||||
|
||||
override fun getClosingDelimiter(): String = DELIMITER
|
||||
|
||||
override fun toString(): String = emoji ?: "no emoji"
|
||||
|
||||
companion object {
|
||||
private const val DELIMITER = ":"
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
package com.fastaccess.github.platform.markwon.emoji
|
||||
package com.fastaccess.markdown.extension.markwon.emoji
|
||||
|
||||
import com.fastaccess.github.platform.markwon.emoji.internal.EmojiDelimiterProcessor
|
||||
import com.fastaccess.github.platform.markwon.emoji.internal.EmojiNodeRenderer
|
||||
import com.fastaccess.markdown.extension.markwon.emoji.internal.EmojiDelimiterProcessor
|
||||
import com.fastaccess.markdown.extension.markwon.emoji.internal.EmojiNodeRenderer
|
||||
import org.commonmark.Extension
|
||||
import org.commonmark.parser.Parser
|
||||
import org.commonmark.renderer.html.HtmlRenderer
|
||||
@ -0,0 +1,33 @@
|
||||
package com.fastaccess.markdown.extension.markwon.emoji
|
||||
|
||||
import android.text.SpannedString
|
||||
import com.fastaccess.markdown.emoji.EmojiManager
|
||||
import io.noties.markwon.AbstractMarkwonPlugin
|
||||
import io.noties.markwon.MarkwonVisitor
|
||||
import org.commonmark.parser.Parser
|
||||
import timber.log.Timber
|
||||
|
||||
|
||||
class EmojiPlugin : AbstractMarkwonPlugin() {
|
||||
|
||||
override fun configureParser(builder: Parser.Builder) {
|
||||
builder.extensions(setOf(EmojiExtension.create()))
|
||||
}
|
||||
|
||||
override fun configureVisitor(builder: MarkwonVisitor.Builder) {
|
||||
builder.on(Emoji::class.java) { visitor, emoji ->
|
||||
val length = visitor.length()
|
||||
val emojiUnicode = emoji.emoji
|
||||
val unicode = EmojiManager.getForAlias(emoji.emoji)?.unicode
|
||||
if (!unicode.isNullOrEmpty()) {
|
||||
visitor.setSpans(length, SpannedString(unicode))
|
||||
} else {
|
||||
Timber.e(emojiUnicode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun create() = EmojiPlugin()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package com.fastaccess.markdown.extension.markwon.emoji
|
||||
|
||||
import android.text.SpannedString
|
||||
import com.fastaccess.markdown.emoji.EmojiManager
|
||||
import io.noties.markwon.MarkwonConfiguration
|
||||
import io.noties.markwon.Prop
|
||||
import io.noties.markwon.RenderProps
|
||||
import io.noties.markwon.SpanFactory
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Created by Kosh on 2019-07-20.
|
||||
*/
|
||||
class EmojiSpanFactory : SpanFactory {
|
||||
|
||||
override fun getSpans(
|
||||
configuration: MarkwonConfiguration,
|
||||
props: RenderProps
|
||||
): Any? {
|
||||
val emoji = props.get<Emoji>(Prop.of(":"))
|
||||
Timber.e("$props $emoji")
|
||||
if (emoji != null) {
|
||||
val unicode = EmojiManager.getForAlias(emoji.emoji)
|
||||
if (unicode?.unicode != null) {
|
||||
return SpannedString(unicode.unicode)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
package com.fastaccess.github.platform.markwon.emoji.internal
|
||||
package com.fastaccess.markdown.extension.markwon.emoji.internal
|
||||
|
||||
|
||||
import com.fastaccess.github.platform.markwon.emoji.Emoji
|
||||
import com.fastaccess.markdown.extension.markwon.emoji.Emoji
|
||||
import org.commonmark.node.Text
|
||||
import org.commonmark.parser.delimiter.DelimiterProcessor
|
||||
import org.commonmark.parser.delimiter.DelimiterRun
|
||||
@ -1,6 +1,6 @@
|
||||
package com.fastaccess.github.platform.markwon.emoji.internal
|
||||
package com.fastaccess.markdown.extension.markwon.emoji.internal
|
||||
|
||||
import com.fastaccess.github.platform.markwon.emoji.Emoji
|
||||
import com.fastaccess.markdown.extension.markwon.emoji.Emoji
|
||||
import org.commonmark.node.Node
|
||||
import org.commonmark.renderer.NodeRenderer
|
||||
import org.commonmark.renderer.html.HtmlNodeRendererContext
|
||||
@ -1,51 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import android.graphics.Typeface
|
||||
import android.text.Layout
|
||||
import android.text.TextPaint
|
||||
import android.text.style.LeadingMarginSpan
|
||||
import android.text.style.LineBackgroundSpan
|
||||
import android.text.style.MetricAffectingSpan
|
||||
|
||||
class CodeBackgroundRoundedSpan constructor(
|
||||
private val color: Int
|
||||
) : MetricAffectingSpan(), LeadingMarginSpan, LineBackgroundSpan {
|
||||
|
||||
private val rect = RectF()
|
||||
|
||||
override fun updateMeasureState(paint: TextPaint) {
|
||||
apply(paint)
|
||||
}
|
||||
|
||||
override fun updateDrawState(paint: TextPaint) {
|
||||
apply(paint)
|
||||
}
|
||||
|
||||
private fun apply(paint: TextPaint) {
|
||||
paint.typeface = Typeface.MONOSPACE
|
||||
}
|
||||
|
||||
override fun drawBackground(
|
||||
c: Canvas, p: Paint, left: Int, right: Int, top: Int, baseline: Int, bottom: Int,
|
||||
text: CharSequence, start: Int, end: Int, lnum: Int
|
||||
) {
|
||||
val style = p.style
|
||||
val color = p.color
|
||||
p.style = Paint.Style.FILL
|
||||
p.color = this.color
|
||||
rect.set(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
|
||||
c.drawRect(rect, p)
|
||||
p.color = color
|
||||
p.style = style
|
||||
}
|
||||
|
||||
override fun getLeadingMargin(first: Boolean) = 30
|
||||
|
||||
override fun drawLeadingMargin(
|
||||
c: Canvas, p: Paint, x: Int, dir: Int, top: Int, baseline: Int, bottom: Int,
|
||||
text: CharSequence, start: Int, end: Int, first: Boolean, layout: Layout
|
||||
) = Unit
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import android.text.style.ImageSpan
|
||||
import android.widget.TextView
|
||||
import com.fastaccess.markdown.spans.drawable.DrawableGetter
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
import org.htmlcleaner.ContentNode
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
/**
|
||||
* Created by Kosh on 22 Apr 2017, 1:09 PM
|
||||
*/
|
||||
|
||||
class DrawableHandler(
|
||||
private val textView: TextView,
|
||||
private val width: Int = 0
|
||||
) : TagNodeHandler() {
|
||||
override fun beforeChildren(node: TagNode?, builder: SpannableStringBuilder?, spanStack: SpanStack?) {
|
||||
super.beforeChildren(node, builder, spanStack)
|
||||
node?.addChild(ContentNode("\n"))
|
||||
}
|
||||
|
||||
override fun handleTagNode(node: TagNode?, builder: SpannableStringBuilder?, start: Int, end: Int, spanStack: SpanStack?) {
|
||||
node?.let { n ->
|
||||
builder?.let { b ->
|
||||
val src = n.getAttributeByName("fallback-src") ?: n.getAttributeByName("data-canonical-src") ?: n.getAttributeByName("src")
|
||||
if (!src.isNullOrEmpty()) {
|
||||
b.append("")
|
||||
b.append("\n")
|
||||
val imageGetter = DrawableGetter(textView, width, n.getAttributeByName("src") ?: src)
|
||||
b.setSpan(ImageSpan(imageGetter.getDrawable(src)), start, b.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
// b.setSpan(CenterSpan(), start, b.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) // no center for now
|
||||
b.append("\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import com.fastaccess.markdown.emoji.EmojiManager
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
/**
|
||||
* Created by Kosh on 27 May 2017, 4:54 PM
|
||||
*/
|
||||
|
||||
class EmojiHandler : TagNodeHandler() {
|
||||
override fun handleTagNode(node: TagNode?, builder: SpannableStringBuilder?, start: Int, end: Int, spanStack: SpanStack?) {
|
||||
val emoji = node?.getAttributeByName("alias")
|
||||
if (emoji != null) {
|
||||
val unicode = EmojiManager.getForAlias(emoji)
|
||||
if (unicode?.unicode != null) {
|
||||
builder?.replace(start, end, " " + unicode.unicode + " ")
|
||||
}
|
||||
} else if (node?.text != null) {
|
||||
val unicode = EmojiManager.getForAlias(node.text.toString())
|
||||
if (unicode?.unicode != null) {
|
||||
builder?.replace(start, end, " " + unicode.unicode + " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.text.ParcelableSpan
|
||||
import android.text.TextPaint
|
||||
import android.text.style.StyleSpan
|
||||
|
||||
class FontSpan(
|
||||
private val size: Float,
|
||||
style: Int
|
||||
) : StyleSpan(style), ParcelableSpan {
|
||||
|
||||
override fun updateMeasureState(p: TextPaint) {
|
||||
super.updateMeasureState(p)
|
||||
p.textSize = p.textSize * size
|
||||
}
|
||||
|
||||
override fun updateDrawState(tp: TextPaint) {
|
||||
super.updateDrawState(tp)
|
||||
updateMeasureState(tp)
|
||||
}
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.RelativeSizeSpan
|
||||
import com.fastaccess.markdown.extension.getFontFamilySpan
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
import net.nightwhistler.htmlspanner.spans.FontFamilySpan
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
/**
|
||||
* Created by Kosh on 29.09.17.
|
||||
*/
|
||||
class HeaderHandler(private val size: Float) : TagNodeHandler() {
|
||||
|
||||
|
||||
override fun beforeChildren(node: TagNode?, builder: SpannableStringBuilder?, spanStack: SpanStack?) {
|
||||
builder?.let { appendNewLine(it) }
|
||||
}
|
||||
|
||||
override fun handleTagNode(node: TagNode?, builder: SpannableStringBuilder?, start: Int, end: Int, spanStack: SpanStack?) {
|
||||
builder?.let { b ->
|
||||
b.setSpan(RelativeSizeSpan(this.size), start, end, 33)
|
||||
val originalSpan = getFontFamilySpan(b, start, end)
|
||||
val boldSpan: FontFamilySpan
|
||||
if (originalSpan == null) {
|
||||
boldSpan = FontFamilySpan(spanner.fontResolver.defaultFont)
|
||||
} else {
|
||||
boldSpan = FontFamilySpan(originalSpan.fontFamily)
|
||||
boldSpan.isItalic = originalSpan.isItalic
|
||||
}
|
||||
|
||||
boldSpan.isBold = true
|
||||
b.setSpan(boldSpan, start, end, 33)
|
||||
appendNewLine(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
import net.nightwhistler.htmlspanner.spans.CenterSpan
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
/**
|
||||
* Created by kosh on 30/07/2017.
|
||||
*/
|
||||
|
||||
class HrHandler(
|
||||
private val color: Int = 0,
|
||||
private val width: Int = 0
|
||||
) : TagNodeHandler() {
|
||||
|
||||
override fun handleTagNode(node: TagNode?, builder: SpannableStringBuilder?, start: Int, end: Int, spanStack: SpanStack?) {
|
||||
builder?.let { spannableStringBuilder ->
|
||||
appendNewLine(spannableStringBuilder)
|
||||
val b = SpannableStringBuilder("$")
|
||||
val hrSpan = HrSpan(color, width)
|
||||
b.setSpan(hrSpan, 0, b.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
b.setSpan(CenterSpan(), 0, b.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
appendNewLine(b)
|
||||
spannableStringBuilder.append(b)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.text.style.LineHeightSpan
|
||||
import android.text.style.ReplacementSpan
|
||||
|
||||
class HrSpan internal constructor(
|
||||
private val color: Int,
|
||||
private val width: Int
|
||||
) : ReplacementSpan(), LineHeightSpan {
|
||||
|
||||
override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int {
|
||||
return paint.measureText(text, start, end).toInt()
|
||||
}
|
||||
|
||||
override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int,
|
||||
y: Int, bottom: Int, paint: Paint) {
|
||||
val currentColor = paint.color
|
||||
paint.color = color
|
||||
paint.style = Paint.Style.FILL
|
||||
val height = 10
|
||||
canvas.drawRect(Rect(0, bottom - height, x.toInt() + width, bottom), paint)
|
||||
paint.color = currentColor
|
||||
}
|
||||
|
||||
override fun chooseHeight(text: CharSequence, start: Int, end: Int, spanstartv: Int, v: Int, fm: Paint.FontMetricsInt) {
|
||||
fm.top /= 3
|
||||
fm.ascent /= 3
|
||||
fm.bottom /= 3
|
||||
fm.descent /= 3
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.text.SpannableStringBuilder
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
/**
|
||||
* Created by Kosh on 06 May 2017, 11:02 AM
|
||||
*/
|
||||
|
||||
class ItalicHandler : TagNodeHandler() {
|
||||
override fun handleTagNode(node: TagNode?, builder: SpannableStringBuilder?, start: Int, end: Int, spanStack: SpanStack?) {
|
||||
builder?.setSpan(FontSpan(1f, Typeface.ITALIC), start, builder.length, 33)
|
||||
}
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.graphics.Color
|
||||
import android.text.SpannableStringBuilder
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
/**
|
||||
* Created by Kosh on 10 May 2017, 8:46 PM
|
||||
*/
|
||||
|
||||
class LinkHandler : TagNodeHandler() {
|
||||
|
||||
override fun handleTagNode(
|
||||
node: TagNode,
|
||||
builder: SpannableStringBuilder,
|
||||
start: Int,
|
||||
end: Int,
|
||||
spanStack: SpanStack
|
||||
) {
|
||||
val href = node.getAttributeByName("href")
|
||||
val url = if (!href.isNullOrEmpty()) {
|
||||
href.toString()
|
||||
} else if (!node.text.isNullOrEmpty()) {
|
||||
"https://github.com/${node.text}"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
url?.let { builder.setSpan(UrlSpan(href, linkColor), start, end, 33) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val linkColor = Color.parseColor("#4078C0")
|
||||
}
|
||||
}
|
||||
@ -1,79 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.SpannableStringBuilder
|
||||
|
||||
import com.fastaccess.markdown.widget.SpannableBuilder
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
class ListsHandler(
|
||||
private val checked: Drawable? = null,
|
||||
private val unchecked: Drawable? = null
|
||||
|
||||
) : TagNodeHandler() {
|
||||
private fun getMyIndex(node: TagNode): Int {
|
||||
if (node.parent == null) {
|
||||
return -1
|
||||
} else {
|
||||
var i = 1
|
||||
for (child in node.parent.children) {
|
||||
if (child === node) {
|
||||
return i
|
||||
}
|
||||
if (child is TagNode) {
|
||||
if ("li" == child.name) {
|
||||
++i
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
private fun getParentName(node: TagNode): String? {
|
||||
return if (node.parent == null) null else node.parent.name
|
||||
}
|
||||
|
||||
override fun beforeChildren(node: TagNode?, builder: SpannableStringBuilder?, spanStack: SpanStack?) {
|
||||
node?.let { n ->
|
||||
var todoItem: TodoItems? = null
|
||||
if (n.childTags?.isNotEmpty() == true) {
|
||||
for (tagNode in n.childTags) {
|
||||
if (tagNode.name != null && tagNode.name == "input") {
|
||||
todoItem = TodoItems()
|
||||
todoItem.isChecked = tagNode.getAttributeByName("checked") != null
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
when {
|
||||
"ol" == getParentName(n) -> builder?.append(getMyIndex(n).toString())?.append(". ")
|
||||
"ul" == getParentName(n) -> if (todoItem != null) {
|
||||
if (checked == null || unchecked == null) {
|
||||
builder?.append(if (todoItem.isChecked) "☑" else "☐")
|
||||
} else {
|
||||
builder?.append(SpannableBuilder.builder()
|
||||
.append(if (todoItem.isChecked) checked else unchecked))
|
||||
?.append(" ")
|
||||
}
|
||||
} else {
|
||||
builder?.append("\u2022 ")
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleTagNode(node: TagNode?, builder: SpannableStringBuilder?, start: Int, end: Int, spanStack: SpanStack?) {
|
||||
builder?.let(this::appendNewLine)
|
||||
}
|
||||
|
||||
internal class TodoItems {
|
||||
var isChecked: Boolean = false
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.LeadingMarginSpan
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
/**
|
||||
* Created by Kosh on 29 Apr 2017, 11:59 PM
|
||||
*/
|
||||
|
||||
class MarginHandler : TagNodeHandler() {
|
||||
|
||||
override fun beforeChildren(node: TagNode?, builder: SpannableStringBuilder?, spanStack: SpanStack?) {
|
||||
builder?.let {
|
||||
if (builder.isNotEmpty() && builder[builder.length - 1].toInt() != 10) { //'10 = \n'
|
||||
this.appendNewLine(builder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleTagNode(node: TagNode?, builder: SpannableStringBuilder?, start: Int, end: Int, spanStack: SpanStack?) {
|
||||
builder?.let {
|
||||
builder.setSpan(LeadingMarginSpan.Standard(30), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
appendNewLine(builder)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,55 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.text.Layout
|
||||
import android.text.style.LeadingMarginSpan
|
||||
import androidx.annotation.ColorInt
|
||||
import com.fastaccess.github.extensions.toPx
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Created by Kosh on 2019-06-30.
|
||||
*/
|
||||
class MdQouteSpan(
|
||||
@ColorInt private val paintColor: Int
|
||||
) : LeadingMarginSpan {
|
||||
|
||||
private val rect = Rect()
|
||||
private val paint = Paint()
|
||||
|
||||
override fun getLeadingMargin(first: Boolean): Int = LEADING_MARGIN.toPx()
|
||||
override fun drawLeadingMargin(
|
||||
c: Canvas?,
|
||||
p: Paint?,
|
||||
x: Int,
|
||||
dir: Int,
|
||||
top: Int,
|
||||
baseline: Int,
|
||||
bottom: Int,
|
||||
text: CharSequence?,
|
||||
start: Int,
|
||||
end: Int,
|
||||
first: Boolean,
|
||||
layout: Layout?
|
||||
) {
|
||||
val w = WIDTH.toPx()
|
||||
paint.set(p)
|
||||
paint.style = Paint.Style.FILL
|
||||
paint.color = paintColor
|
||||
val leftWithMargin = x + dir * w
|
||||
val rightWithMargin = leftWithMargin + dir * w
|
||||
val left = min(leftWithMargin, rightWithMargin)
|
||||
val right = max(leftWithMargin, rightWithMargin)
|
||||
rect.set(left, top, right, bottom)
|
||||
c?.drawRect(rect, paint)
|
||||
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val LEADING_MARGIN = 24
|
||||
private const val WIDTH = 4
|
||||
}
|
||||
}
|
||||
@ -1,82 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.graphics.Color
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
import android.text.style.BackgroundColorSpan
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.TypefaceSpan
|
||||
import com.fastaccess.github.extensions.isTrue
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
import net.nightwhistler.htmlspanner.TextUtil
|
||||
import net.nightwhistler.htmlspanner.handlers.PreHandler
|
||||
import org.htmlcleaner.ContentNode
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
/**
|
||||
* Created by Kosh on 22 Apr 2017, 1:07 PM
|
||||
*/
|
||||
class PreTagHandler(
|
||||
private val color: Int = 0,
|
||||
private val isPre: Boolean = false,
|
||||
private val isLightTheme: Boolean = true
|
||||
) : PreHandler() {
|
||||
|
||||
private fun getPlainText(buffer: StringBuffer, node: Any?) {
|
||||
if (node is ContentNode) {
|
||||
val text = TextUtil.replaceHtmlEntities(node.content.toString(), true)
|
||||
buffer.append(text)
|
||||
} else if (node is TagNode) {
|
||||
for (child in node.allChildren) {
|
||||
getPlainText(buffer, child)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun replace(text: String): String {
|
||||
return text.replace(" ".toRegex(), "\u00A0")
|
||||
.replace("&", "&", true)
|
||||
.replace(""", "\"", true)
|
||||
.replace("¢", "¢", true)
|
||||
.replace("<", "<", true)
|
||||
.replace(">", ">", true)
|
||||
.replace("§", "§", true)
|
||||
.replace("“", "“", true)
|
||||
.replace("”", "”", true)
|
||||
.replace("‘", "‘", true)
|
||||
.replace("’", "’", true)
|
||||
.replace("–", "\u2013", true)
|
||||
.replace("—", "\u2014", true)
|
||||
.replace("―", "\u2015", true)
|
||||
}
|
||||
|
||||
override fun beforeChildren(node: TagNode?, builder: SpannableStringBuilder?, spanStack: SpanStack?) {
|
||||
super.beforeChildren(node, builder, spanStack)
|
||||
isPre.isTrue { node?.addChild(ContentNode("\n")) } // append fake hr
|
||||
}
|
||||
|
||||
override fun handleTagNode(node: TagNode?, builder: SpannableStringBuilder?, start: Int, end: Int, spanStack: SpanStack?) {
|
||||
builder?.let {
|
||||
if (isPre) {
|
||||
val buffer = StringBuffer()
|
||||
buffer.append("\n")//fake padding top + make sure, pre is always by itself
|
||||
getPlainText(buffer, node)
|
||||
builder.append(replace(buffer.toString()))
|
||||
this.appendNewLine(builder)
|
||||
builder.setSpan(CodeBackgroundRoundedSpan(color), start, builder.length, SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
} else {
|
||||
val text = node?.text ?: ""
|
||||
builder.append(" ")
|
||||
builder.append(replace(text.toString()))
|
||||
builder.append(" ")
|
||||
val stringStart = start + 1
|
||||
val stringEnd = builder.length - 1
|
||||
builder.setSpan(BackgroundColorSpan(color), stringStart, stringEnd, SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
if (isLightTheme) {
|
||||
builder.setSpan(ForegroundColorSpan(Color.RED), stringStart, stringEnd, SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
builder.setSpan(TypefaceSpan("monospace"), stringStart, stringEnd, SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
/**
|
||||
* Created by Kosh on 23 Apr 2017, 11:30 AM
|
||||
*/
|
||||
|
||||
class QuoteHandler(private val color: Int = 0) : TagNodeHandler() {
|
||||
override fun handleTagNode(
|
||||
node: TagNode?,
|
||||
builder: SpannableStringBuilder?,
|
||||
start: Int,
|
||||
end: Int,
|
||||
spanStack: SpanStack?
|
||||
) {
|
||||
builder?.setSpan(MdQouteSpan(color), if (start > builder.length - 1) start + 1 else start, builder.length - 1, 33)
|
||||
}
|
||||
}
|
||||
@ -1,14 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.StrikethroughSpan
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
class StrikethroughHandler : TagNodeHandler() {
|
||||
|
||||
override fun handleTagNode(node: TagNode?, builder: SpannableStringBuilder?, start: Int, end: Int, spanStack: SpanStack?) {
|
||||
builder?.setSpan(StrikethroughSpan(), start, end, 33)
|
||||
}
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.RelativeSizeSpan
|
||||
import android.text.style.SubscriptSpan
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
class SubScriptHandler : TagNodeHandler() {
|
||||
override fun handleTagNode(node: TagNode?, builder: SpannableStringBuilder?, start: Int, end: Int, spanStack: SpanStack) {
|
||||
builder?.let {
|
||||
builder.setSpan(SubscriptSpan(), start, end, 33)
|
||||
builder.setSpan(RelativeSizeSpan(0.8f), start, end, 33)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.RelativeSizeSpan
|
||||
import android.text.style.SuperscriptSpan
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
class SuperScriptHandler : TagNodeHandler() {
|
||||
|
||||
override fun handleTagNode(node: TagNode?, builder: SpannableStringBuilder?, start: Int, end: Int, spanStack: SpanStack?) {
|
||||
builder?.let {
|
||||
builder.setSpan(SuperscriptSpan(), start, end, 33)
|
||||
builder.setSpan(RelativeSizeSpan(0.8f), start, end, 33)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.style.UnderlineSpan
|
||||
import net.nightwhistler.htmlspanner.SpanStack
|
||||
|
||||
import net.nightwhistler.htmlspanner.TagNodeHandler
|
||||
|
||||
import org.htmlcleaner.TagNode
|
||||
|
||||
class UnderlineHandler : TagNodeHandler() {
|
||||
override fun handleTagNode(node: TagNode?, builder: SpannableStringBuilder?, start: Int, end: Int, spanStack: SpanStack?) {
|
||||
builder?.setSpan(UnderlineSpan(), start, end, 33)
|
||||
}
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
package com.fastaccess.markdown.spans
|
||||
|
||||
import android.text.TextPaint
|
||||
import android.text.style.URLSpan
|
||||
|
||||
/**
|
||||
* Created by Kosh on 2019-06-30.
|
||||
*/
|
||||
class UrlSpan (
|
||||
url: String,
|
||||
private val textColor: Int
|
||||
) : URLSpan(url) {
|
||||
override fun updateDrawState(textPaint: TextPaint) {
|
||||
super.updateDrawState(textPaint)
|
||||
textPaint.color = textColor
|
||||
textPaint.isUnderlineText = false
|
||||
}
|
||||
}
|
||||
@ -1,68 +0,0 @@
|
||||
package com.fastaccess.markdown.spans.drawable
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.Html
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.NonNull
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.load.resource.gif.GifDrawable
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.fastaccess.github.extensions.getDrawableCompat
|
||||
import com.fastaccess.markdown.R
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Created by Kosh on 22 Apr 2017, 7:44 PM
|
||||
*/
|
||||
|
||||
class DrawableGetter(
|
||||
private val tv: TextView?,
|
||||
private val width: Int,
|
||||
private val url: String
|
||||
) : Html.ImageGetter, Drawable.Callback {
|
||||
private val cachedTargets = hashSetOf<GlideDrawableTarget<out Drawable>>()
|
||||
|
||||
init {
|
||||
tv?.setTag(R.id.drawable_callback, this)
|
||||
}
|
||||
|
||||
override fun getDrawable(oriUrl: String): Drawable {
|
||||
val urlDrawable = UrlDrawable()
|
||||
tv?.let {
|
||||
val context = it.context ?: return urlDrawable
|
||||
val imageTarget = if (oriUrl.endsWith(".gif")) {
|
||||
GlideDrawableTarget<GifDrawable>(urlDrawable, tv, width)
|
||||
} else {
|
||||
GlideDrawableTarget<Drawable>(urlDrawable, tv, width)
|
||||
}
|
||||
Glide.with(it).apply {
|
||||
clear(it)
|
||||
applyDefaultRequestOptions(RequestOptions().diskCacheStrategy(DiskCacheStrategy.ALL)
|
||||
.placeholder(context.getDrawableCompat(R.drawable.ic_image)))
|
||||
if (oriUrl.endsWith(".gif")) {
|
||||
asGif().load(url).into(imageTarget as GlideDrawableTarget<GifDrawable>)
|
||||
} else {
|
||||
asDrawable().load(url).into(imageTarget as GlideDrawableTarget<Drawable>)
|
||||
}
|
||||
}
|
||||
cachedTargets.add(imageTarget)
|
||||
}
|
||||
return urlDrawable
|
||||
}
|
||||
|
||||
override fun invalidateDrawable(@NonNull drawable: Drawable) {
|
||||
tv?.invalidate()
|
||||
}
|
||||
|
||||
override fun scheduleDrawable(@NonNull drawable: Drawable, @NonNull runnable: Runnable, l: Long) {}
|
||||
|
||||
override fun unscheduleDrawable(@NonNull drawable: Drawable, @NonNull runnable: Runnable) {}
|
||||
|
||||
fun clear(@NonNull drawableGetter: DrawableGetter) {
|
||||
Timber.e("clearing......")
|
||||
for (target in drawableGetter.cachedTargets) {
|
||||
tv?.let { Glide.with(it).clear(target) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,67 +0,0 @@
|
||||
package com.fastaccess.markdown.spans.drawable
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.widget.TextView
|
||||
import com.bumptech.glide.load.resource.gif.GifDrawable
|
||||
import com.bumptech.glide.request.target.SimpleTarget
|
||||
import com.bumptech.glide.request.transition.Transition
|
||||
import com.fastaccess.markdown.R
|
||||
|
||||
internal class GlideDrawableTarget<T : Drawable>(
|
||||
private val urlDrawable: UrlDrawable,
|
||||
private val container: TextView?,
|
||||
private val width: Int
|
||||
) : SimpleTarget<T>() {
|
||||
|
||||
override fun onResourceReady(resource: T, transition: Transition<in T>?) {
|
||||
val textView = container
|
||||
textView?.post {
|
||||
val width: Float
|
||||
val height: Float
|
||||
if (resource.intrinsicWidth >= this.width) {
|
||||
val downScale = resource.intrinsicWidth.toFloat() / this.width
|
||||
width = (resource.intrinsicWidth.toDouble() / downScale.toDouble() / 1.3).toFloat()
|
||||
height = (resource.intrinsicHeight.toDouble() / downScale.toDouble() / 1.3).toFloat()
|
||||
} else {
|
||||
val multiplier = this.width.toFloat() / resource.intrinsicWidth
|
||||
width = resource.intrinsicWidth.toFloat() * multiplier
|
||||
height = resource.intrinsicHeight.toFloat() * multiplier
|
||||
}
|
||||
val rect = Rect(0, 0, Math.round(width), Math.round(height))
|
||||
resource.bounds = rect
|
||||
urlDrawable.bounds = rect
|
||||
urlDrawable.setDrawable(resource)
|
||||
urlDrawable.callback = textView.getTag(R.id.drawable_callback) as DrawableGetter
|
||||
if (resource is GifDrawable) {
|
||||
resource.setLoopCount(GifDrawable.LOOP_FOREVER)
|
||||
resource.start()
|
||||
}
|
||||
textView.text = textView.text
|
||||
textView.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLoadStarted(placeholder: Drawable?) {
|
||||
super.onLoadStarted(placeholder)
|
||||
applyTempDrawable(placeholder)
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
super.onLoadFailed(errorDrawable)
|
||||
applyTempDrawable(errorDrawable)
|
||||
}
|
||||
|
||||
private fun applyTempDrawable(placeholder: Drawable?) {
|
||||
placeholder?.let { drawable ->
|
||||
val rect = Rect(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
|
||||
drawable.bounds = rect
|
||||
urlDrawable.bounds = rect
|
||||
urlDrawable.setDrawable(drawable)
|
||||
container?.let {
|
||||
it.text = it.text
|
||||
it.invalidate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
package com.fastaccess.markdown.spans.drawable
|
||||
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.graphics.drawable.Drawable
|
||||
|
||||
import com.bumptech.glide.load.resource.gif.GifDrawable
|
||||
|
||||
class UrlDrawable : BitmapDrawable(), Drawable.Callback {
|
||||
|
||||
private var drawable: Drawable? = null
|
||||
|
||||
override fun draw(canvas: Canvas) {
|
||||
drawable?.let {
|
||||
it.draw(canvas)
|
||||
if (it is GifDrawable) {
|
||||
if (!it.isRunning) it.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun invalidateDrawable(who: Drawable) {
|
||||
if (callback != null) {
|
||||
callback!!.invalidateDrawable(who)
|
||||
}
|
||||
}
|
||||
|
||||
override fun scheduleDrawable(who: Drawable, what: Runnable, `when`: Long) {
|
||||
if (callback != null) {
|
||||
callback!!.scheduleDrawable(who, what, `when`)
|
||||
}
|
||||
}
|
||||
|
||||
override fun unscheduleDrawable(who: Drawable, what: Runnable) {
|
||||
if (callback != null) {
|
||||
callback!!.unscheduleDrawable(who, what)
|
||||
}
|
||||
}
|
||||
|
||||
fun setDrawable(drawable: Drawable) {
|
||||
this.drawable?.callback = this
|
||||
drawable.callback = this
|
||||
this.drawable = drawable
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
package com.fastaccess.markdown.widget
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
|
||||
/**
|
||||
* Created by Kosh on 02.02.19.
|
||||
*/
|
||||
class HtmlTextView constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0
|
||||
) : AppCompatTextView(context, attrs, defStyle) {
|
||||
constructor(context: Context) : this(context, null)
|
||||
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
|
||||
|
||||
fun setHtml(text: String? = null) {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user