0

I'm facing an issue with HtmlTextView, when an image tag is present in response body, textview is not resizing to show the contents.

P.S - On Reopening the same screen, textview is displayed correctly. Does not work only when open for the first time.

Please see the attached code for ImageGetter below,

package com.desidime.editor.editor.render

import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.util.Log
import android.view.View
import android.widget.TextView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.SimpleTarget
import com.bumptech.glide.request.target.Target
import com.bumptech.glide.request.transition.Transition
import com.desidime.editor.editor.Constants
import com.desidime.editor.editor.Constants.dp2px
import com.desidime.editor.editor.inner.Html
import java.lang.ref.WeakReference

class AreImageGetter(private val container: TextView) : Html.ImageGetter {

    override fun getDrawable(source: String): Drawable {
        val urlDrawable = UrlDrawable()

        val target = ImageGetterTarget(urlDrawable, this, container, true, Constants.isEmoji(source),
            Constants.SCREEN_WIDTH)

        Glide.with(container.context)
            .asDrawable()
            .load(source)
            .apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL))
            .into(target)

        return urlDrawable
    }


    private class ImageGetterTarget(urlDrawable: UrlDrawable,
                                    imageGetter: AreImageGetter,
                                    container: View,
                                    private val matchParentWidth: Boolean,
                                    private val isEmoji: Boolean,
                                    maxWidth: Int) : SimpleTarget<Drawable>(maxWidth, Target.SIZE_ORIGINAL) {

        private val drawableReference: WeakReference<UrlDrawable> = WeakReference(urlDrawable)
        private val imageGetterReference: WeakReference<AreImageGetter> = WeakReference(imageGetter)
        private val containerReference: WeakReference<View> = WeakReference(container)
        private var scale: Float = 1f

        private fun getScale(drawable: Drawable): Float {
            val container = containerReference.get()
            if (!matchParentWidth || container == null) {
                return 1f
            }
            val maxWidth = container.width.toFloat() - container.paddingLeft - container.paddingRight
            val originalDrawableWidth = drawable.intrinsicWidth.toFloat()

            println(
                "/*-------------------------------------------------------------------------------------------*/")
            println("maxWidth : $maxWidth")
            println("container.width.toFloat() : ${container.width.toFloat()}")
            println("container.paddingLeft : ${container.paddingLeft}")
            println("container.paddingRight : ${container.paddingRight}")
            println("originalDrawableWidth : $originalDrawableWidth")
            println("drawable.intrinsicWidth.toFloat() : ${drawable.intrinsicWidth.toFloat()}")


            return maxWidth / originalDrawableWidth
        }

        override fun onResourceReady(drawable: Drawable, transition: Transition<in Drawable>?) {
            val urlDrawable = drawableReference.get() ?: return
            if (isEmoji) {
                if (containerReference.get() != null) {
                    val dp2px = dp2px(containerReference.get()?.context, 24f)
                    drawable.setBounds(0, 0, dp2px, dp2px)
                    urlDrawable.setBounds(0, 0, drawable.bounds.right, drawable.bounds.bottom)
                }
            } else {
                scale = getScale(drawable)

                val width: Int = drawable.intrinsicWidth
                val height: Int = drawable.intrinsicHeight

                println("(drawable.intrinsicWidth * scale).toInt() ${(drawable.intrinsicWidth * scale).toInt()}")
                println("(drawable.intrinsicHeight * scale).toInt() ${(drawable.intrinsicHeight * scale).toInt()}")
                println("(drawable.intrinsicHeight)${(drawable.intrinsicHeight)}")
                println("(drawable.intrinsicWidth)${(drawable.intrinsicWidth)}")
                drawable.setBounds(0, 0, (drawable.intrinsicWidth * scale).toInt(),
                    (drawable.intrinsicHeight * scale).toInt())
//                drawable.setBounds(0, 0, width, height)
                // set the correct bound according to the result from HTTP call
                urlDrawable.setBounds(0, 0, (drawable.intrinsicWidth * scale).toInt(),
                    (drawable.intrinsicHeight * scale).toInt())
//                urlDrawable.setBounds(0, 0, width, height)
            }

            // change the reference of the current drawable to the result from the HTTP call
            urlDrawable.drawable = drawable

            val imageGetter = imageGetterReference.get() ?: return

            // redraw the image by invalidating the container
            imageGetter.container.invalidate()

            // re-set text to fix images overlapping text
            imageGetter.container.text = imageGetter.container.text
            println(
                "/*-------------------------------------------------------------------------------------------*/")
        }

    }

    inner class UrlDrawable : BitmapDrawable() {
        var drawable: Drawable? = null

        override fun draw(canvas: Canvas) {
            // override the draw to facilitate refresh function later
            try {
                if (drawable != null) {
                    drawable!!.draw(canvas)
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }
}

Code for image tag

private static void startImg(Editable text, Attributes attributes, Html.ImageGetter img) {
    String src = attributes.getValue("", "src");
    if (src == null) {
        return;
    }
    Drawable d = null;
    ImageSpan imageSpan = null;
    if (img != null) {
        Log.e("HTML", "startImg");
        d = img.getDrawable(src);
        imageSpan = new AreImageSpan(sContext, d, src);
    }

    if (d == null) {
        if (sContext == null) {
            d = Resources.getSystem().getDrawable(R.drawable.ic_image);
        } else {
            d = sContext.getResources().getDrawable(R.drawable.ic_image);
        }

        d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
    }
    Log.e("HTML", imageSpan.getSource());
    int len = text.length();
    text.append('\uFFFC');
    text.setSpan(imageSpan, len, text.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}

Update

Just Found out

d = img.getDrawable(src);

This Line return image with bounds Rect(0, 0 - 0, 0) on first open.

Sam
  • 61
  • 1
  • 9
  • What do you see in the _TextView_ when the drawable isn't displayed properly? Is it just a smaller but full version or is it clipped in some way? Are you sure that you are getting to the line `imageGetter.container.text = imageGetter.container.text`? I [answered](https://stackoverflow.com/a/64739764/6287910) a related question that may be helpful. – Cheticamp Mar 16 '22 at 12:14
  • @Cheticamp Yes, `imageGetter.container.text = imageGetter.container.text` this line is executed. And the content is clipped. – Sam Mar 16 '22 at 12:22
  • That makes sense. I don't believe that the drawable's span is being remeasured and is stuck at the initial value. That is why you are seeing the clipped image. Take a look at the answer I referenced in my last comment. It might help. Another approach (that I haven't tried) would be to run the text through `fromHtml()` again once the drawable is loaded. This is, in essence, what is happening when you restart the app. – Cheticamp Mar 16 '22 at 12:36
  • Yeah, i tried to invalidate the view rendering html but that didn't worked too. – Sam Mar 16 '22 at 12:38
  • @Cheticamp, is that what you suggesting? also have gone through the code, i have edited the question. – Sam Mar 16 '22 at 13:15

1 Answers1

0

The bounds of UrlDrawable should match the bounds of the drawable it contains. I suggest accomplishing this with the following:

inner class UrlDrawable : BitmapDrawable() {
    var drawable: Drawable? = null
        set(value) {
            field = value?.also {
                // Set bounds of the enclosed drawable to its intrinsic size.
                it.bounds = Rect(0, 0, it.intrinsicWidth, it.intrinsicHeight)
            }
            // The wrapper should have the same bounds as the drawable.
            bounds = field?.bounds ?: Rect()
        }

    override fun draw(canvas: Canvas) {
        try {
            drawable?.draw(canvas)
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }
}

Now, when you draw the image, it should have the correct size.

Cheticamp
  • 61,413
  • 10
  • 78
  • 131
  • Hi @Cheticamp i have added the code, but the issue still exist, on initially opening the screen image is clipped and on reopening the screen image is displayed properly. – Sam Mar 21 '22 at 07:35
  • also i have noticed that on upgrading the Facebook login this issue appears. i crossed check the following scenario. – Sam Mar 21 '22 at 07:38
  • i doubt, textview getting attached to window before rendering. – Sam Mar 21 '22 at 10:22