Contacts Provider Tutorial >

Contacts Provider Exercise 1

In this exercise we will use a Contacts Provider to access the Profile information. This is a new addition to the Contacts data and represents the users’s profile who owns the Phone. The Uri for accessing the Profile Information is stored in the Constant ContactContracts.Profile.CONTENT_URI.

In this exercise we will handle the following concepts

  • User Profile Introduced in Android 4.0
  • ListFragment to display the Profile
  • ActionBar and a Profile Tab
  • AsyncTask to query and populate a Custom View in a ListFragment
[Exercise 1] [Exercise 2]

Step 1 : Create ContactsProviderExample_Start Project from Source

Create a new Android Project from source and point it to the folder ContactsProviderExample_Start. This will be the starting point of our tutorial. It contains a single activity MainActivity. Please make sure Project Build target is Android 4.0.

Step 2 : Create a Fragment ProfileFragment

  1. Create a new fragment ProfileFragment in the package com.android.example.contact. It should extend android.app.ListFragment.
    public class ProfileFragment extends ListFragment {
    
    }   
  2. Create a new Model class Pair in the package com.android.example.contact.data . This class has two class level variables key and value to hold Profile and other data.
    public class Pair{
        public String key;
        public String value;
        public Pair(String key,String value){
            this.key = key;
            this.value = value;
        }
        public String toString() {
            return "{" +  key + ", " + value + "}";
        }
    }   
  3. Create class level fields in ProfileFragment
    1. mAdapter : Custom Adapter to render a CustomView for each list item
    2. pairList : List of Pair Objects to hold the result returned by the Cursor
    3. LayoutInflator : mInflater to inflate the layout.
    4. boolean taskRun keeps tracks whether the AsycTask has been run
    private ProfileAdapter mAdapter;
    private List pairList = null;
    private LayoutInflater mInflater;
    public boolean taskRun;

Step 3 : Create a Custom Adapter

  1. Create a Custom Adapter for the ListFragment. Create a new class ProfileAdapter which extends ArrayAdapter<Pair>. This adapter will be used to retrieve each rows' key and value. This class can reside in the same Java file as ProfileFragment
    class ProfileAdapter extends ArrayAdapter {
        private static String TAG = ProfileAdapter.class.getName();
        private LayoutInflater inflator = null;
        List pairList = null;
        private int layout;
        public ProfileAdapter(Context context, int resource,
                             int textViewResourceId, List objects) {
            super(context, resource, textViewResourceId, objects);
            this.pairList = objects;
        }
        public View getView(final int position, View convertView,
            ViewGroup parent) {
        }
    }    

    Make sure appropriate packages are imported

  2. Create an Inner Static class ViewHolder which holds TextViews for each row.
  3. Define a layout for each List Item in the file res/layout/list_item.xml
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent" android:paddingLeft="10dp" android:paddingRight="10dp">
        <TextView android:id="@+id/key" android:layout_height="match_parent"
        android:layout_width="150dp" android:padding="5dp" />
        <TextView android:id="@+id/value" android:layout_height="match_parent"
        android:gravity="right" android:layout_width="fill_parent" android:textColor="@color/icsblue"
        />
    </LinearLayout>
  4. Define the color resource in the file res/values/colours.xml. This will be the colour of our values in the list.
    <resources>
        <color name="icsblue">#33b5e5</color>
    </resources>
  5. Add the following ViewGroup to the main.xml layout file. This is the container for the Fragments.
    <LinearLayout android:id="@+id/fragment_container" xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical" android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>
  6. Implement method getView(). Here we will retrieve the appropriate Pair from the pairList object depending on the position and return the View. Set value of ViewHolder key and value from the Pair object for this row . Set convertView Tag to the ViewHolder instance and return this view.
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        try {
            if (convertView == null) {
                convertView = this.inflator.inflate(layout, null);
                holder = new ViewHolder();
                holder.key = (TextView) convertView.findViewById(R.id.key);
                holder.value = (TextView) convertView.findViewById(R.id.value);
                convertView.setTag(holder);
            }else {
                holder = (ViewHolder) convertView.getTag();
            }
            Pair pair = (Pair) getItem(position);        
            String key = pair.key;
            String value = pair.value;
            holder.key.setText(key);
            holder.value.setText(value);
        } catch (Exception e) {
            Log.e(TAG, e.toString(), e);
        }
        return convertView;
    }   
  7. Add two setter methods in the ProfileAdapter. They will be called from the ProfileFragment to set the layout and the List of Pair objects
    public void setInflater(LayoutInflater mInflater) {
        this.inflator = mInflater;
    }
    
    public void setLayout(int layout) {
        this.layout = layout;
    }
  8. Add the following method in ProfileAdapter. This is a utility method for setting the Data List. This is called once the async task has finished retrieving the contacts.
    public void setDataList( List result) {
    
    }   

Step 4 : Update ProfileFragment when Activity is created

Override onActivityCreated() method of ProfileFragment

Steps:

  • Instantiate LayoutInflator mInflater using Activity.LAYOUT_INFLATER_SERVICE
  • If the AsyncTask ListProfileTask is not run execute it
  • Instantiate ArrayAdapter mAdapter
  • Set LayoutInflater and Layout R.layout.list_item for this adapter
  • Set the list Adapter for ProfileFragment as mAdapter
  • Call getListView().invalidate() so that it can be redrawn
@Override
public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mInflater = (LayoutInflater) 
    getActivity().getSystemService(Activity.LAYOUT_INFLATER_SERVICE);        
    if(!taskRun){
        FragmentTransaction ft = 
       getFragmentManager().beginTransaction();
       ListProfileTask task= new ListProfileTask(getActivity(),ft);
       task.execute();        
    }
    taskRun = true;
    mAdapter = new ProfileAdapter(getActivity(), 
    R.layout.list_item,R.id.key, pairList);
    mAdapter .setInflater(mInflater);
    mAdapter.setLayout(R.layout.list_item);
    setListAdapter(mAdapter );
    getListView().invalidate();
}  

Step 5 : Define ActionBar Listener and Add ActionBar

  1. Create a class TabListener which implements ActionBar.TabListener as an inner class of MainActivity.

    class TabListener implements ActionBar.TabListener {
        String lastTab = null;
        private Activity activity;
        public TabListener(Activity activity) {
            this.activity = activity;
        }
        @Override
        public void onTabReselected(Tab tab, FragmentTransaction ft) {
    
        }
    
        @Override
        public void onTabSelected(Tab tab, FragmentTransaction ft) {
    
        }
    
        @Override
        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    
        }
    }   

    Note : Make sure you import Appropriate classes like ActionBar, FragmentTransaction and Tab

  2. Add ActionBar and Profile Tab to onCreate() method of MainActivity. Once the profileTab has been instantiated, set tabListener object to listen for tab select event

    actionBar = getActionBar();    
    actionBar.setDisplayShowTitleEnabled(false);
    tabListener = new TabListener(this);
    profileTab = actionBar.newTab().setText("Profile")
       .setTabListener(tabListener);
    actionBar.addTab(profileTab);
    actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM |   
        ActionBar.DISPLAY_USE_LOGO);
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

Step 6 : Define an AsyncTask to Fetch the Profile Data

Define an AsyncTask to Fetch Profile details from the ContentProvider

  1. Create a new Class ListProfileTask in Package com.android.example.contact.task. This class extends AsyncTask<Void, Void, List<Pair>>
    public class ListProfileTask extends AsyncTask<Void, Void, List<Pair>> {
        private FragmentTransaction ft;
        private Activity activity;
    
        public ListProfileTask(Activity act, FragmentTransaction ft) {
            this.activity = act;
            this.ft = ft;
        }
    
        protected List doInBackground(Void... params) {
            return null;
        }
    
        protected void onPostExecute(List result) {
                
        }
    }      
  2. Implement doInBackground() method.

    Query the ContactsContract.Profile.CONTENT_URI. It will return the following fields for a Profile. Populate List of Pair object with Key and Value. Return this list

    Cursor c = activity.getContentResolver().query(
                        ContactsContract.Profile.CONTENT_URI, null,  null, null, null);
        int count = c.getCount();
    
        String[] columnNames = c.getColumnNames();
        List profileList = new LinkedList();
        boolean b = c.moveToFirst();
        int position = c.getPosition();
        if (count == 1 && position == 0) {
            for (int i = 0; i < count; i++) {
                for (int j = 0; j < columnNames.length; j++) {
                    String columnName = columnNames[j];
                    Pair pair = new Pair(columnName, 
                    c.getString(c.getColumnIndex(columnName)));
                    profileList.add(pair);
                }
                boolean b2 = c.moveToNext();
            }
        }
        c.close();
        return profileList;
    }     
  3. Implement onPostExecute() method

    1. Instantiate ProfileFragment if it is not already available.
    2. Set the List of this ProfileFragment with the List of Pair objects returned by doInBackground() method
    3. Use the FragmentTransaction to replace this profileFragment in the Main layout’s R.id.fragment_container view.
    4. Commit the FragmentTransaction.
    ProfileFragment profileFragment = (ProfileFragment) activity
                    .getFragmentManager().findFragmentByTag("Profile");
    if (profileFragment == null) {
            profileFragment = new ProfileFragment();
    }
    profileFragment.setDataList(result);
    ft.replace(R.id.fragment_container, profileFragment, "Profile");
    ft.commit();
    profileFragment.taskRun = true;
           
  4. Implement setDataListMethod() in ProfileFragment
    pairList = result;
    Activity act = getActivity();
    
    if (act != null) {
        mInflater = (LayoutInflater)  
        getActivity().getSystemService(
          Activity.LAYOUT_INFLATER_SERVICE);
        mAdapter = new ProfileAdapter(getActivity(),   
        R.layout.list_item,R.id.key, result);
        mAdapter.setLayout(R.layout.list_item);
        mAdapter .setInflater(mInflater);
        setListAdapter(mAdapter );
        getListView().invalidate();
    }   
  5. Step 7 : Populate Profile Tab

    Implement the onTabSelected method of TabListener class ( inner class of MainActivity)

    CharSequence tabText = tab.getText();
    Log.i(TAG, tabText.toString());
    if(tabText.equals("Profile")) {                        
        ListProfileTask task = new ListProfileTask(activity,ft);
        task.execute();
    }   

    Step 8 : Update Manifest Permissions

    Add following Permission to AndroidManifest.xml for reading the contacts and the Profile

    <uses-permission android:name="android.permission.READ_PROFILE"/>
    <uses-permission android:name="android.permission.READ_CONTACT"/>

    Summary

    This brings us to the end of Exercise 1. We have created a Single Tab with a Fragment which populates Profile data using the Contacts Provider. You can look at ContactsProviderExample_1 which replicates all the steps from Start in this exercise.

↑ Go to top

← Back to Contacts Provider Tutorial