noties / Markwon

Android markdown library (no WebView)
https://noties.io/Markwon/
Apache License 2.0
2.68k stars 299 forks source link

Markwon silently fails to show any image in TextView #377

Open Mek101 opened 2 years ago

Mek101 commented 2 years ago

I'm giving my try to a WYSIWYG Markdown editor. Following the mvvm architecture I decopulated the markdown renderer from the UI fragment and re-implemented part of MarkwonEditorTextWatcher to use Kotlin coroutines in order to render the markdown on a background thread; then following your original implementation, I send the PreRenderResult back to the UI and update the TextView with the following lambda:

// Register to receive the rendering results
// I use MutableLiveData.postValue to call this here, which queues the callback into the main thread loop
editorViewModel.editableData.observe(this.viewLifecycleOwner) { result ->
    if (result !== null && binding !== null) {
        // editorText is a androidx.appcompat.widget.AppCompatEditText
        binding?.editorText?.post {
            binding?.editorText?.text?.let {
                selfChange = true
                // The text editor starts with 0 spans. `dispatchTo` sets them immediately
                // by index, causing an OutOfRange exception.
                if (it.length < result.resultEditable().length) {
                    binding!!.editorText.text = result.resultEditable()
                } else {
                    result.dispatchTo(it)
                }
                selfChange = false
            }
        }
    }
}

And since I use SAF, I implemented a custom scheme handler for android's content uirs:

class ContentUriSchemeHandler(private val application: Application): SchemeHandler() {

    override fun handle(raw: String, uri: Uri): ImageItem {
        if (uri.scheme != SCHEME) {
            throw IllegalArgumentException("Invalid scheme ${uri.scheme}")
        }

        Log.i(ContentUriSchemeHandler::class.simpleName, "Handling $uri")

        val stream = application.contentResolver.openInputStream(uri)
        val drawable = Drawable.createFromStream(stream, uri.toString())
        return ImageItem.withResult(drawable)
    }

    override fun supportedSchemes(): Collection<String> = Collections.singleton("content")

    companion object {
        const val SCHEME = "content"
    }
}

While the Markwon and MarkwonEditor instances are built in the following manner:

val markwon = Markwon.builder(application.applicationContext)
            .usePlugin(SoftBreakAddsNewLinePlugin.create())
            .usePlugin(ImagesPlugin.create {
                it.errorHandler(imageErrorHandlerImpl)
                it.placeholderProvider(imagePlaceholderImpl)
                it.addSchemeHandler(ContentUriSchemeHandler(application))
                it.addSchemeHandler(NetworkSchemeHandler.create())
            })
            .build()
return MarkwonEditor.builder(markwon)
            .punctuationSpan(
                HidePunctuationSpan::class.java
            ) { HidePunctuationSpan() }
            .useEditHandler(EmphasisEditHandler())
            .useEditHandler(StrongEmphasisEditHandler())
            .useEditHandler(StrikethroughEditHandler())
            .useEditHandler(CodeEditHandler())
            .useEditHandler(BlockQuoteEditHandler())
            .useEditHandler(LinkEditHandler { widget, link ->
                linkListener?.invoke(widget, link)
            })
            .useEditHandler(HeadingEditHandler())
            .build()
    }

The problem is that in the end result no image is shown. Adding Log.i() to the various callbacks show that neither the error handler, the place holder provider or the scheme handler are ever called.

The markdown I used to test:

![Local Image](./image.jpg)    // Kinda expected this not to work out of the box with SAF
![Remote Image](https://i.redd.it/yssl1fdzvzy71.jpg)    // Trouble is, this also doesn't work

I already added the internet permissions

Sorry If I've been a bit verbose but I'm not sure if using a different thread for rendering influences the image rendering or they also somehow depend on the rest of the library's architecture(?)

noties commented 2 years ago

Hello @Mek101 ,

you would need to use an edit handler for the image span. But it looks like an edit handler for image won't be possible because of current limitations. The same limitations are applied to tables and latex and come from the fact that they are not represented as text when displayed in a TextView.

Mek101 commented 2 years ago

So I would need some kind of custom spannable?

noties commented 2 years ago

Well, it would be nessesary for a few things more.

Mek101 commented 2 years ago

come from the fact that they are not represented as text when displayed in a TextView.

If markwon uses ImageSpan, then couldn't it simply call getSource() on it to retrieve the text from the span? I'm not sure if I understand the roadblock