Amazon AWS - EBS Volume Deletion on Instance Termination

This post explains how to set the DeleteOnTermination flag for an AMI image so that on down-scaling, you don't have to clean up all the EBS volumes that have been retained (which incur extra cost)

Prerequisites:

  • AWS Cli 
    • in ubuntu, apt-get awscli will install this, you will need to run aws configure post installation
  • AMI image which currently have the DeleteOnTermination flag false (When you click on the AMI, see the 'Block Devices'. /dev/sda=snap-snapshot_id:10:false:gp2  - The false indicates that the volume won't be deleted upon instance termination)
Step 1 (Optional) :

Run 

aws ec2 describe-images --image-ids ami-image_id

You will see the details of the image and you can spot   "DeleteOnTermination": false 

Step 2:

What you are going to do is to register a new AMI, which is a clone of the original AMI (with the DeleteOnTermination flag false) and set the DeleteOnTermination flag while registering. And the following command achieves this:

aws ec2 register-image --name "AMI New"  --block-device-mappings "[{\"DeviceName\": \"device_name_of_old\",\"Ebs\": {\"SnapshotId\": \"snap-snapshot_id_of_old_ami\",\"VolumeSize\": volume_size_of_old_ami,\"DeleteOnTermination\": true,\"VolumeType\":\"volume_type_of_old\"}}]"  --architecture architecture_of_old --virtualization-type virtualization_type_of_old --kernel-id kernel_id_of_old --root-device-name "root_device_name_of_old" 

If the command was successful, you will receive the Image ID of the newly registered AMI, which you can use in your code for up-scaling and down-scaling without worrying about unused volumes piling up. You can verify the change by redoing step 1 and replacing the ami-image_id with the image_id of the new AMI


JSON to HTML - Display JSON as a list in HTML

Many-a-times it is required to print list out your json object in html, like when you are testing your API and api produces the out put as json. The following javascript function may be used to print your json object as a neat html un-ordered list

function json_to_html(json) 
{

   ret = "";
   ret += "<ul>";
   for( i in json) 
   {
      ret += "<li>"+i+": ";

      if( typeof json[i] === "object") 
         ret += json_to_html(json[i]);
      else 
         ret += json[i];
          
     ret += "</li>";
   }
   ret += "</ul>";
   return ret;
}

Converting Putty ppk file to pem in Ubuntu

First you need to install putty tools. You may either install putty from the Software Center or use the following command in terminal

sudo apt-get install putty-tools

Converting the ppk file to pem file 

Use puttygen to convert ppk to pem

sudo puttygen /path/to/ppk/file.ppk -O private-openssh -o /path/for/saving/pem/key.pem

Copy the key.pem to ~/.ssh path 

sudo cp /path/of/pem/key.pem ~/.ssh/

Set file permission for ~/.ssh/key.pem 

sudo chmod 400 ~/.ssh/key.pem 

Use the pem key to ssh to the remote system 

sudo ssh -i ~/.ssh/key.pem user@your.systems.ip.address



Resetting MySQL root password

Forgot mysql root password? Follow the steps given below to reset it.

Step 1:

Stop the currently running mysql daemon.

sudo service mysql stop

OR

 sudo /etc/init.d/mysql stop

Step 2:

Start mysql with --skip-grant-tables

sudo /usr/sbin/mysqld --skip-grant-tables &

Step 3:

Start the mysql client without a password

mysql -u root

Step 4:

From the client, flush privileges

 FLUSH PRIVILEGES;

Step 5:

Reset password by executing the following from mysql client

SET PASSWORD FOR root@'localhost' = PASSWORD('newpassword');

Step 6:

Reset password for all root, if remote connection is enabled, from mysql client

UPDATE mysql.user SET Password=PASSWORD('newpassword') WHERE User='root';

Step 7:

Run flush priveleges again, from the client

FLUSH PRIVILEGES;

Step 8:

Exit the mysql client

exit

Step 9:

Restart mysql

sudo service mysql stop
sudo service mysql start

OR

sudo /etc/init.d/mysql stop
sudo /etc/init.d/mysql start

Your password has been successfully reset. Now you may login  using your new password.

Asterisk/Freepbx - Cannot record call after fetching it from parking lot -- FIX


Asterisk is not recording calls that are fetched from park. Here is my call flow.
  1. A calls to Asterisk Server(AS)
  2. Call is picked up by extension B
  3. B does an attended transfer by dialling *2200 (200 is my default parking lot)
  4. C dials 1 to fetch the parked call
  5. C dials *1 to record the call.
Recording is not done. Here is a fix that worked for me. Hope this helps someone.


In the asterisk log i found that asterisk tries to record to an invalid file with no filename, just an extension(.wav). It executed 2 files - /var/lib/asterisk/agi-bin/parkfetch.agi and /var/lib/asterisk/bin/one_touch_record.php. one_touch_record.php generates filename from data read from channel, like year, date, mixmonitor folder etc, but as there was no valid filename in the log, these should be null here.
$mixMonDir = getVariable($channel, "MIXMON_DIR");
$year = getVariable($channel, "YEAR");
$month = getVariable($channel, "MONTH");
$day = getVariable($channel, "DAY");
$mixMonFormat = getVariable($channel, "MIXMON_FORMAT");
$mixMonPost = getVariable($channel, "MIXMON_POST");
$astman->mixmonitor($channel, "{$mixMonDir}{$year}/{$month}/{$day}/{$callFileName}.{$mixMonFormat}", "a", $mixMonPost, rand());
So i inspected the parkfetch.agi were i found that these channel vars are copied only if REC_STATUS is "RECORDING" and in this case REC_STATUS is "INITIALIZED". So i added an OR clause ie i changed if ($rec_status == "RECORDING") to if ($rec_status == "RECORDING" || $rec_status=="INITIALIZED")
if ($channel)
{
 $rec_status = get_var("IMPORT($channel,REC_STATUS)");
 $agi->set_variable('REC_STATUS', $rec_status);
 if ($rec_status == "RECORDING" ||  $rec_status=="INITIALIZED") {
 foreach (array('MIXMON_DIR', 'YEAR', 'MONTH', 'DAY',  'CALLFILENAME', 'MIXMON_FORMAT', 'MIXMON_POST',  'MON_FMT') as $v) {
  $agi->set_variable($v, get_var("IMPORT($channel,$v)"));
  }
 }
}
And it worked. Now when I pressed *1 after fetching call from park, it is getting recorded. Hope dev team finds and fix this bug. I was using Asterisk 11.2.1.

MD5 in Java

The MD5 or Message-Digest Algorithm is a commonly used hash function, which produces a 128 bit hash value, commonly expressed as a 32 digit hex number. It is commonly used by web developers to store passwords or sensitive information in their databases. It is to be noted that an MD5 string cannot be converted back into the original string.
Although this hashing method is now considered "cryptographically broken or not suitable for further use" as of 2005, it is still widely used. In PHP, performing MD5 is very simple - one just has to call a function with the message to be digested as the argument($digest = md5("message"); ). But it is not that simple in java.

Here is a java class which you can place in your project to perform MD5 hashing.



import java.security.MessageDigest;

public class MD5
{
public static final String salt = "aq32nngetg45678";

public static String getMd5(String value)
{
try
{
 char[] hexDigits =  {'0','1','2','3','4','5','6','7','8','9','a','b','c','d' ,'e','f'};
 MessageDigest md5 = MessageDigest.getInstance("MD5");
 md5.update(value.getBytes("UTF-8"));
 byte[] messageDigest = md5.digest();
 StringBuilder sb = new StringBuilder(32);


 for (byte b : messageDigest) //for zero padding, ie make '1' '01'
 {
   sb.append(hexDigits[(b >> 4) & 0x0f]); //0x0f in hex = 15 in decimal. If b=1
   sb.append(hexDigits[b & 0x0f]);// sb += hexDigits[0] + hexDigits[1](sb += "01")
 }
 return sb.toString();
}
catch(Exception e)
{
 return "ERROR";
}
}

}
After placing this class in your project, you can perform md5 hashing by
String digest = MD5.getMd5("message");
It is a good practice to append a random string to your original string before performing hashing. This random string is called 'salt'. Salt is added to prevent dictionary attacks and rainbow table attacks.

>> is the signed right shift operator and it shifts a bit pattern to right. Its syntax is bit_pattern_to_shift >> no_of_positions_to_shift. Hence, here, before placing a digit into the final string,it is padded.

Android - Simple Multi-Contacts Picker with Listview and Checkbox

I assume the reader to have basic android programming skills. This post will cover the making of a simple android contact picker with the following features
  • Asynchronous loading of contact with AsyncTask
  • Display of all contacts in a Listview with the help of an Adapter
  • Multi Contact Select with Checkbox
  • Contact Search/Filtering
  • The Activity will return the contacts
Final Result

Step by step guide

Step 1: Get required permission in the manifest file


<uses-permission android:name="android.permission.READ_CONTACTS" />
This should be written outside the application tag.

Step 2: Set up the contacts picker activity

The contacts picker activity will display a list of contacts and a search box, which will enable the end-user to select multiple contacts. The following is the layout file for the activity
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="sachinmuralig.me.simplemulticontactpicker.ContactsPickerActivity">
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/txt_filter"
        android:hint="@string/txt_search"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />


    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/txt_load_progress"
        android:layout_below="@+id/txt_filter"
        android:text="Loading..."
        />


    <ListView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/txt_load_progress"
        android:layout_above="@+id/btn_done"
        android:id="@+id/lst_contacts_chooser"
    />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/btn_done"
        android:layout_alignParentBottom="true"
        android:text="@string/txt_done"
        />



</RelativeLayout>
The layout is pretty straight forward - an EditText for the search input, a ListView for contacts display and a 'Done' Button.

Step 3 : Represent a Contact as a class


package sachinmuralig.me.simplemulticontactpicker;

import android.os.Parcel;
import android.os.Parcelable;


public class Contact implements Parcelable {

    public String id,name,phone,label;

    Contact(String id, String name,String phone,String label){
        this.id=id;
        this.name=name;
        this.phone=phone;
        this.label=label;
    }

    protected Contact(Parcel in) {
        id = in.readString();
        name = in.readString();
        phone = in.readString();
        label = in.readString();
    }

    public static final Creator<Contact> CREATOR = new Creator<Contact>() {
        @Override
        public Contact createFromParcel(Parcel in) {
            return new Contact(in);
        }

        @Override
        public Contact[] newArray(int size) {
            return new Contact[size];
        }
    };

    @Override
    public String toString()
    {
        return name+" | "+label+" : "+phone;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(id);
        dest.writeString(name);
        dest.writeString(phone);
        dest.writeString(label);
    }
}
The Class implements Parcelable so that the selected contacts can be easily returned to another activity. Read more about parcelable here Not that the toString() method was overridden to return a presentable view of the contact data. The contact data that is used here is minimal(id, name, phone, label) to keep it simple and the data members can be easily populated using the Constructor.

Step 4 : The Contacts List Class

This adds another level of abstraction. We will be basically working with Contacts List. An instance of contacts list will contain all the contacts that is in the phone, another contacts list will hold the filtered contacts, which are filtered according to the search input and a third contact list will contain the contacts selected by the end-user, and this third list will be returned as the result.
package sachinmuralig.me.simplemulticontactpicker;


import java.util.ArrayList;


public class ContactsList{

    public ArrayList<Contact> contactArrayList;

    ContactsList(){

        contactArrayList = new ArrayList<Contact>();
    }

    public int getCount(){

        return contactArrayList.size();
    }

    public void addContact(Contact contact){
        contactArrayList.add(contact);
    }

    public  void removeContact(Contact contact){
        contactArrayList.remove(contact);
    }

    public Contact getContact(int id){

        for(int i=0;i<this.getCount();i++){
            if(Integer.parseInt(contactArrayList.get(i).id)==id)
                return contactArrayList.get(i);
        }

        return null;
    }
}
An ArrayList will hold the instances of Contact class and the methods aid in manipulating this list.

Step 5: Contacts List Adapter

The ContactsListAdapter will serve as the adapter for the contacts listview in our activity.
package sachinmuralig.me.simplemulticontactpicker;

import android.app.Activity;
import android.content.Context;
import android.database.DataSetObservable;
import android.database.DataSetObserver;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;


import java.util.ArrayList;


public class ContactsListAdapter extends BaseAdapter {

    Context context;
    ContactsList contactsList,filteredContactsList,selectedContactsList;
    String filterContactName;

    ContactsListAdapter(Context context, ContactsList contactsList){

        super();
        this.context = context;
        this.contactsList = contactsList;
        this.filteredContactsList=new ContactsList();
        this.selectedContactsList = new ContactsList();
        this.filterContactName = "";
    }

    public void filter(String filterContactName){



        filteredContactsList.contactArrayList.clear();

        if(filterContactName.isEmpty() || filterContactName.length()<1){
            filteredContactsList.contactArrayList.addAll(contactsList.contactArrayList);
            this.filterContactName = "";

        }
        else {
            this.filterContactName = filterContactName.toLowerCase().trim();
            for (int i = 0; i < contactsList.contactArrayList.size(); i++) {

                if (contactsList.contactArrayList.get(i).name.toLowerCase().contains(filterContactName))
                    filteredContactsList.addContact(contactsList.contactArrayList.get(i));
            }
        }
        notifyDataSetChanged();

    }

    public void addContacts(ArrayList<Contact> contacts){
        this.contactsList.contactArrayList.addAll(contacts);
        this.filter(this.filterContactName);

    }

    @Override
    public int getCount() {
        return filteredContactsList.getCount();
    }

    @Override
    public Contact getItem(int position) {
        return filteredContactsList.contactArrayList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return Long.parseLong(this.getItem(position).id);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        ViewHolder viewHolder;

        if(convertView==null){
            LayoutInflater inflater = ((Activity)context).getLayoutInflater();

            convertView = inflater.inflate(R.layout.contact_item, parent, false);

            viewHolder = new ViewHolder();
            viewHolder.chkContact = (CheckBox) convertView.findViewById(R.id.chk_contact);
            convertView.setTag(viewHolder);

        }else {
            viewHolder = (ViewHolder) convertView.getTag();
        }

        viewHolder.chkContact.setText(this.filteredContactsList.contactArrayList.get(position).toString());
        viewHolder.chkContact.setId(Integer.parseInt(this.filteredContactsList.contactArrayList.get(position).id));
        viewHolder.chkContact.setChecked(alreadySelected(filteredContactsList.contactArrayList.get(position)));

        viewHolder.chkContact.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                Contact contact = filteredContactsList.getContact(buttonView.getId());

                if(contact!=null && isChecked && !alreadySelected(contact)){
                    selectedContactsList.addContact(contact);
                }
                else if(contact!=null && !isChecked){
                    selectedContactsList.removeContact(contact);
                }
            }
        });

        return convertView;
    }

    public boolean alreadySelected(Contact contact)
    {
        if(this.selectedContactsList.getContact(Integer.parseInt(contact.id))!=null)
            return true;

        return false;
    }

    public static class ViewHolder{

        CheckBox chkContact;
    }
}
contactsList - will hold the entire contacts in the device, filteredContactsList will hold a subset of the contactsList and is filtered based on the contact name, selectedContactsList also contains a subset of contactsList and this holds the contacts that are selected from the list using the checkbox. The getView method inflates a layout that basically just contains a checkbox, for each contact that is to be displayed. You can modify this, if you want to add other fields like email or contact image etc.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <CheckBox
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/chk_contact"

        />

