Sunday, October 20, 2013

Open URL, Linkify text and Spannable string in Android

In this post I will be going to describe how to open URL in Android and how to link text in different ways using default methods and spannable string.
Below are the different methods to linking text with URL:

1.Android's default method to link text with URL:
Here I added one text box :
<TextView android:id="@+id/text1"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/link"/>

For this I am using below string :
 <string name="link"><a href="http://android2011dev.blogspot.com">Go to Android-Dev blog page.</a> </string>

and now will call this string on click of text view:
private void linkWithWeb()
{
TextView text1 = (TextView)findViewById(R.id.text1);
text1.setMovementMethod(LinkMovementMethod.getInstance());
}

It will look like this on device :


Here complete text is linked with URL.

2.In this second option I will link some part of string with URL and make the link text bold.
Add the text box:
<TextView android:id="@+id/text2"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/bold_link"/>

Here is the bold_link string :
    <string name="bold_link">
Go to
<a STYLE="text-decoration:none" href="http://android2011dev.blogspot.com">
<b>Android Dev Blog</b>
</a> page.
</string>

Here we will be going to initialize text box and on click of text box app will launch above URL in web browser. Call below method on click of text box.

private void linkWithBoldWeb() {
TextView text2 = (TextView)findViewById(R.id.text2);
text2.setMovementMethod(LinkMovementMethod.getInstance());
}

It will look like this on device :



3. Change the text font and color of linking text
Text box:
<TextView android:id="@+id/text3" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/link"/>  
Here is the string:
<string name="custom_link_pre">Go to </string>
<string name="custom_link_end">Android Dev Blog </string>
<string name="custom_link_end_text">page.</string>
Here I used three strings, text before link text, link text and finally text after link.

Here is the method to build third text box:

String linkBlog = "http://android2011dev.blogspot.com";
private void cutomizeLink(){
TextView text3 = (TextView)findViewById(R.id.text3);
text3.setMovementMethod(LinkMovementMethod.getInstance());
String strLeading = getXMLString(this, R.string.custom_link_pre);
String strTrailing = getXMLString(this, R.string.custom_link_end);
String strTrailingEnd = getXMLString(this, R.string.custom_link_end_text);

SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
stringBuilder.append(strLeading);
stringBuilder.append(" " + strTrailing);
stringBuilder.append(" " + strTrailingEnd);

stringBuilder.setSpan(new NonUnderlinedClickableSpan() {

@Override
public void onClick(View widget) {

Intent browse = new Intent(Intent.ACTION_VIEW, Uri.parse(linkBlog));
startActivity(browse);
}
}, strLeading.length(), strLeading.length() + strTrailing.length()+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

text3.setText(stringBuilder);
text3.setMovementMethod(LinkMovementMethod.getInstance());

}

getXMLString method is used for int to string conversion.
public static String getXMLString(Context context, int stringID) {

Resources res = context.getResources();
return res.getString(stringID);

}

NonUnderlinedClickableSpan class will be used to change the text color from default blue to megenta and removed underline from lined text.
public class NonUnderlinedClickableSpan extends ClickableSpan {
@Override
public void updateDrawState(TextPaint ds) {

ds.setColor(Color.MAGENTA);
ds.setUnderlineText(false);
ds.setTypeface(Typeface.DEFAULT_BOLD);

}

@Override
public void onClick(View v) {

}
}

It will look like this on device :


4. Here we will see how to multiple links in same string along with I will change the text font and color of linking text same as third option.
Add text box:
<TextView android:id="@+id/text4" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:textSize="20sp"
    android:text="@string/custom_link_text"
    android:padding="15dip"/>

Then add string:
<string name="custom_link_text">Go to Google search page and Blog page.</string>

And now initialize and build the text box as per requirement:

String link = "http://www.google.com";
String linkBlog = "http://android2011dev.blogspot.com";

private void customLink(){
TextView text4 = (TextView)findViewById(R.id.text4);
text4.setMovementMethod(LinkMovementMethod.getInstance());
String customStr = getXMLString(this, R.string.custom_link_text);
String strLeading = customStr.substring(0, customStr.indexOf("Google"));
String strTrailing = customStr.substring(customStr.indexOf("Google"), (customStr.indexOf("search") - 1));
String strTrailingEnd = customStr.substring(0, (customStr.indexOf("Blog")));
String strTrailingEnd1 = customStr.substring((customStr.lastIndexOf("page")));
SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
stringBuilder.append(customStr);

stringBuilder.setSpan(new NonUnderlinedClickableSpan() {

@Override
public void onClick(View widget) {

Intent browse = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
startActivity(browse);
}
},strLeading.length(), 
strLeading.length() + strTrailing.length()+1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

stringBuilder.setSpan(new NonUnderlinedClickableSpan() {

@Override
public void onClick(View widget) {

Intent browse = new Intent(Intent.ACTION_VIEW, Uri.parse(linkBlog));
startActivity(browse);
}
},strTrailingEnd.length(), 
strTrailingEnd.length() + strTrailingEnd1.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

text4.setText(stringBuilder);
text4.setMovementMethod(LinkMovementMethod.getInstance());

}
Here is the result :

On click of  'Google' app will navigate to "http://www.google.com" page and on click 'Blog' navigate to "http://android2011dev.blogspot.com" page.

That's it
Here is the complete source code :
Activity file:
public class SpannableStringActivity extends Activity {

String link = "http://www.google.com";
String linkBlog = "http://android2011dev.blogspot.com";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
linkWithWeb();
linkWithBoldWeb();
cutomizeLink();
customLink();
}
/*
* Used to linking with web
*/
private void linkWithWeb() {
TextView text1 = (TextView)findViewById(R.id.text1);
text1.setMovementMethod(LinkMovementMethod.getInstance());
}

/*
* Link with bold font
*/
private void linkWithBoldWeb() {
TextView text2 = (TextView)findViewById(R.id.text2);
text2.setMovementMethod(LinkMovementMethod.getInstance());
}

/*
* Change the text font and color of linking text
*/
private void cutomizeLink(){
TextView text3 = (TextView)findViewById(R.id.text3);
text3.setMovementMethod(LinkMovementMethod.getInstance());
String strLeading = getXMLString(this, R.string.custom_link_pre);
String strTrailing = getXMLString(this, R.string.custom_link_end);
String strTrailingEnd = getXMLString(this, R.string.custom_link_end_text);

SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
stringBuilder.append(strLeading);
stringBuilder.append(" " + strTrailing);
stringBuilder.append(" " + strTrailingEnd);

stringBuilder.setSpan(new NonUnderlinedClickableSpan() {

@Override
public void onClick(View widget) {

Intent browse = new Intent(Intent.ACTION_VIEW, Uri.parse(linkBlog));
startActivity(browse);
}
}, strLeading.length(), strLeading.length() + strTrailing.length()+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

text3.setText(stringBuilder);
text3.setMovementMethod(LinkMovementMethod.getInstance());
}
/*
*  Change the text font and color of linking text. Add multiple links in to the single sentence.
*/
private void customLink(){
TextView text4 = (TextView)findViewById(R.id.text4);
text4.setMovementMethod(LinkMovementMethod.getInstance());
String customStr = getXMLString(this, R.string.custom_link_text);
String strLeading = customStr.substring(0, customStr.indexOf("Google"));
String strTrailing = customStr.substring(customStr.indexOf("Google"), (customStr.indexOf("search") - 1));
String strTrailingEnd = customStr.substring(0, (customStr.indexOf("Blog")));
String strTrailingEnd1 = customStr.substring((customStr.lastIndexOf("page")));
SpannableStringBuilder stringBuilder = new SpannableStringBuilder();
stringBuilder.append(customStr);
stringBuilder.setSpan(new NonUnderlinedClickableSpan() {

@Override
public void onClick(View widget) {

Intent browse = new Intent(Intent.ACTION_VIEW, Uri.parse(link));
startActivity(browse);
}
},strLeading.length(), 
strLeading.length() + strTrailing.length()+1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

stringBuilder.setSpan(new NonUnderlinedClickableSpan() {

@Override
public void onClick(View widget) {

Intent browse = new Intent(Intent.ACTION_VIEW, Uri.parse(linkBlog));
startActivity(browse);
}
},strTrailingEnd.length(), 
strTrailingEnd.length() + strTrailingEnd1.length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

text4.setText(stringBuilder);
text4.setMovementMethod(LinkMovementMethod.getInstance());
}

public class NonUnderlinedClickableSpan extends ClickableSpan {
@Override
public void updateDrawState(TextPaint ds) {

ds.setColor(Color.MAGENTA);
ds.setUnderlineText(false);
ds.setTypeface(Typeface.DEFAULT_BOLD);
}

@Override
public void onClick(View v) {

}
}

public static String getXMLString(Context context, int stringID) {

Resources res = context.getResources();
return res.getString(stringID);
}
}

main.xml file :
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView android:id="@+id/text1" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:textSize="20sp"
    android:text="@string/link"
    android:padding="15dip"/>

<TextView android:id="@+id/text2" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:textSize="20sp"
     android:text="@string/bold_link"
    android:padding="15dip"/>
    
<TextView android:id="@+id/text3" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:textSize="20sp"
    android:text="@string/link"
    android:padding="15dip"/>  
    
    
 <TextView android:id="@+id/text4" 
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:textSize="20sp"
    android:text="@string/custom_link_text"
    android:padding="15dip"/>       
</LinearLayout>

string.xml file:
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello World, SpannableStringActivity!</string>
    <string name="app_name">SpannableString</string>
    <string name="link"><a href="http://android2011dev.blogspot.com">Go to Android-Dev blog page.</a> </string>
    <string name="bold_link">
Go to 
<a STYLE="text-decoration:none" href="http://android2011dev.blogspot.com">
<b>Android Dev Blog</b>
</a> page.
</string>
<string name="custom_link_pre">Go to </string>
<string name="custom_link_end">Android Dev Blog </string>
<string name="custom_link_end_text">page.</string>
<string name="custom_link_text">Go to Google search page and Blog page.</string>
</resources>

Monday, October 14, 2013

Outgoing SMS details

In my previous post you can find how to read incoming SMS content programmatically.
In this post I will give you information about how to read outgoing SMS content. It's same as incoming SMS data reading, just need use different URI to read outgoing SMS content.
To read outgoing SMS content first add user permission:
 <uses-permission android:name="android.permission.READ_SMS"/>

Then call below method :

public StringBuffer getOutgoingSMSContent() {

ContentResolver contentResolver = getContentResolver();
Uri uri = Uri.parse("content://sms/sent/");
StringBuffer messagedata = new StringBuffer();
int count = 0;

Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor.getCount() != 0) {
if (cursor.moveToFirst()) {
do {
messagedata.append("Outgoing message count: " + (count +1) + "\n");
for (int m = 0; m < cursor.getColumnCount(); m++) {
if (cursor.getColumnName(m).equalsIgnoreCase("address")
|| cursor.getColumnName(m).equalsIgnoreCase("date")
|| cursor.getColumnName(m).equalsIgnoreCase("body")
|| cursor.getColumnName(m).equalsIgnoreCase("type"))
{
messagedata.append(cursor.getColumnName(m) + "  : "
+ cursor.getString(m));
messagedata.append("\n");
}
}
messagedata.append("\n");
count++;
} while (cursor.moveToNext());
}
}
cursor.close();
cursor = null;
return messagedata;
}

This method will return list of all outgoing SMS content.

Result will look something like this :


Here 'address' is mobile number of receiver.
'date' is message sent time in milliseconds.
'type' is message type incoming/outgoing. 2 is outgoing message type.
'body' is message text.











Incoming SMS details

Using Android API's user can read incoming SMS message content programmatically.
To get SMS content first add below user permission in manifest file:
<uses-permission android:name="android.permission.READ_SMS"></uses-permission>

Then call below method from activity class:

public StringBuffer getIncomingSMSContent() {

ContentResolver contentResolver = getContentResolver();
Uri uri = Uri.parse("content://sms/inbox/");
StringBuffer messagedata = new StringBuffer();
int count = 0;
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor.getCount() != 0) {
if (cursor.moveToFirst()) {
do {
messagedata.append("Incoming message count: " + (count +1) + "\n");
for (int m = 0; m < cursor.getColumnCount(); m++) {
if (cursor.getColumnName(m).equalsIgnoreCase("address")
|| cursor.getColumnName(m).equalsIgnoreCase("date")
|| cursor.getColumnName(m).equalsIgnoreCase("body")
|| cursor.getColumnName(m).equalsIgnoreCase("type"))
{
messagedata.append(cursor.getColumnName(m) + "  : "
+ cursor.getString(m));
messagedata.append("\n");
}
}
messagedata.append("\n");
count++;
} while (cursor.moveToNext());
}
}
cursor.close();
cursor = null;
return messagedata;
}

This method will return list of all incoming message content from inbox.

Here is the result :


In above result screen, 
address : mobile number from which device received message
date : message received time in milliseconds
type : message type incoming/outgoing. Type 1 indicates as incoming message type
body : message text