How to write a custom content provider in android? with a sample application using our own custom content provider.

By | September 22, 2013

Hello all

we all know what are content providers right?
Those who are unaware of content providers please visit this link http://developer.android.com/guide/topics/providers/content-providers.html

Custom Content Provider

Custom Content Provider

In a breif content Providers are

Content providers manage access to a structured set of data. They encapsulate the data, and provide mechanisms for defining data security. Content providers are the standard interface that connects data in one process with code running in another process.

When you want to access data in a content provider, you use the ContentResolver object in your application’s Context to communicate with the provider as a client. The ContentResolver object communicates with the provider object, an instance of a class that implements ContentProvider. The provider object receives data requests from clients, performs the requested action, and returns the results.

This sample Demo is creating custom content provider using SQLite Database.

Now we will see how we can create our own custom content provider.

Tn this example I have three classes.
1. MainActivity.java
2. NotesContentProvider.java
3. NotesMetaData.java

At first we will look at “NotesMetaData.java”.

package com.coderzheaven.custom_contentproviderdemo;

import android.net.Uri;
import android.provider.BaseColumns;

public class NotesMetaData {

	public NotesMetaData() {

	}

	// A content URI is a URI that identifies data in a provider. Content URIs
	// include the symbolic name of the entire provider (its authority)
	public static final String AUTHORITY = "com.coderzheaven.custom_contentproviderdemo.Notes";
	public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
			+ "/notes");

	public static final String DATABASE_NAME = "notes.db";
	public static final int DATABASE_VERSION = 1;

	public static final String CONTENT_TYPE_NOTES_ALL = "vnd.android.cursor.dir/vnd.coderz.notes";
	public static final String CONTENT_TYPE_NOTES_ONE = "vnd.android.cursor.item/vnd.coderz.notes";

	public class NotesTable implements BaseColumns {

		private NotesTable() {

		}

		public static final String TABLE_NAME = "tbl_notes";

		public static final String ID = "_id";
		public static final String TITLE = "title";
		public static final String CONTENT = "content";
	}

}

NotesMetaData class defines some required constant values to use:

+ AUTHORITY: this is the name of your content provider

+ CONTENT_URI: is your content provider URI for other applications to access data from it.

Let’s say if you want to get all notes, it would be like:

content://”com.coderzheaven.custom_contentproviderdemo.Notes/notes”

To retrieve a specific note:

content://com.coderzheaven.custom_contentproviderdemo.Notes/notes/5″

Now the “NotesContentProvider.java”

package com.coderzheaven.custom_contentproviderdemo;

import java.util.HashMap;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;

public class NotesContentProvider extends ContentProvider {

	private static final UriMatcher sUriMatcher;

	private static final int NOTES_ALL = 1;
	private static final int NOTES_ONE = 2;

	static {
		sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		sUriMatcher.addURI(NotesMetaData.AUTHORITY, "notes", NOTES_ALL);
		sUriMatcher.addURI(NotesMetaData.AUTHORITY, "notes/#", NOTES_ONE);
	}

	// Map table columns
	private static final HashMap<String, String> sNotesColumnProjectionMap;
	static {
		sNotesColumnProjectionMap = new HashMap<String, String>();
		sNotesColumnProjectionMap.put(NotesMetaData.NotesTable.ID,
				NotesMetaData.NotesTable.ID);
		sNotesColumnProjectionMap.put(NotesMetaData.NotesTable.TITLE,
				NotesMetaData.NotesTable.TITLE);
		sNotesColumnProjectionMap.put(NotesMetaData.NotesTable.CONTENT,
				NotesMetaData.NotesTable.CONTENT);
	}

	private static class NotesDBHelper extends SQLiteOpenHelper {

		public NotesDBHelper(Context c) {
			super(c, NotesMetaData.DATABASE_NAME, null,
					NotesMetaData.DATABASE_VERSION);
		}

		private static final String SQL_QUERY_CREATE = "CREATE TABLE "
				+ NotesMetaData.NotesTable.TABLE_NAME + " ("
				+ NotesMetaData.NotesTable.ID
				+ " INTEGER PRIMARY KEY AUTOINCREMENT, "
				+ NotesMetaData.NotesTable.TITLE + " TEXT NOT NULL, "
				+ NotesMetaData.NotesTable.CONTENT + " TEXT NOT NULL" + ");";