</LinearLayout>
The 'filter' method is the one performing the filter actions and displaying the filtered contacts.

Step 6: Loading the contacts Asynchronously

The contactsList in the Adapter must be populated in a separate thread. An AsyncTask is used for the same.
package sachinmuralig.me.simplemulticontactpicker;


import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.ContactsContract;
import android.view.View;
import android.widget.TextView;

import java.util.ArrayList;


public class ContactsLoader extends AsyncTask<String,Void,Void> {

    ContactsListAdapter contactsListAdapter;
    Context context;
    private ArrayList<Contact> tempContactHolder;
    TextView txtProgress;
    int totalContactsCount,loadedContactsCount;


    ContactsLoader(Context context,ContactsListAdapter contactsListAdapter){
        this.context = context;
        this.contactsListAdapter = contactsListAdapter;
        this.tempContactHolder= new ArrayList<>();
        loadedContactsCount=0;
        totalContactsCount=0;
        txtProgress=null;
    }

    @Override
    protected Void doInBackground(String[] filters) {


        String filter = filters[0];

        ContentResolver contentResolver = context.getContentResolver();

        Uri uri = ContactsContract.Contacts.CONTENT_URI;

        String[] projection = new String[]{
                ContactsContract.Contacts._ID,
                ContactsContract.Contacts.DISPLAY_NAME,
                ContactsContract.Contacts.HAS_PHONE_NUMBER
        };
        Cursor cursor;
        if(filter.length()>0) {
            cursor = contentResolver.query(
                    uri,
                    projection,
                    ContactsContract.Contacts.DISPLAY_NAME + " LIKE ?",
                    new String[]{filter},
                    ContactsContract.Contacts.DISPLAY_NAME + " ASC"
            );
        }else {

            cursor = contentResolver.query(
                    uri,
                    projection,
                    null,
                    null,
                    ContactsContract.Contacts.DISPLAY_NAME + " ASC"
            );

        }
        totalContactsCount = cursor.getCount();
        if(cursor!=null && cursor.getCount()>0){



            while(cursor.moveToNext()) {
                if (Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {

                    String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
                    String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));


                    Cursor phoneCursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                            null,
                            ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?",
                            new String[]{id},
                            null
                    );

                    if (phoneCursor != null && phoneCursor.getCount() > 0) {

                        while (phoneCursor.moveToNext()) {
                            String phId = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID));

                            String customLabel = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.LABEL));

