Search This Blog

Thursday, 15 March 2012

Chapter 14 :Linkify Android


Linkify Android

Linkify

Linkify take a piece of text and a regular expression and turns all of the regex matches in the text into clickable links. This is particularly useful for matching things like email addresses, web urls, etc. and making them actionable. Alone with the pattern that is to be matched, a url scheme prefix is also required. Any pattern match that does not begin with the supplied scheme will have the scheme prepended to the matched text when the clickable url is created. For instance, if you are matching web urls you would supply the scheme http://. If the pattern matches example.com, which does not have a url scheme prefix, the supplied scheme will be prepended to create http://example.com when the clickable url link is created.

Constants

public static final int ALL

Bit mask indicating that all available patterns should be matched in methods that take an options mask
Constant Value: 15 (0x0000000f)

public static final int EMAIL_ADDRESSES

Bit field indicating that email addresses should be matched in methods that take an options mask
Constant Value: 2 (0x00000002)

public static final int MAP_ADDRESSES

Bit field indicating that street addresses should be matched in methods that take an options mask
Constant Value: 8 (0x00000008)

public static final int PHONE_NUMBERS

Bit field indicating that phone numbers should be matched in methods that take an options mask
Constant Value: 4 (0x00000004)

public static final int WEB_URLS

Bit field indicating that web URLs should be matched in methods that take an options mask
Constant Value: 1 (0x00000001)

Fields

public static final Linkify.MatchFilter sPhoneNumberMatchFilter

Filters out URL matches that don't have enough digits to be a phone number.

public static final Linkify.TransformFilter sPhoneNumberTransformFilter

Transforms matched phone number text into something suitable to be used in a tel: URL. It does this by removing everything but the digits and plus signs. For instance: '+1 (919) 555-1212'
becomes '+19195551212'

public static final Linkify.MatchFilter sUrlMatchFilter

Filters out web URL matches that occur after an at-sign (@). This is to prevent turning the domain name in an email address into a web link.

Public Constructors

public Linkify ()

Public Methods

public static final boolean addLinks (TextView text, int mask)

Scans the text of the provided TextView and turns all occurrences of the link types indicated in the mask into clickable links. If matches are found the movement method for the TextView is set to LinkMovementMethod.

public static final void addLinks (TextView text, Pattern p, String scheme, Linkify.MatchFilter matchFilter, Linkify.TransformFilter transformFilter)

