Android Text Links Using Linkify

April 09, 2010

The Android framework provides an easy way to automatically convert text patterns into clickable links. By default, Android knows how to recognize web URLs, email addresses, map addresses, and phone numbers, but it also includes a flexible mechanism for recognizing and converting additional text patterns, as well.

The Android Developers Blog has an article entitled Linkify your Text! that provides a nice overview of the system. It discusses how the Linkify class can be used to enable the default link patterns and then continues with a more advanced WikiWords example that demonstrates custom links. That article is a fine introduction to the system, so the rest of this article will primarily focus on details not covered therein.

All of the examples in this article are based on the TextView widget. The Linkify class can also be used to add links to Spannable text, but those use cases won’t be covered here because their usage is nearly identical to the TextView cases.

TextView AutoLinking

The TextView widget features an android:autoLink attribute that controls the types of text patterns that are automatically recognized and converted to clickable links. This attribute is a convenient way to enable one or more of the default link patterns because it can be configured directly from a layout without involving any additional code.

However, for those cases where programmatically setting this value is useful, the setAutoLinkMask() function exists.

There is one important caveat to using this “auto-linking” functionality, however. It appears that when “auto-linking” is enabled, all additional Linkify operations are ignored. It’s unclear whether this behavior is intentional or inadvertent, so it’s possible things could change in future released of the Android SDK. Consider disabling “auto-linking” before using any of the Linkify operations discussed below.

// Disable the text view's auto-linking behavior
textView.setAutoLinkMask(0);

Enabling support for one of Android’s default link patterns is very easy. Simply use the addLinks(TextView text, int mask) function and specify a mask that describes the desired link types.

import android.text.util.Linkify;

// Recognize phone numbers and web URLs
Linkify.addLinks(text, Linkify.PHONE_NUMBERS | Linkify.WEB_URLS);

// Recognize all of the default link text patterns 
Linkify.addLinks(text, Linkify.ALL);

// Disable all default link detection
Linkify.addLinks(text, 0);

Detecting additional types of link patterns is easy, too. The addLinks(TextView text, Pattern pattern, String scheme) function detects links based on a regular expression pattern.

import java.util.regex.Pattern;
import android.text.util.Linkify;

// Detect US postal ZIP codes and link to a lookup service
Pattern pattern = Pattern.compile("\\d{5}([\\-]\\d{4})?");
String scheme = "http://zipinfo.com/cgi-local/zipsrch.exe?zip=";
Linkify.addLinks(text, pattern, scheme);

The text is scanned for pattern matches. Matches are converted to links that are generated by appending the matched text to the provided URL scheme base.

Note that the scheme doesn’t have to be an external web-like URL. It could also be an Android Content URI that can be used in conjunction with a content provider to reference application resources, for example.

Match Filters

Regular expressions are a very powerful way to match text patterns, but sometimes a bit more flexibility is needed. The MatchFilter class provides this capability by giving user code a chance to evaluate the link worthiness of some matched text.

import java.util.regex.Pattern;
import android.text.util.Linkify;
import android.text.util.Linkify.MatchFilter;

// A match filter that only accepts odd numbers.
MatchFilter oddFilter = new MatchFilter() {
    public final boolean acceptMatch(CharSequence s, int start, int end) {
        int n = Character.digit(s.charAt(end-1), 10);
        return (n & 1) == 1;
    }
};

// Match all digits in the pattern but restrict links to only odd
// numbers using the filter.
Pattern pattern = Pattern.compile("[0-9]+");
Linkify.addLinks(text, pattern, "http://...", oddFilter, null);

A more complex (but useful!) example would involve matching valid dates. The regular expression could be generous enough to match strings like “2010-02-30” (February 30, 2010), but a match filter could provide the logic to reject bogus calendar dates.

Transform Filters

Up until this point, the final link was always being generated based on the exact matched text. There are many cases where that is not desirable, however. For example, it’s common to mention a username using the @username syntax, but the resulting link should only include the username portion of the text. The TransformFilter class provides a solution.

import java.util.regex.Pattern;
import android.text.util.Linkify;
import android.text.util.Linkify.TransformFilter;

// A transform filter that simply returns just the text captured by the
// first regular expression group.
TransformFilter mentionFilter = new TransformFilter() {
    public final String transformUrl(final Matcher match, String url) {
        return match.group(1);
    }
};

// Match @mentions and capture just the username portion of the text.
Pattern pattern = Pattern.compile("@([A-Za-z0-9_-]+)");
String scheme = "http://twitter.com/";
Linkify.addLinks(text, pattern, scheme, null, mentionFilter);

This approach uses the regular expression’s capture syntax to extract just the username portion of the pattern as a uniquely addressable match group. Alternatively, the transform filter could just return all of the matched text after the first character (@), but the above approach is nice because it keeps all of the pattern’s details within the regular expression.

Of course, transform filters can be combined with match filters for ultimate flexibility. The Android SDK uses this approach to detect wide ranges of phone number formats (many of which include various parentheses and dashes) while always generating a simplified link containing only digits.

Further Reading

For more information about the specific implementation details of Android’s link generation system, the best reference is actually the source code itself. In addition to being a good resource for understanding the system, it’s also the best way to track down potential bugs or misunderstandings about how the system is intended to be used.