                            String label = (String) ContactsContract.CommonDataKinds.Phone.getTypeLabel(context.getResources(),
                                    phoneCursor.getInt(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.TYPE)),
                                    customLabel
                            );

                            String phNo = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));


                            tempContactHolder.add(new Contact(phId, name, phNo, label));



                        }
                        phoneCursor.close();

                    }

                }
                loadedContactsCount++;

                publishProgress();


            }
            cursor.close();
        }

        return null;
    }

    @Override
    protected void onProgressUpdate(Void[] v){




        if(this.tempContactHolder.size()>=100) {


            contactsListAdapter.addContacts(tempContactHolder);

            this.tempContactHolder.clear();

            if(txtProgress!=null){
                txtProgress.setVisibility(View.VISIBLE);
                String progressMessage = "Loading...("+loadedContactsCount+"/"+totalContactsCount+")";
                txtProgress.setText(progressMessage);
            }

        }

    }

    @Override
    protected void onPostExecute(Void v){

        contactsListAdapter.addContacts(tempContactHolder);
        tempContactHolder.clear();

        if(txtProgress!=null) {
            txtProgress.setText("");
            txtProgress.setVisibility(View.GONE);
        }
    }
}
A cursor is used to get the contacts with phone number and each contact is exploded, if it has multiple phone numbers. A buffer tempContactHolder is used to get a set of contacts at a time and then populate the adapter's contactsList using the adapter's addContacts() method. The addContacts() will update the contactsList, apply any existing filters and then update the ListView.