		@Override
		public void onCreate(SQLiteDatabase db) {
			db.execSQL(SQL_QUERY_CREATE);
		}

		private static final String SQL_QUERY_DROP = "DROP TABLE IF EXISTS "
				+ NotesMetaData.NotesTable.TABLE_NAME + ";";

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVer, int newVer) {
			db.execSQL(SQL_QUERY_DROP);
			onCreate(db);
		}
	}

	// create a db helper object
	private NotesDBHelper mDbHelper;

	@Override
	public boolean onCreate() {
		mDbHelper = new NotesDBHelper(getContext());
		return false;
	}

	@Override
	public int delete(Uri uri, String where, String[] whereArgs) {
		SQLiteDatabase db = mDbHelper.getWritableDatabase();
		int count = 0;
		switch (sUriMatcher.match(uri)) {
		case NOTES_ALL:
			count = db.delete(NotesMetaData.NotesTable.TABLE_NAME, where,
					whereArgs);
			break;

		case NOTES_ONE:
			String rowId = uri.getPathSegments().get(1);
			count = db.delete(
					NotesMetaData.NotesTable.TABLE_NAME,
					NotesMetaData.NotesTable.ID
							+ " = "
							+ rowId
							+ (!TextUtils.isEmpty(where) ? " AND (" + where
									+ ")" : ""), whereArgs);
			break;

		default:
			throw new IllegalArgumentException("Unknown URI: " + uri);
		}

		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

	@Override
	public String getType(Uri uri) {

		switch (sUriMatcher.match(uri)) {
		case NOTES_ALL:
			return NotesMetaData.CONTENT_TYPE_NOTES_ALL;

		case NOTES_ONE:
			return NotesMetaData.CONTENT_TYPE_NOTES_ONE;

		default:
			throw new IllegalArgumentException("Unknown URI: " + uri);
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues values) {

		// you cannot insert a bunch of values at once so throw exception
		if (sUriMatcher.match(uri) != NOTES_ALL) {
			throw new IllegalArgumentException(" Unknown URI: " + uri);
		}

		// Insert once row
		SQLiteDatabase db = mDbHelper.getWritableDatabase();
		long rowId = db.insert(NotesMetaData.NotesTable.TABLE_NAME, null,
				values);
		if (rowId > 0) {
			Uri notesUri = ContentUris.withAppendedId(
					NotesMetaData.CONTENT_URI, rowId);
			getContext().getContentResolver().notifyChange(notesUri, null);
			return notesUri;
		}
		throw new IllegalArgumentException("<Illegal>Unknown URI: " + uri);
	}

	// Get values from Content Provider
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
		switch (sUriMatcher.match(uri)) {
		case NOTES_ALL:
			builder.setTables(NotesMetaData.NotesTable.TABLE_NAME);
			builder.setProjectionMap(sNotesColumnProjectionMap);
			break;

		case NOTES_ONE:
			builder.setTables(NotesMetaData.NotesTable.TABLE_NAME);
			builder.setProjectionMap(sNotesColumnProjectionMap);
			builder.appendWhere(NotesMetaData.NotesTable.ID + " = "
					+ uri.getLastPathSegment());
			break;

		default:
			throw new IllegalArgumentException("Unknown URI: " + uri);
		}

		SQLiteDatabase db = mDbHelper.getReadableDatabase();
		Cursor queryCursor = builder.query(db, projection, selection,
				selectionArgs, null, null, null);
		queryCursor.setNotificationUri(getContext().getContentResolver(), uri);

		return queryCursor;
	}

	@Override
	public int update(Uri uri, ContentValues values, String where,
			String[] whereArgs) {

		SQLiteDatabase db = mDbHelper.getWritableDatabase();
		int count = 0;
		switch (sUriMatcher.match(uri)) {
		case NOTES_ALL:
			count = db.update(NotesMetaData.NotesTable.TABLE_NAME, values,
					where, whereArgs);
			break;

		case NOTES_ONE:
			String rowId = uri.getLastPathSegment();
			count = db
					.update(NotesMetaData.NotesTable.TABLE_NAME, values,
							NotesMetaData.NotesTable.ID	+ " = "	+ rowId	+ (!TextUtils.isEmpty(where) ? " AND ("	+ ")" : ""), whereArgs);

		default:
			throw new IllegalArgumentException("Unknown URI: " + uri);
		}

		getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

}

When you insert/update/delete, always remember to call “notifyChange()” to the URI that has been used.

When you query values, always remember to to call “setNotificationUri()” for Cursor.

That’s done for creating your custom content provider. In order to use, you need to register it to AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.coderzheaven.custom_contentproviderdemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.coderzheaven.custom_contentproviderdemo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        
        
        <provider
            android:name="com.coderzheaven.custom_contentproviderdemo.NotesContentProvider"
            android:authorities="com.coderzheaven.custom_contentproviderdemo.Notes" >
        </provider>
        
    </application>

</manifest>

Now we look how we can use it in an activity

MainActivity.java

package com.coderzheaven.custom_contentproviderdemo;

import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.coderzheaven.custom_contentproviderdemo.NotesMetaData.NotesTable;

public class MainActivity extends Activity implements OnClickListener {

	private final static String TAG = "CustomContentProvider";
	EditText title, content, delete_id;
	Button add, update, delete, showNotes;

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

		title = (EditText) findViewById(R.id.title);
		content = (EditText) findViewById(R.id.content);
		delete_id = (EditText) findViewById(R.id.delete_id);

		// add Click Listners
		add = (Button) findViewById(R.id.button_add);
		add.setOnClickListener(this);
		update = (Button) findViewById(R.id.button_update);
		update.setOnClickListener(this);
		delete = (Button) findViewById(R.id.button_delete);
		delete.setOnClickListener(this);
		showNotes = (Button) findViewById(R.id.show_notes);
		showNotes.setOnClickListener(this);

		getNotes();

	}

	void addNote() {
		if (title.getText().toString().length() > 0
				&& content.getText().toString().length() > 0) {
			ContentValues values = new ContentValues();
			values.put(NotesTable.TITLE, title.getText().toString());
			values.put(NotesTable.CONTENT, content.getText().toString());
			getContentResolver().insert(NotesMetaData.CONTENT_URI, values);
			Log.i(TAG, "Inserted");
			makeToast("Note Added");
		} else {
			makeToast("Empty Field");
		}
	}

	void deleteNote(String str_id) {
		try {
			int id = Integer.parseInt(str_id);
			Log.i(TAG, "Deleting with id = " + id);
			getContentResolver().delete(NotesMetaData.CONTENT_URI,
					NotesMetaData.NotesTable.ID + " = " + id, null);
			Log.i(TAG, "Deleted");
			makeToast("Note Deleted");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	void updateNote(String str_id) {
		try {
			int id = Integer.parseInt(str_id);
			Log.i(TAG, "Updating with id = " + id);
			ContentValues values = new ContentValues();
			values.put(NotesTable.TITLE, title.getText().toString());
			values.put(NotesTable.CONTENT, content.getText().toString());
			getContentResolver().update(NotesMetaData.CONTENT_URI, values,
					NotesMetaData.NotesTable.ID + " = " + id, null);
			makeToast("Note Updated");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	void getNotes() {

		Cursor cur = getContentResolver().query(NotesMetaData.CONTENT_URI,
				null, null, null, null);

		if (cur.getCount() > 0) {
			Log.i(TAG, "Showing values.....");
			while (cur.moveToNext()) {
				String Id = cur.getString(cur.getColumnIndex(NotesTable.ID));
				String title = cur.getString(cur
						.getColumnIndex(NotesTable.TITLE));
				System.out.println("Id = " + Id + ", Note Title : " + title);
			}
			makeToast("Check the LogCat for Notes");
		} else {
			Log.i(TAG, "No Notes added");
			makeToast("No Notes added");
		}
	}

	@Override
	public void onClick(View arg0) {
		if (arg0 == add) {
			addNote();
		}
		if (arg0 == update) {
			// update note with Id
			updateNote(delete_id.getText().toString());
		}
		if (arg0 == delete) {
			// delete note with Id
			deleteNote(delete_id.getText().toString());
		}
		if (arg0 == showNotes) {
			// show all
			getNotes();
		}
	}

	private void makeToast(String text) {
		Toast.makeText(this, text, Toast.LENGTH_LONG).show();
	}

}

You can download the complete source code from this demo here.

Leave a Reply

Your email address will not be published. Required fields are marked *