Applies a regex to the text of a TextView turning the matches into links. If links are found then UrlSpans are applied to the link text match areas, and the movement method for the text is changed to LinkMovementMethod.
Parameters
text TextView whose text is to be marked-up with links
p Regex pattern to be used for finding links
scheme Url scheme string (eg http:// to be prepended to the url of links that do not have a scheme specified in the link text
matchFilter The filter that is used to allow the client code additional control over which pattern matches are to be converted into links.

public static final boolean addLinks (Spannable text, int mask)

Scans the text of the provided Spannable and turns all occurrences of the link types indicated in the mask into clickable links. If the mask is nonzero, it also removes any existing URLSpans attached to the Spannable, to avoid problems if you call it repeatedly on the same text.

public static final boolean addLinks (Spannable s, Pattern p, String scheme, Linkify.MatchFilter matchFilter, Linkify.TransformFilter transformFilter)

Applies a regex to a Spannable turning the matches into links.
Parameters
s Spannable whose text is to be marked-up with links
p Regex pattern to be used for finding links
scheme Url scheme string (eg http:// to be prepended to the url of links that do not have a scheme specified in the link text
matchFilter The filter that is used to allow the client code additional control over which pattern matches are to be converted into links.

public static final boolean addLinks (Spannable text, Pattern pattern, String scheme)

Applies a regex to a Spannable turning the matches into links.
Parameters
text Spannable whose text is to be marked-up with links
pattern Regex pattern to be used for finding links
scheme Url scheme string (eg http:// to be prepended to the url of links that do not have a scheme specified in the link text

public static final void addLinks (TextView text, Pattern pattern, String scheme)

Applies a regex to the text of a TextView turning the matches into links. If links are found then UrlSpans are applied to the link text match areas, and the movement method for the text is changed to LinkMovementMethod.
Parameters
text TextView whose text is to be marked-up with links
pattern Regex pattern to be used for finding links
scheme Url scheme string (eg http:// to be prepended to the url of links that do not have a scheme specified in the link text.

MatchFilter

MatchFilter enables client code to have more control over what is allowed to match and become a link, and what is not. For example: when matching web urls you would like things like http://www.example.com to match, as well as just example.com itelf. However, you would not want to match against the domain in support@example.com. So, when matching against a web url pattern you might also include a MatchFilter that disallows the match if it is immediately preceded by an at-sign (@).
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);


public abstract boolean acceptMatch (CharSequence s, int start, int end)

Examines the character span matched by the pattern and determines if the match should be turned into an actionable link.
Parameters
s The body of text against which the pattern was matched
start The index of the first character in s that was matched by the pattern - inclusive
end The index of the last character in s that was matched - exclusive
Returns
  • Whether this match should be turned into a link

TransformFilter

TransformFilter enables client code to have more control over how matched patterns are represented as URLs. For example: when converting a phone number such as (919) 555-1212 into a tel: URL the parentheses, white space, and hyphen need to be removed to produce tel:9195551212.
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.
mport 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.


public abstract String transformUrl (Matcher match, String url)

Examines the matched text and either passes it through or uses the data in the Matcher state to produce a replacement.
Parameters
match The regex matcher state that found this URL text
url The text that was matched
Returns
  • The transformed form of the URL


Creating Links using Linkify



Linkify is a class that lets you create links from a TextView or a Spannable.
You can create links not just to web pages, but also to locations on the map, emails and even phone numbers.




Web address:
TextView myWebSite = new TextView(this);
myWebSite .setText("http://http://www.dzone.com/");
Linkify.addLinks(myWebSite , Linkify.WEB_URLS);













Phone number:


TextView myPhone = (TextView) findViewById(R.id.my_web_site);


myPhone .setText(“5552323233”);


Linkify.addLinks(myPhone  , Linkify.PHONE_NUMBERS);



Map address:


TextView myLocation = new TextView(this);


myLocation.setText("436 Mayfield Ave, Stanford, CA");


Linkify.addLinks(myLocation , Linkify.MAP_ADDRESSES);


mainLayout.addView(myLocation);


Email address:


TextView myEmail= (TextView) findViewById(R.id.my_web_site);


myEmail.setText(“aviyehuda@gmail.com”);


Linkify.addLinks(myEmail  , Linkify.EMAIL_ADDRESSES);



Auto detect:
Use Linkify.ALL to automatically detect the link type.


Linkify.addLinks(myTextView , Linkify.ALL);

More than one option:
You can choose more than a single option for the link type.


Linkify.addLinks(myTextView, Linkify.PHONE_NUMBERS | Linkify.WEB_URLS);

Using pattern:
You can use a regular expression for detecting text parts and transform only them to links instead of the whole text.


TextView myCustomLink = new TextView(this);


Pattern pattern = Pattern.compile("[a-zA-Z]+&");


myCustomLink.setText("press Linkify& or on Android& to search it on google");


Linkify.addLinks(myCustomLink,pattern, "http://www.google.ie/search?q=");


mainLayout.addView(myCustomLink);


MatchFilter:
MatchFilter is used for more complicated filters.


MatchFilter myMatchFilter = new MatchFilter() {


   @Override


   public boolean acceptMatch(CharSequence cs, int start, int end) {





      return start > 48;


   }


};





TextView myCustomLink2 = new TextView(this);


Pattern pattern2 = Pattern.compile("[a-zA-Z]+");


myCustomLink2.setText("press one of these words to search it on google: Android Linkify dzone");


Linkify.addLinks(myCustomLink2,pattern2, "http://www.google.ie/search?q=", myMatchFilter, null);





mainLayout.addView(myCustomLink2);


TransformFilter:
So fat the text we have filtered stayed the same. But sometimes you need the text to be different than the text which is appended to the link.


  TransformFilter myTransformFilter = new TransformFilter() {


   @Override


   public String transformUrl(Matcher match, String url) {


      return url.substring(1); //remove the $ sign


   }


};





TextView myCustomLink3 = new TextView(this);


Pattern pattern3 = Pattern.compile("\\$[a-zA-Z]+");


myCustomLink3.setText("press $Linkify or on $Android to search it on google");


Linkify.addLinks(myCustomLink3,pattern3, "http://www.google.ie/search?q=", null, myTransformFilter);


mainLayout.addView(myCustomLink3);








Linkify your Text!




For example, in our case we want to look for a regular expression match for a WikiWord (that is, a word with camel case » and no spaces). Linkify can then turn this into a Content URI — something like content://com.google.android.wikinotes.db.wikinotes/wikinotes/WikiWord, which can then be used to locate the correct wiki page from a ContentProvider.
As a bonus, the Linkify class also defines several default matches, in particular it is able to turn web URLs, email addresses and telephone numbers into active links which fire Android intents automatically.
Linkify can be passed any TextView in your application, and will take care of creating the links and enabling their "clickability" for you.
Default Linkify: Using the set of default active link options is very straightforward. Simply pass it a handle to a TextView with content in it, and the Linkify.ALL flag:
TextView noteView = (TextView) findViewById(R.id.noteview);
noteView.setText(someContent);
Linkify.addLinks(noteView, Linkify.ALL);
and that's it. The Linkify.ALL flag applies all of the predefined link actions, and the TextView will be immediately updated with a set of active links which, if you select them, fire default intents for the actions (e.g. a web URL will start the browser with that URL, a telephone number will bring up the phone dialer with that number ready to call, etc.).
Custom Linkify: So what about our WikiWord? There is no pre-defined action for that, so it needs to be defined and associated with a scheme.
The first task is to define a regular expression that matches the kind of WikiWords we want to find. The regex in this case is:
\b[A-Z]+[a-z0-9]+[A-Z][A-Za-z0-9]+\b
Obvious, no? Well actually this is equivalent to the following description: "Starting with a word boundary (the \b) find at least one upper case letter, followed by at least one lower case letter or a numeric digit, followed by another upper case letter, and then any mix of upper case, lower case or numeric until the next word boundary (the final \b)". Regular expressions are not very pretty, but they are an extremely concise and accurate way of specifying a search pattern.
We also need to tell Linkify what to do with a match to the WikiWord. Linkify will automatically append whatever is matched to a scheme that is supplied to it, so for the sake of argument let's assume we have a ContentProvider that matches the following content URI:
content://com.google.android.wikinotes.db.wikinotes/wikinotes/WikiWord
The WikiWord part will be appended by Linkify when it finds a match, so we just need the part before that as our scheme.
Now that we have these two things, we use Linkify to connect them up:
Pattern wikiWordMatcher = Pattern.compile("\\b[A-Z]+[a-z0-9]+[A-Z][A-Za-z0-9]+\\b");
String wikiViewURL =    "content://com.google.android.wikinotes.db.wikinotes/wikinotes/";
Linkify.addLinks(noteView, wikiWordMatcher, wikiViewURL);
Note that the \b's had to be escaped with double backslashes for the Java Pattern.compile line.
Linkify can be used multiple times on the same view to add more links, so using this after the Default Linkify call means that the existing active links will be maintained and the new WikiWords will be added. You could define more Linkify actions and keep applying them to the same TextView if you wanted to.
Now, if we have a WikiWord in the TextView, let's say MyToDoList, Linkify will turn it into an active link with the content URI:
content://com.google.android.wikinotes.db.wikinotes/wikinotes/MyToDoList
and if you click on it, Android will fire the default intent for that content URI.
For this to all work, you will need a ContentProvider that understands that Content URI, and you will need a default activity capable of doing something with the resulting data. I plan to cover these in future blog entries (and soon). In fact, the whole Wiki Note Pad application is currently undergoing some clean up and review, and will then hopefully be released as a sample application.






Custom Link Patterns

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.






Default Link Patterns

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);



Example :
linkifySample






package com.ipsr;



import com.ipsr.R.layout;



import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.text.util.Linkify;
import android.widget.TextView;



public class LinkifySampleActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView tv=(TextView)findViewById(R.id.textView1);
tv.setTextColor(Color.WHITE);
String data="This is sample Linkify coding. \n"+"\n"+"URL:http://www.google.com/"+" \n"+
"email:abc@gmail.com\n"+"Phone: (82)-10-9887-6231 \n" +
"Address: 436 Mayfield Ave, Stanford, CA \n" +
"\n";
if(tv!=null)
{
tv.setText(data);
Linkify.addLinks(tv, Linkify.ALL);

} }
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />

<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge" />

</LinearLayout>


AndroidManifest.xml


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ipsr"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk android:minSdkVersion="8" />
<uses-permission android:name="android.permission.INTERNET"/>

<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".LinkifySampleActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>








No comments:

Post a Comment