Question

Having the textView with autoLinkMask set to Linkify.ALL, i'm able to open the links and the browser shows the web-page.

I need to call another activity that will do it with it's webView not leaving the application.

Important notes:

  • changing the textView content is not an option, i need to have the links displayed as they are with the schemes they have,
  • there's lot of text in the textView, not only the link.

I looked through movementMethod and IntentFilters, might miss something but looks like it can't help.

So, any option to intercept the touched link in the TextView to do something with it not opening the browser ?

If you want to mention this SO question, please give some arguments why cause it doesn't seem to solve the same problem as I have.

Was it helpful?

Solution

Step #1: Create your own subclass of ClickableSpan that does what you want in its onClick() method (e.g., called YourCustomClickableSpan)

Step #2: Run a bulk conversion of all of the URLSpan objects to be YourCustomClickableSpan objects. I have a utility class for this:

public class RichTextUtils {
    public static <A extends CharacterStyle, B extends CharacterStyle> Spannable replaceAll(Spanned original,
    Class<A> sourceType,
    SpanConverter<A, B> converter) {
        SpannableString result=new SpannableString(original);
        A[] spans=result.getSpans(0, result.length(), sourceType);

        for (A span : spans) {
            int start=result.getSpanStart(span);
            int end=result.getSpanEnd(span);
            int flags=result.getSpanFlags(span);

            result.removeSpan(span);
            result.setSpan(converter.convert(span), start, end, flags);
        }

        return(result);
    }

    public interface SpanConverter<A extends CharacterStyle, B extends CharacterStyle> {
        B convert(A span);
    }
}

You would use it like this:

yourTextView.setText(RichTextUtils.replaceAll((Spanned)yourTextView.getText(),
                                             URLSpan.class,
                                             new URLSpanConverter()));

with a custom URLSpanConverter like this:

class URLSpanConverter
      implements
      RichTextUtils.SpanConverter<URLSpan, YourCustomClickableSpan> {
    @Override
    public URLSpan convert(URLSpan span) {
      return(new YourCustomClickableSpan(span.getURL()));
    }
  }

to convert all URLSpan objects to YourCustomClickableSpan objects.

OTHER TIPS

I make @CommonsWare's code more elegant and clear by adding Click Listener which can be added directly into the same method which replace URL Spans.

  1. Define YourCustomClickableSpan Class.

     public static class YourCustomClickableSpan extends ClickableSpan {
    
        private String url;
        private OnClickListener mListener;
    
        public YourCustomClickableSpan(String url, OnClickListener mListener) {
            this.url = url;
            this.mListener = mListener;
        }
    
        @Override
        public void onClick(View widget) {
            if (mListener != null) mListener.onClick(url);
        }
    
        public interface OnClickListener {
            void onClick(String url);
        }
    }
    
  2. Define RichTextUtils Class which will manipulate the Spanned text of your TextView.

    public static class RichTextUtils {
    
        public static <A extends CharacterStyle, B extends CharacterStyle> Spannable replaceAll (
              Spanned original,
              Class<A> sourceType,
              SpanConverter<A, B> converter,
              final ClickSpan.OnClickListener listener) {
    
                  SpannableString result = new SpannableString(original);
                  A[] spans = result.getSpans(0, result.length(), sourceType);
    
                  for (A span : spans) {
                      int start = result.getSpanStart(span);
                      int end = result.getSpanEnd(span);
                      int flags = result.getSpanFlags(span);
    
                      result.removeSpan(span);
                      result.setSpan(converter.convert(span, listener), start, end, flags);
                  }
    
                  return (result);
              }
    
              public interface SpanConverter<A extends CharacterStyle, B extends CharacterStyle> {
                  B convert(A span, ClickSpan.OnClickListener listener);
    
         }
    }
    
  3. Define URLSpanConverter class which will do the actual code of replacement URLSpan with Your Custom Span.

        public static class URLSpanConverter
            implements RichTextUtils.SpanConverter<URLSpan, ClickSpan> {
    
    
            @Override
            public ClickSpan convert(URLSpan span, ClickSpan.OnClickListener listener) {
                return (new ClickSpan(span.getURL(), listener));
            }
       }
    
  4. Usage

    TextView textView = ((TextView) this.findViewById(R.id.your_id));
    textView.setText("your_text");
    Linkify.addLinks(contentView, Linkify.ALL);
    
    Spannable formattedContent = UIUtils.RichTextUtils.replaceAll((Spanned)textView.getText(), URLSpan.class, new UIUtils.URLSpanConverter(), new UIUtils.ClickSpan.OnClickListener() {
    
        @Override
        public void onClick(String url) {
            // Call here your Activity
        }
    });
    textView.setText(formattedContent);
    

Note that I defined all classes here as static so you can put it directly info your Util class and then reference it easily from any place.

Just to share an alternative solution using Textoo that I just created:

    TextView yourTextView = Textoo
        .config((TextView) findViewById(R.id.your_text_view))
        .addLinksHandler(new LinksHandler() {
            @Override
            public boolean onClick(View view, String url) {
                if (showInMyWebView(url)) {
                    //
                    // Your custom handling here
                    //
                    return true;  // event handled
                } else {
                    return false; // continue default processing i.e. launch browser app to display link
                }
            }
        })
        .apply();

Under the hood the library replace URLSpan's in the TextView with custom ClickableSpan implementation that dispatch click events to the user supplied LinksHandler(s). The mechanism is very similar to solution from @commonsWare. Just package in a higher level API to make it easier to use.

I think David Hedlund's answer to himself is the way to go. In my case I had a TextView containing SMS content form user's inbox and I wanted to handle the fallowing behaviour:

  1. If a WEB URL is found then linkify it and handle the click within the application.
  2. If PHONE NUMBER is found, then linkify it and handle the click showing a picker to let the user choose if call the number or add it to the address-book (all within the application)

To achieve this I used the linked answer in this way:

TextView tvBody = (TextView)findViewById(R.id.tvBody);
tvBody.setText(messageContentString);

// now linkify it with patterns and scheme
Linkify.addLinks(tvBody, Patterns.WEB_URL, "com.my.package.web:");
Linkify.addLinks(tvBody, Patterns.PHONE, "com.my.package.tel:");

Now in the manifest:

<activity
    android:name=".WebViewActivity"
    android:label="@string/web_view_activity_label">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="android.intent.action.VIEW"/>
        <data android:scheme="com.duckma.phonotto.web"/>
    </intent-filter>
</activity>
...
<activity
    android:name=".DialerActivity"
    android:label="@string/dialer_activity_label">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="android.intent.action.VIEW"/>
        <data android:scheme="com.duckma.phonotto.tel"/>
    </intent-filter>
</activity>
<activity
    android:name=".AddressBook"
    android:label="@string/address_book_activity_label">
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />
        <action android:name="android.intent.action.VIEW"/>
        <data android:scheme="com.duckma.phonotto.tel"/>
    </intent-filter>
</activity>

And it works just fine for me, when user clicks a phone number the picker will show up with the dialer/address book choice. In the called activities use getIntent().getData() to find the passed Url with the data in it.

Than do this:

        TextView textView=(TextView) findViewById(R.id.link);
        textView.setOnClickListener(new OnClickListener() {

            public void onClick(View v) {
                Intent intent=new Intent(YourActivityName.this,WebActivity.class);
                startActivity(intent);
            }
        });

onCreate of WebActivity Class:

WebView webView=(WebView) findViewById(R.id.web);
webView.loadUrl("http://www.google.co.in");

Add this under textview in xml:

android:clickable="true"
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top