Step 7: Initialize the Listview, Search textbox, 'Done' Button and load contacts in the ContactPickerActivity


package sachinmuralig.me.simplemulticontactpicker;

import android.content.Intent;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;

public class ContactsPickerActivity extends AppCompatActivity {

    ListView contactsChooser;
    Button btnDone;
    EditText txtFilter;
    TextView txtLoadInfo;
    ContactsListAdapter contactsListAdapter;
    ContactsLoader contactsLoader;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contacts_picker);

        contactsChooser = (ListView) findViewById(R.id.lst_contacts_chooser);
        btnDone = (Button) findViewById(R.id.btn_done);
        txtFilter = (EditText) findViewById(R.id.txt_filter);
        txtLoadInfo = (TextView) findViewById(R.id.txt_load_progress);


        contactsListAdapter = new ContactsListAdapter(this,new ContactsList());

        contactsChooser.setAdapter(contactsListAdapter);


        loadContacts("");



        txtFilter.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                contactsListAdapter.filter(s.toString());

            }

            @Override
            public void afterTextChanged(Editable s) {


            }
        });

        btnDone.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if(contactsListAdapter.selectedContactsList.contactArrayList.isEmpty()){
                    setResult(RESULT_CANCELED);
                }
                else{

                    Intent resultIntent = new Intent();

                    resultIntent.putParcelableArrayListExtra("SelectedContacts", contactsListAdapter.selectedContactsList.contactArrayList);
                    setResult(RESULT_OK,resultIntent);

                }
                finish();

            }
        });
    }



    private void loadContacts(String filter){

        if(contactsLoader!=null && contactsLoader.getStatus()!= AsyncTask.Status.FINISHED){
            try{
                contactsLoader.cancel(true);
            }catch (Exception e){

            }
        }
        if(filter==null) filter="";

        try{
            //Running AsyncLoader with adapter and  filter
            contactsLoader = new ContactsLoader(this,contactsListAdapter);
            contactsLoader.txtProgress = txtLoadInfo;
            contactsLoader.execute(filter);
        }catch(Exception e){
            e.printStackTrace();
        }
    }




}
Note that the search textbox has a TextWatcher attached to it, that will call the filter method of the adapter on text change. The loadContacts() method will load all contacts from device and populate the adapter data members. The 'Done' button, on click will set the intent data as the selected contact list and will return the contacts to the activity which invoked startActivityForResult.

Step 8: Read the contacts on Activity result

From the Activity which invoked the ContactPickerActivity, you can read the selected contacts from onActivityResult. A sample implementation is as follows. (See MainActivity in the github project)
@Override
    public void onActivityResult(int requestCode,int resultCode,Intent data){
        super.onActivityResult(requestCode, resultCode, data);

        if(requestCode == CONTACT_PICK_REQUEST && resultCode == RESULT_OK){

            ArrayList<Contact> selectedContacts = data.getParcelableArrayListExtra("SelectedContacts");

            String display="";
            for(int i=0;i<selectedContacts.size();i++){

                display += (i+1)+". "+selectedContacts.get(i).toString()+"\n";

            }
            contactsDisplay.setText("Selected Contacts : \n\n"+display);

        }

    }