When you have HTML and you want to show it to your users, one of the possibilities is using a WebView. Check the WebView tutorial for more info. You can also use a TextView, but the HTML has to be changed for that. You could use linkify for this. This should be enough, but when you also want (for instance) bold and italic to work, we have to do some more trickery. The first iteration we used was this:

//description is a TextView
description.setText(Html.fromHtml(desc));
description.setMovementMethod(LinkMovementMethod.getInstance());

This works amazingly well, except for this ‘minor’ detail: full links are working perfectly, but a link like this does a force close:

<a href="/dr">Link with relative path</a>

The TextView is not a browser, so it doesn’t even know what protocol we are using, let alone what domain or path we’re supposed to be in. This will result in a force close, with a log that looks like this:

ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.View data=/dr}

The intent expects something like “http://” or “mailto:” I presume, but if it gets “/dr”, there is no activity to handle the link.

Elegance aside, a simple solution is to make an absolute link if you know the correct data. This is a quick and dirty solution, but let’s share it anyway because a lot of solutions we found were specifically for linkify, not for complete “fromhtml” purposes.

/**
 * Removes relative a hrefs
 * @param spantext (from Html.fromhtml())
 * @return spanned with fixed links
 */
public Spanned correctLinkPaths (Spanned spantext) {
  Object[] spans = spantext.getSpans(0, spantext.length(), Object.class);
  for (Object span : spans) {
    int start = spantext.getSpanStart(span);
    int end = spantext.getSpanEnd(span);
    int flags = spantext.getSpanFlags(span);
    if (span instanceof URLSpan) {
      URLSpan urlSpan = (URLSpan) span;
      if (!urlSpan.getURL().startsWith("http")) {
        if (urlSpan.getURL().startsWith("/")) {
          urlSpan = new URLSpan("http://domain+path" + urlSpan.getURL());
        } else {
          urlSpan = new URLSpan("http://domain+path/" + urlSpan.getURL());
        }
      }
      ((Spannable) spantext).removeSpan(span);
      ((Spannable) spantext).setSpan(urlSpan, start, end, flags);
    }
  }
  return spantext;
}

We traverse the objects in the Spanned. If we find a URLSpan we copy the span and fix that one. Then we remove the old span, and insert the new one. The copy move might not be necessary, but as it doesn’t bother us at the moment I’ll leave it like this. This has taken up quite enough time, thankyouverymuch. If you want to do something extra, and instead of opening a browser do your own onClick magic, you can take a look at this anddev forumthread.

Finally, our assign has to look like this obviously:

//description is still a TextView
description.setText(correctLinkPaths(Html.fromHtml(desc)));
description.setMovementMethod(LinkMovementMethod.getInstance());

Another interesting link is this bugreport on code.google.com. I’d say that your browser shouldn’t know where to point itself, but apparently the project members don’t agree.

Comments are closed.