androidhtmlandroid-intentandroid-activityclickablespan

Android Handle/Override/Interrupt Intent from the same Activity that fired it


I have an android application that has to parse fairly large amounts of HTML and place it inside one or more TextViews. So any one of these given text views might contain one more html link. These links are created dynamically and embedded in the rest of the text. It seems the only way I can allow these links to work is by using the following code.

TextView textView = new TextView(mContext);
textView.setAutoLinkMask(0);
textView.setLinksClickable(true);
Linkify.addLinks(textView, Patterns.WEB_URL, "ref://");
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.append(getSpannable(htmlString));

This works just fine for any http link, I simply added filter to a WebActivity in my a filter in my manifest and call it a day.

My problem however is that I have links that I want to send with additional data not contained in the link itself. Additionally the the extra data I want to send would change based on the target of the link. I cannot do this because the intents are fired based on the HTML data the user clicks.

Is there any way to modify / intercept/ override an Intent fired by my Activity in order to add additional data to it after it has been fired?

Edit With Answer Based on @Tanis.7x Suggestion In my original question I failed to specify that I was using a Spanned derived from Html.fromHtml(htmlString);

I was able to figured out a solution based on @Tanis.7x answer and this question's accepted answer Android TextView with Clickable Links: how to capture clicks?

TextView textView = new TextView(mContext);
textView.setAutoLinkMask(0);
textView.setLinksClickable(true);
Linkify.addLinks(textView, Patterns.WEB_URL, "ref://");
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.append(getHtmlStringForTextView(Html.fromHtml(htmlString)));

private SpannableStringBuilder getHtmlStringForTextView(String html)
{
    CharSequence sequence = Html.fromHtml(html);
    SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
    URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class);
    for(URLSpan span : urls) {
        makeLinkClickable(strBuilder, span);
    }
    return strBuilder;
}

private void makeLinkClickable(SpannableStringBuilder spannableStringBuilder, final URLSpan span)
{
    int start = spannableStringBuilder.getSpanStart(span);
    int end = spannableStringBuilder.getSpanEnd(span);
    int flags = spannableStringBuilder.getSpanFlags(span);
    ClickableSpan clickable = new ClickableSpan() {
        public void onClick(View view) {
           //handle click here
        }
    };
    spannableStringBuilder.setSpan(clickable, start, end, flags);
    spannableStringBuilder.removeSpan(span);
}

Solution

  • You cannot intercept intents after they are fired.

    If you look at the source of URLSpan (which is responsible for handling links in TextViews), you will see that in it's onClick() method it creates an appropriate Intent, then fires it off with a call to context.startActivity(intent);. At that point in time the Intent is out of your hands and responsibility for handling the Intent lies within the Android framework.

    You can, however, get the behavior you desire. First you need to create your own URLSpan subclass. Override onClick() and handle the click however you want.

    public class CustomUrlSpan extends URLSpan {
        public CustomUrlSpan(String url) {
            super(url);
        }
    
        /* You may want to properly support the ParcelableSpan interface as well */
    
        @Override
        public void onClick(View widget) {
            Uri uri = Uri.parse(getURL());
            Context context = widget.getContext();
            Intent intent = new Intent(Intent.ACTION_VIEW, uri);
            intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName());
    
            // Put your custom intent handling in here
    
            try {
                context.startActivity(intent);
            } catch (ActivityNotFoundException e) {
                Log.w("URLSpan", "Actvity was not found for intent, " + intent.toString());
            }
        }
    }
    

    Then, instead of using Linkify to make your links clickable, you will need to create the spans yourself. Take a look at the Linkify source if you need a starting point- you'll probably want to do something very similar to addLinks(), but with your CustomUrlSpan instead of the standard URLSpan.