Content Provider in Android

Abhishek Pathak
4 min readSep 21, 2022

--

What is Content Provider?

Content Providers is one of the android application important component that serves the purpose of a relational database to store the data of applications. Content provider in the android system is like a “central repository” in which data of the applications are stored, and it allows other applications to securely access or supplies and even modifies that data based on the requirements.

Content Providers support the four basic operations, normally called CRUD-operations.

What is Content URI(Universal Resource Identifier)?

Content URI is the key concept of content provider. To query a content provider, you can specify the query string in the form of URI.

URI: content://authority/data_type/id

  • content:// →This is prefix of all the URI and it is mandatory part of URI.
  • authority →This part should be always unique for every content provider, it specify the name of content provider. for example contacts, browser etc.
  • data_type → This indicates the type of data provider by content provider.
  • id → it is a numeric value that is used to access particular record.

Operations of Content Provider are CRUD

  • CREATE : you can create a new data.
  • READ : you can access the available data.
  • UPDATE: you can modify the existing data.
  • DELETE: you can delete the data permanently from device as well.

How does Content Provider works internally?

Android applications UI components like Activities/Fragments use an object CursorLoader to send query requests to ContentResolver. The ContentResolver object sends requests like “create, read, update, and delete” to the ContentProvider as a client. After receiving a request, ContentProvider process it and returns the expected result.

Imlementation of Contnent Provider for fetching contact list

UI components of android applications like Activity and Fragments use an object CursorLoader to send query requests to ContentResolver. The ContentResolver object sends requests (like create, read, update, and delete) to the ContentProvider as a client. After receiving a request, ContentProvider process it and returns the desired result

Step 1: Add a contact data class to handle the fields cleanly

data class Contact (val name: String, val number: String)

Step 2: Add adapter for the items of contacts list

class ContactAdapter(private val contacts: List<Contact>) :
RecyclerView.Adapter<ContactAdapter.ContactViewHolder>() {

private lateinit var binding: ContactBinding

override fun getItemCount() = contacts.size

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ContactViewHolder {
val layoutInflater = LayoutInflater.from(parent.context)
binding = ContactBinding.inflate(layoutInflater, parent, false)
return ContactViewHolder(binding.root)
}

override fun onBindViewHolder(holder: ContactViewHolder, position: Int) {
holder.apply {
val contact = contacts[position]
bind(contact)
}
}

inner class ContactViewHolder(private val v: View) : RecyclerView.ViewHolder(v) {
fun bind(contact: Contact) {
val name = v.findViewById<TextView>(R.id.name)
val number = v.findViewById<TextView>(R.id.number)
name.text = contact.name
number.text = contact.number
}
}
}

Step 3: Now implement the content provider

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var contactAdapter: ContactAdapter
private var contacts = ArrayList<Contact>()
private var contactsStr = ArrayList<String>()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.apply {
fab.setOnClickListener {
syncContacts()
}
}
}

@SuppressLint("ObsoleteSdkInt")
private fun syncContacts() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& checkSelfPermission(Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED
) {
requestPermissions(
arrayOf(Manifest.permission.READ_CONTACTS),
PERMISSION_REQ_READ_CONTACTS
)
} else {
fetchContacts()
setAdapter()
}
}

override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == PERMISSION_REQ_READ_CONTACTS) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
syncContacts()
} else {
Snackbar.make(
this,
binding.root,
"Permission Not Granted",
Snackbar.LENGTH_SHORT
)
.show()
}
}
}

@SuppressLint("Range", "Recycle")
private fun fetchContacts() {

val resolver: ContentResolver = contentResolver
val cursor = resolver.query(
ContactsContract.Contacts.CONTENT_URI,
null, null, null, null
)
if (cursor!!.count > 0) {
while (cursor.moveToNext()) {
val id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID))
val name =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
val phoneNum =
cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))
.toInt()
if (phoneNum > 0) {
val cursorPhone = contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " =?",
arrayOf(id),
null
)
if (cursorPhone!!.count > 0) {
while (cursorPhone.moveToNext()) {
val phoneNumValue = cursorPhone.getString(
cursorPhone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)
)
contactsStr.add("$name|$phoneNumValue")
}
}
cursorPhone.close()

}
}
}
for (contact in contactsStr) {
val contactSplit = contact.split("|")
Log.i("CONTACT", contactSplit.toString())
contacts.add(Contact(contactSplit[0], contactSplit[1]))
}
}

private fun setAdapter() {
contactAdapter = ContactAdapter(contacts)
binding.apply {
contactRecyclerView.layoutManager = LinearLayoutManager(this@MainActivity)
contactRecyclerView.adapter = contactAdapter

ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val pos = viewHolder.adapterPosition
contacts.removeAt(pos)
contactAdapter.notifyItemRemoved(pos)
Toast.makeText(this@MainActivity, "Contact Deleted", Toast.LENGTH_LONG)
.show()
}
}).attachToRecyclerView(contactRecyclerView)

ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.RIGHT) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}

override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val pos = viewHolder.adapterPosition
contacts.removeAt(pos)
contactAdapter.notifyItemRemoved(pos)
Toast.makeText(this@MainActivity, "Contact Archived", Toast.LENGTH_LONG)
.show()
}
}).attachToRecyclerView(contactRecyclerView)
}
}

companion object {
const val PERMISSION_REQ_READ_CONTACTS = 100
}
}

Conclusion:

you have learned all about the two types of URIs for content providers and basic concepts of using it with an example.

Thanks for reading this article. Be sure to click 👏 below to applause this article if you found it helpful. It means a lot to me.

--

--