Store data using SQLite by fetching from ContentProvider and display using RecyclerView | This Application uses ContentProvider to fetch contacts from contact app then stores them in SQLite database and it is displayed using the RecyclerView. It also demonstrates the use of the Loader framework which allows loading data asynchronously.

Output:

Download Source Code: [media-downloader media_id=”1188″ texts=”ContactList.zip”]

Download APK: [media-downloader media_id=”1190″ texts=”ContactList apk”]

Wants to Learn Advanced Android Application development from scratch- Beyond Basics

 

  1. Create a new project in Android Studio.
  2. Add RecyclerView dependency in-app level build.gradle file.
compile 'com.android.support:recyclerview-v7:25.1.1'

3. AndroidManifest.xml:

Add the following in your AndroidManifest.xml file to request the permission to read contacts from the user.

<uses-permission android:name="android.permission.READ_CONTACTS"/>
  1. create or update the activity_list.XML 

This file contains RecyclerView.

<?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"
    tools:context="com.mytrendin.recyclerview.ListActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rvContacts"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>

</RelativeLayout>

5. activity_listitems.XML 

This file is used to render inside RecyclerView.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="56dp"
    android:gravity="center_vertical|left"
    android:orientation="horizontal"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"
    android:id="@+id/cont_item_root" android:background="#ffffff">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="3dp"
        android:paddingBottom="3dp"
        android:background="@drawable/cardview_bg">

        <ImageView
            android:id="@+id/ivContactImage"
            android:layout_width="55dp"
            android:layout_height="55dp"
            android:layout_marginLeft="15dp"
            android:layout_marginStart="10dp"
            android:paddingTop="5dp"
            android:paddingBottom="5dp"
            android:src="@drawable/photo" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:gravity="center_vertical">

            <TextView
                android:id="@+id/tvContactName"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:layout_marginStart="10dp"
                android:textSize="16sp"
                android:textColor="@android:color/primary_text_light"
                android:text="Name"/>

            <TextView
                android:id="@+id/tvPhoneNumber"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:layout_marginStart="10dp"
                android:textSize="14sp"
                android:textColor="@android:color/primary_text_light"
                android:text="Phone"/>

        </LinearLayout>

    </LinearLayout>
</RelativeLayout>

6. AllContacts.java

package com.mytrendin.recyclerview;

import android.app.LoaderManager;
import android.content.ContentResolver;
import android.content.CursorLoader;
import android.content.Loader;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.os.Build;
import android.provider.ContactsContract;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;

public class AllContacts extends AppCompatActivity implements 
LoaderManager.LoaderCallbacks<Cursor>{

    RecyclerView rvContacts;
    database myDB;
    Boolean permission=false;
    int exist;
    private String mOrderBy = 
                ContactsContract.Contacts.DISPLAY_NAME_PRIMARY;
    public static final int 
                MY_PERMISSIONS_REQUEST_READ_CONTACTS = 99;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_list);
     myDB = new database(this);
     exist = myDB.tableExists();
     rvContacts = (RecyclerView) 
                      findViewById(R.id.rvContacts);
     if (android.os.Build.VERSION.SDK_INT >= 
                         Build.VERSION_CODES.M) {
         permission = checkContactsPermission();
         if(permission){
            if(exist==0)
            {
                getLoaderManager().initLoader(1, null, this);
                displayAllContacts();
            }
         }
     }
     else {
         if(exist == 0) {
             getLoaderManager().initLoader(1, null, this);
             displayAllContacts();
         }
     }
     displayAllContacts();
 }

 private void displayAllContacts() {
     List<ContactListItem> contactList = new ArrayList();
     ContactListItem contactListItem;

     Cursor c = myDB.getAllData();
     if(c!=null && c.getCount()>0)
     {
         while (c.moveToNext()) {
            String name = c.getString(1);
            String phoneNo = c.getString(0);
            contactListItem = new ContactListItem();
            contactListItem.setContactName(name);
            contactListItem.setContactNumber(phoneNo);
            contactList.add(contactListItem);
         }
     }
     AllContactsAdapter contactAdapter = new 
       AllContactsAdapter(contactList, getApplicationContext());
     rvContacts.setLayoutManager(new LinearLayoutManager(this));
     rvContacts.setAdapter(contactAdapter);
 }

 @Override
 public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
    if(i==1) {
      return new CursorLoader(this, 
      ContactsContract.Contacts.CONTENT_URI, 
      null, 
      null, 
      null,
      "upper("+
      ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME+
      ") ASC");
   }
   return null;
 }

 @Override
 public void onLoadFinished(Loader<Cursor> loader,Cursor cursor)
 {
   if (cursor != null && cursor.getCount() > 0) {
     while (cursor.moveToNext()) {
       int hasPhoneNumber = Integer.parseInt(cursor.getString
           (cursor.getColumnIndex
           (ContactsContract.Contacts.HAS_PHONE_NUMBER)));
       if (hasPhoneNumber > 0) {
         String id = cursor.getString(cursor.getColumnIndex
            (ContactsContract.Contacts._ID));
         String name = cursor.getString(cursor.getColumnIndex
            (ContactsContract.Contacts.DISPLAY_NAME_PRIMARY));
         ContentResolver contentResolver = getContentResolver();
         Cursor phoneCursor = contentResolver.query(
            ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
            null,
            ContactsContract.CommonDataKinds.Phone.CONTACT_ID +
            " = ?",
            new String[]{id},
            null);
         if (phoneCursor.moveToNext()) {
            String phoneNumber = phoneCursor.getString
             (phoneCursor.getColumnIndex
             (ContactsContract.CommonDataKinds.Phone.NUMBER));
            phoneCursor.close();
            myDB.addContact(name,phoneNumber);
         }
       }
    }
    displayAllContacts();
    }
  }

  @Override
  public void onLoaderReset(Loader<Cursor> loader) {
  }

  public boolean checkContactsPermission() {
   if(ContextCompat.checkSelfPermission(this,
        android.Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {
    if(ActivityCompat.shouldShowRequestPermissionRationale(this,
        android.Manifest.permission.READ_CONTACTS)) {
        ActivityCompat.requestPermissions(this,
        new String[]{android.Manifest.permission.READ_CONTACTS},
        MY_PERMISSIONS_REQUEST_READ_CONTACTS);
    } 
    else {
        ActivityCompat.requestPermissions(this,
        new String[]{android.Manifest.permission.READ_CONTACTS},
        MY_PERMISSIONS_REQUEST_READ_CONTACTS);
    }
    return false;
   } 
   else {
    return true;
   }
 }

 @Override
 public void onRequestPermissionsResult(int requestCode,
           String permissions[], int[] grantResults) {
     switch (requestCode) {
       case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
           if (grantResults.length > 0 && 
        grantResults[0] == PackageManager.PERMISSION_GRANTED) {
             if (ContextCompat.checkSelfPermission(this,
               android.Manifest.permission.READ_CONTACTS)
               == PackageManager.PERMISSION_GRANTED) {
               exist = myDB.tableExists();
                 if(exist==0)
                 {
                   getLoaderManager().initLoader(1, null, this);
                 }
                 return;
               }
             } else {
                Toast.makeText(this, "permission denied",
                   Toast.LENGTH_LONG).show();
              }
            return;
         }
     }
   }
}

From Android 6.0 (API level 23) users need to grant permissions to the app while it is running rather than at installation time Therefore not all the permissions will be accepted by default in those Android Versions. we will apply a Version check before executing our code. If the Android Version is equal or above the Marshmallow version then permission has to be granted first, by the user.

if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
checkStoragePermission();
}

checkStoragePermission() function checks if the app has permission to read the external storage of the device.

public boolean checkStoragePermission() {
  if (ContextCompat.checkSelfPermission(this,
     android.Manifest.permission.READ_EXTERNAL_STORAGE)
     != PackageManager.PERMISSION_GRANTED) {
   if (ActivityCompat.shouldShowRequestPermissionRationale(this,
    android.Manifest.permission.READ_EXTERNAL_STORAGE)) {
    ActivityCompat.requestPermissions(this, new String[]
    {android.Manifest.permission.READ_EXTERNAL_STORAGE},
    MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
   } else {
   ActivityCompat.requestPermissions(this,new String[]
     {android.Manifest.permission.READ_EXTERNAL_STORAGE},
     MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
    }
    return false;
  } else {
  return true;
 }
}

Then once the permission request is accepted or rejected the result will be handled by the onRequestPermissionsResult method. If the permission was granted then in this method we’ll check if the database table we created is empty or not. If it is then we’ll initialize the LoaderManager.

exist = myDB.tableExists();
if(exist==0)
{
      getLoaderManager().initLoader(1, null, this);
 }

To make use of LoaderManager we need to make our class implement this LoaderManager.LoaderCallbacks<Cursor>.

When we implement this interface then we’ll have to override the methods that are part of this interface:

  1. onCreateLoader: Here we pass the ContentProvider Uri i.e. telling which particular content provider you want to access data from.
  2. onLoadFinished: In this UI application is to be initialized
  3. onLoaderReset

Once the Loader is initialized it will trigger the callback methods of the LoaderCallbacks.

Related:

Display data in Recyclerview using SQLiteCursor Class in android

How to insert the data into SQLite database in Android

SQLite database tutorial

Introduction to SQLite Database

Android Firebase working with Realtime Database and Storage

Insert and retrieve data using Android ORMLite database

CursorLoader is a subclass of AsyncTaskLoader. So when the CursorLoader is used it indirectly calls AsyncTaskLoader so that all data is loaded on the worker thread and not on the main thread.

 @Override
    public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
        if(i==1) {
            return new CursorLoader(this, 
            ContactsContract.Contacts.CONTENT_URI, 
            null, 
            null, 
            null,
            "upper("+
   ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + ") ASC");
        }
        return null;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, 
           Cursor cursor) {
     if (cursor != null && cursor.getCount() > 0) {
        while (cursor.moveToNext()) {
         int hasPhoneNumber = Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)));
            if (hasPhoneNumber > 0) {
              String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
              String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY));
              ContentResolver contentResolver = getContentResolver();
              Cursor phoneCursor = contentResolver.query(
              ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                null,
            ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ?",
               new String[]{id}, null);
                 if (phoneCursor.moveToNext()) {
                 String phoneNumber = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                 phoneCursor.close();
                 myDB.addContact(name,phoneNumber);
                 }
                }
            }
            displayAllContacts();
        }
    }

displayAllContacts method:

This Method will get all the data from our database in the form of a cursor. We’ll parse through this cursor to obtain our contacts list. The data will be stored in contactListItem class’s object. This object will then be passed to AllContactsAdapter class.

private void displayAllContacts() {

            List<ContactListItem> contactList = new ArrayList();
            ContactListItem contactListItem;

            Cursor c = myDB.getAllData();
            if(c!=null && c.getCount()>0)
            {
                while (c.moveToNext()) {

                    String name = c.getString(1);
                    String phoneNo = c.getString(0);
                    contactListItem = new ContactListItem();
                    contactListItem.setContactName(name);
                    contactListItem.setContactNumber(phoneNo);
                    contactList.add(contactListItem);
                }
            }
            AllContactsAdapter contactAdapter = new 
AllContactsAdapter(contactList, getApplicationContext());
        rvContacts.setLayoutManager(new LinearLayoutManager(this));
        rvContacts.setAdapter(contactAdapter);
    }

Then the Layout would be set for RecyclerView using

rvContacts.setLayoutManager(new LinearLayoutManager(this));

And then the adapter of RecyclerView will be set.

  1. ContactListItem.java
package com.mytrendin.recyclerview;

public class ContactListItem {
    private String ContactImage;
    private String ContactName;
    private String ContactNumber;

    public String getContactName() {
        return ContactName;
    }

    public void setContactName(String contactName) {
        ContactName = contactName;
    }

    public String getContactNumber() {
        return ContactNumber;
    }

    public void setContactNumber(String contactNumber) {
        ContactNumber = contactNumber;
    }
}

This file will hold the values in the form getter and setter methods for the contact name and contact number.

  1. database.java:

This file has all the operations of SQLite database.

package com.mytrendin.recyclerview;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class database extends SQLiteOpenHelper {

    private static final int DATABASE_VERSION = 1;
    private static final String DATABASE_NAME = "Contacts.database";
    private static final String TABLE_NAME = "Contacts";
    private static final String COLUMN_NAME = "contact_name";
    private static final String COLUMN_NUMBER = "contact_number";
  
  public database(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        //execute the create table query
        sqLiteDatabase.execSQL("CREATE TABLE " + TABLE_NAME + "(" +
                COLUMN_NAME + " TEXT, " +
                COLUMN_NUMBER + " TEXT " +
                ");");
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);        
onCreate(sqLiteDatabase);
    }

    //add row to database
    public void addContact( String ContactNumber, String ContactName){
        ContentValues values = new ContentValues();
            values.put(COLUMN_NAME, ContactName);
            values.put(COLUMN_NUMBER, ContactNumber);
            SQLiteDatabase db = getWritableDatabase();
            db.insert(TABLE_NAME, null, values);
            db.close();
    }

    public Cursor getAllData() {
        SQLiteDatabase db = this.getWritableDatabase(); //create/open db for writing
        Cursor res = db.rawQuery("SELECT * FROM " + TABLE_NAME , null);  //runs SQL query and returns cursor over result
        /*the second paramenter to rawQuery is cancellableSignal which signifies that what needs to be done when the operation is cancelled*/
        return res;
    }

    public int tableExists(){
       SQLiteDatabase db = this.getWritableDatabase();
        Cursor cursor = db.rawQuery("SELECT  * FROM " + TABLE_NAME,null);
            int count = cursor.getCount();
            cursor.close();
            return count;
    }
}

In the onCreate method of database write the query to create the table.

@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
     //execute the create table query
     sqLiteDatabase.execSQL("CREATE TABLE " + TABLE_NAME + "(" +
           COLUMN_NAME + " TEXT, " +
           COLUMN_NUMBER + " TEXT " +
           ");");
}

9. AllContactsAdapter.java

package com.mytrendin.recyclerview;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;

public class AllContactsAdapter extends RecyclerView.Adapter<AllContactsAdapter.ContactViewHolder>{

    private List<ContactListItem> contactList;
    private Context mContext;
    public AllContactsAdapter(List<ContactListItem> contactList, Context mContext){
        this.contactList = contactList;
        this.mContext = mContext;
    }

    @Override
    public ContactViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.activity_listitems, null);
        ContactViewHolder contactViewHolder = new ContactViewHolder(view);
        return contactViewHolder;
    }

    @Override
    public void onBindViewHolder(ContactViewHolder holder, int position) {
        ContactListItem contactListItem = contactList.get(position);
        holder.tvContactName.setText(contactListItem.getContactName());
        holder.tvPhoneNumber.setText(contactListItem.getContactNumber());
    }

    @Override
    public int getItemCount() {
        return contactList.size();
    }

    public static class ContactViewHolder extends RecyclerView.ViewHolder{
        ImageView ivContactImage;
        TextView tvContactName;
        TextView tvPhoneNumber;

        public ContactViewHolder(View itemView) {
            super(itemView);
            ivContactImage = (ImageView) itemView.findViewById(R.id.ivContactImage);
            tvContactName = (TextView) itemView.findViewById(R.id.tvContactName);
            tvPhoneNumber = (TextView) itemView.findViewById(R.id.tvPhoneNumber);
        }
    }
}
  1. update colors.XML file
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#00B873</color>
    <color name="colorPrimaryDark">#303F9F</color>
     <color name="colorBackroundWhite">#f0f2ea</color>
</resources>
  1. create one XML file inside drawable folder named cardview_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
    <solid android:color="#ffffff" />
    <stroke android:width="1.5dp" android:color="@color/colorBackroundWhite"/>
    <corners android:radius="2dp"/>
</shape>

This file will create card view look. If You are Beginner Learn Android application development from the scratch


If you really liked the article, please subscribe to our YouTube Channel for videos related to this article.Please find us on Twitter and Facebook.

If you really liked the article, please subscribe to our YouTube Channel for videos related to this article.Please find us on Twitter and Facebook.

Related Posts