mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-07-08 13:35:10 +00:00
Merge pull request #1509 from cvuorinen/904-chat-markdown-images
904 chat markdown images
This commit is contained in:
commit
69a1322b10
3 changed files with 118 additions and 82 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package com.habitrpg.android.habitica.models.social
|
||||
|
||||
import android.renderscript.BaseObj
|
||||
import android.text.Spanned
|
||||
import com.habitrpg.android.habitica.models.BaseObject
|
||||
import com.habitrpg.android.habitica.models.user.Backer
|
||||
import com.habitrpg.android.habitica.models.user.ContributorInfo
|
||||
|
|
@ -32,7 +33,7 @@ open class ChatMessage : RealmObject(), BaseObject {
|
|||
var text: String? = null
|
||||
|
||||
@Ignore
|
||||
var parsedText: CharSequence? = null
|
||||
var parsedText: Spanned? = null
|
||||
|
||||
var timestamp: Long? = null
|
||||
|
||||
|
|
|
|||
|
|
@ -1,80 +1,114 @@
|
|||
package com.habitrpg.android.habitica.ui.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.text.SpannableString
|
||||
import android.text.Spanned
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.widget.TextView
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
|
||||
import io.noties.markwon.image.ImagesPlugin
|
||||
import io.noties.markwon.image.file.FileSchemeHandler
|
||||
import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler
|
||||
import io.noties.markwon.movement.MovementMethodPlugin
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.functions.Consumer
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
|
||||
|
||||
object MarkdownParser {
|
||||
|
||||
internal var markwon: Markwon? = null
|
||||
|
||||
fun setup(context: Context) {
|
||||
markwon = Markwon.builder(context)
|
||||
.usePlugin(StrikethroughPlugin.create())
|
||||
.usePlugin(ImagesPlugin.create {
|
||||
it.addSchemeHandler(OkHttpNetworkSchemeHandler.create())
|
||||
.addSchemeHandler(FileSchemeHandler.createWithAssets(context.assets))
|
||||
})
|
||||
.usePlugin(MovementMethodPlugin.create(LinkMovementMethod.getInstance()))
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses formatted markdown and returns it as styled CharSequence
|
||||
*
|
||||
* @param input Markdown formatted String
|
||||
* @return Stylized CharSequence
|
||||
*/
|
||||
fun parseMarkdown(input: String?): Spanned {
|
||||
if (input == null) {
|
||||
return SpannableString("")
|
||||
}
|
||||
val text = EmojiParser.parseEmojis(input) ?: input
|
||||
return markwon?.toMarkdown(text) ?: SpannableString(text)
|
||||
}
|
||||
|
||||
fun parseMarkdownAsync(input: String?, onSuccess: Consumer<Spanned>) {
|
||||
Single.just(input ?: "")
|
||||
.map { this.parseMarkdown(it) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(onSuccess, RxErrorHandler.handleEmptyError())
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts stylized CharSequence into markdown
|
||||
*
|
||||
* @param input Stylized CharSequence
|
||||
* @return Markdown formatted String
|
||||
*/
|
||||
fun parseCompiled(input: CharSequence): String? {
|
||||
return EmojiParser.convertToCheatCode(input.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun TextView.setMarkdown(input: String?) {
|
||||
MarkdownParser.markwon?.setParsedMarkdown(this, MarkdownParser.parseMarkdown(input))
|
||||
}
|
||||
|
||||
fun TextView.setParsedMarkdown(input: Spanned?) {
|
||||
if (input != null) {
|
||||
MarkdownParser.markwon?.setParsedMarkdown(this, input)
|
||||
} else {
|
||||
text = null
|
||||
}
|
||||
package com.habitrpg.android.habitica.ui.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.text.SpannableString
|
||||
import android.text.Spanned
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.widget.TextView
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import io.noties.markwon.AbstractMarkwonPlugin
|
||||
import io.noties.markwon.Markwon
|
||||
import io.noties.markwon.MarkwonConfiguration
|
||||
import io.noties.markwon.MarkwonPlugin
|
||||
import io.noties.markwon.ext.strikethrough.StrikethroughPlugin
|
||||
import io.noties.markwon.image.ImageSize
|
||||
import io.noties.markwon.image.ImageSizeResolverDef
|
||||
import io.noties.markwon.image.ImagesPlugin
|
||||
import io.noties.markwon.image.file.FileSchemeHandler
|
||||
import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler
|
||||
import io.noties.markwon.movement.MovementMethodPlugin
|
||||
import io.reactivex.rxjava3.core.Single
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.functions.Consumer
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
|
||||
|
||||
object MarkdownParser {
|
||||
|
||||
internal var markwon: Markwon? = null
|
||||
|
||||
fun setup(context: Context) {
|
||||
markwon = Markwon.builder(context)
|
||||
.usePlugin(StrikethroughPlugin.create())
|
||||
.usePlugin(ImagesPlugin.create {
|
||||
it.addSchemeHandler(OkHttpNetworkSchemeHandler.create())
|
||||
.addSchemeHandler(FileSchemeHandler.createWithAssets(context.assets))
|
||||
})
|
||||
.usePlugin(this.createImageSizeResolverScaleDpiPlugin(context))
|
||||
.usePlugin(MovementMethodPlugin.create(LinkMovementMethod.getInstance()))
|
||||
.build()
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom markwon plugin to scale image size according to dpi
|
||||
*/
|
||||
private fun createImageSizeResolverScaleDpiPlugin(context: Context): MarkwonPlugin {
|
||||
return object : AbstractMarkwonPlugin() {
|
||||
override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
|
||||
builder.imageSizeResolver(object : ImageSizeResolverDef() {
|
||||
override fun resolveImageSize(imageSize: ImageSize?, imageBounds: Rect, canvasWidth: Int, textSize: Float): Rect {
|
||||
val dpi = context.resources.displayMetrics.density
|
||||
var width = imageBounds.width()
|
||||
if (dpi > 1) {
|
||||
width = (dpi * width.toFloat()).toInt()
|
||||
}
|
||||
if (width > canvasWidth) {
|
||||
width = canvasWidth
|
||||
}
|
||||
|
||||
val ratio = imageBounds.width().toFloat() / imageBounds.height()
|
||||
val height = (width / ratio + .5f).toInt()
|
||||
|
||||
return Rect(0, 0, width, height)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses formatted markdown and returns it as styled CharSequence
|
||||
*
|
||||
* @param input Markdown formatted String
|
||||
* @return Stylized CharSequence
|
||||
*/
|
||||
fun parseMarkdown(input: String?): Spanned {
|
||||
if (input == null) {
|
||||
return SpannableString("")
|
||||
}
|
||||
val text = EmojiParser.parseEmojis(input) ?: input
|
||||
return markwon?.toMarkdown(text) ?: SpannableString(text)
|
||||
}
|
||||
|
||||
fun parseMarkdownAsync(input: String?, onSuccess: Consumer<Spanned>) {
|
||||
Single.just(input ?: "")
|
||||
.map { this.parseMarkdown(it) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(onSuccess, RxErrorHandler.handleEmptyError())
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts stylized CharSequence into markdown
|
||||
*
|
||||
* @param input Stylized CharSequence
|
||||
* @return Markdown formatted String
|
||||
*/
|
||||
fun parseCompiled(input: CharSequence): String? {
|
||||
return EmojiParser.convertToCheatCode(input.toString())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun TextView.setMarkdown(input: String?) {
|
||||
MarkdownParser.markwon?.setParsedMarkdown(this, MarkdownParser.parseMarkdown(input))
|
||||
}
|
||||
|
||||
fun TextView.setParsedMarkdown(input: Spanned?) {
|
||||
if (input != null) {
|
||||
MarkdownParser.markwon?.setParsedMarkdown(this, input)
|
||||
} else {
|
||||
text = null
|
||||
}
|
||||
}
|
||||
|
|
@ -26,6 +26,7 @@ import io.reactivex.rxjava3.core.Maybe
|
|||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import com.habitrpg.android.habitica.models.members.Member
|
||||
import com.habitrpg.android.habitica.ui.helpers.setParsedMarkdown
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
|
@ -172,7 +173,7 @@ class ChatRecyclerMessageViewHolder(itemView: View, private var userId: String,
|
|||
itemView.setPadding(16.dpToPx(context), itemView.paddingTop, itemView.paddingRight, itemView.paddingBottom)
|
||||
}
|
||||
|
||||
binding.messageText.text = chatMessage?.parsedText
|
||||
binding.messageText.setParsedMarkdown(chatMessage?.parsedText)
|
||||
if (msg.parsedText == null) {
|
||||
binding.messageText.text = chatMessage?.text
|
||||
Maybe.just(chatMessage?.text ?: "")
|
||||
|
|
@ -181,7 +182,7 @@ class ChatRecyclerMessageViewHolder(itemView: View, private var userId: String,
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ parsedText ->
|
||||
chatMessage?.parsedText = parsedText
|
||||
binding.messageText.text = chatMessage?.parsedText
|
||||
binding.messageText.setParsedMarkdown(parsedText)
|
||||
}, { it.printStackTrace() })
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue