sqlite database is private to the application. So if you want to access your sqlite database in any other application, you can use content provider. Even there are some benifits of using content provider private to your app e.g. if you are using CursorLoader or sync adapter in your app.
First we need to declare content provider in AndroidMenifest.xml file
These are minimum required attributes to declare your content provider.
android:name – name of the subclass of ContentProvider
android:authorities – list of the authorities
other attributes that you can use
android:authorities="list"
android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:grantUriPermissions=["true" | "false"]
android:icon="drawable resource"
android:initOrder="integer"
android:label="string resource"
android:multiprocess=["true" | "false"]
android:name="string"
android:permission="string"
android:process="string"
android:readPermission="string"
android:syncable=["true" | "false"]
android:writePermission="string"
http://developer.android.com/guide/topics/manifest/provider-element.html
android:enable – Whether or not the content provider can be instantiated by the system. You can also toggle this from code PackageManager and setComponentEnabledSetting()
android:exported- if you want to create content provider private to your app, just set adnroid:exported = false;
android:grantUriPermissions – the app with full access of content provider can grant temporary access to other app.
e.g.
Granting permission:
uri = "content://com.vimviv.mailprovider/attachments/42";
Context.grantUriPermission("com.example.test", uri,
Intent.FLAG_GRANT_READ_URI_PERMISSION);
or
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(i);
android:icon- icon for content provider.
android:label – label for content provider.
android:multiprocess – ‘true’ if content provider instance can run in multi process.
android:readPermission – Permission that client must have to read this content provider.
android:writePermission : Permission that client must have to change data of this content provider
android:permission : permission for read or write
e.g.
android:process - process in which content provider will run.
Content Uri:
We can identify data in content provider using content uri. It has following structure.
content://AUTHORITY/path
content://AUTHORITY/path/id
Authority: a symbolic unique name of content provider.
path: name of the table or file.
id: id of the single record
Uri Matcher:
Provides a mechanism to identify all the incoming uri patterns. e.g. content://AUTHORITY/table_name/21 matches a content uri of single record with id 21.
private static final UriMatcher sUriMatcher;
private static final int INCOMING_CONTACT_COLLECTION_URI_INDICATOR = 1;
private static final int INCOMING_SINGLE_CONTACT_URI_INDICATOR = 2;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, "contacts",
INCOMING_CONTACT_COLLECTION_URI_INDICATOR);
sUriMatcher.addURI(AUTHORITY, "contacts/#",
INCOMING_SINGLE_CONTACT_URI_INDICATOR);
}
switch (sUriMatcher.match(uri)) {
// If the incoming URI was for all of table contact
case INCOMING_CONTACT_COLLECTION_URI_INDICATOR:
// do something
break;
// If the incoming URI was for a single row
case INCOMING_CONTACT_COLLECTION_URI_INDICATOR:
id = uri.getLastPathSegment();
break;
default:
// URI is not recognized
}
Mimetype:
getType method returns mimetype of data returned by the content URI argument.
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.com.vimviv.provider.contact";
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.com.vimviv.provider.contact";
@Override
public String getType(Uri uri) {
switch (sUriMatcher.match(uri)) {
case INCOMING_CONTACT_COLLECTION_URI_INDICATOR:
return CONTENT_TYPE;
case INCOMING_SINGLE_CONTACT_URI_INDICATOR:
return CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
Database columns:
public class ContactColumns implements BaseColumns
{
private ContactColumns() {}
public static final String DEFAULT_SORT_ORDER = "name ASC";
//Additional Columns start here.
public static final String NAME = "name";
public static final String PHONE_NUMBER = "phoneNumber";
public static final String EMAIL = "email";
public static final String ADDRESS = "address";
public static final String DOB = "dob";
//Integer from System.currentTimeMillis()
public static final String CREATED_DATE = "created";
//Integer from System.currentTimeMillis()
public static final String MODIFIED_DATE = "modified";
}
Database helper class:
create database helper class for create and upgrade database.
/**
* Setup/Create Database This class helps open, create, and upgrade the
* database file.
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context,
DATABASE_NAME,
null,
DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db)
{
Log.d(TAG, "inner oncreate called");
db.execSQL("CREATE TABLE " + TABLE_NAME + " ("
+ ContactColumns._ID + " INTEGER PRIMARY KEY,"
+ ContactColumns.NAME + " TEXT,"
+ ContactColumns.PHONE_NUMBER + " TEXT,"
+ ContactColumns.EMAIL + " TEXT,"
+ ContactColumns.ADDRESS + " TEXT,"
+ ContactColumns.DOB + " INTEGER,"
+ ContactColumns.CREATED_DATE + " INTEGER,"
+ ContactColumns.MODIFIED_DATE + " INTEGER"
+ ");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
Log.d(TAG, "inner onupgrade called");
Log.w(TAG, "Upgrading database from version "
+ oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " +
TABLE_NAME);
onCreate(db);
}
}
projection map:
private static HashMapsContactsProjectionMap; static { sContactsProjectionMap = new HashMap (); sContactsProjectionMap.put(ContactColumns._ID, ContactColumns._ID); // name, phone number, email,address sContactsProjectionMap.put(ContactColumns.NAME, ContactColumns.NAME); sContactsProjectionMap.put(ContactColumns.PHONE_NUMBER, ContactColumns.PHONE_NUMBER); sContactsProjectionMap.put(ContactColumns.ADDRESS, ContactColumns.ADDRESS); sContactsProjectionMap.put(ContactColumns.EMAIL, ContactColumns.EMAIL); sContactsProjectionMap.put(ContactColumns.DOB, ContactColumns.DOB); // created date, modified date sContactsProjectionMap.put(ContactColumns.CREATED_DATE, ContactColumns.CREATED_DATE); sContactsProjectionMap.put(ContactColumns.MODIFIED_DATE, ContactColumns.MODIFIED_DATE); }
Now we will override methods
query:
In query method we will filter uri using UriMatcher, if query is for single row, we will first get the id from uri using getLastPathSegment() method, after that append a where clause in sql query.
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
switch (sUriMatcher.match(uri)) {
case INCOMING_CONTACT_COLLECTION_URI_INDICATOR:
qb.setTables(TABLE_NAME);
qb.setProjectionMap(sContactsProjectionMap);
break;
case INCOMING_SINGLE_CONTACT_URI_INDICATOR:
qb.setTables(TABLE_NAME);
qb.setProjectionMap(sContactsProjectionMap);
qb.appendWhere(ContactColumns._ID + "="
+ uri.getLastPathSegment());
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
// If no sort order is specified use the default
String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
orderBy = ContactColumns.DEFAULT_SORT_ORDER;
} else {
orderBy = sortOrder;
}
// Get the database and run the query
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection,
selectionArgs, null, null, orderBy);
return c;
}
delete:
delete data on the basis of where argument,
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
case INCOMING_CONTACT_COLLECTION_URI_INDICATOR:
count = db.delete(TABLE_NAME,
where, whereArgs);
break;
case INCOMING_SINGLE_CONTACT_URI_INDICATOR:
String rowId = uri.getPathSegments().get(1);
count = db.delete(TABLE_NAME,
ContactColumns._ID + "=" + rowId
+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""),
whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
return count;
}
insert:
Insert data on the basis of contentvalues argument and return the uri of newly inserted row.
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
// Validate the requested uri
if (sUriMatcher.match(uri)
!= INCOMING_CONTACT_COLLECTION_URI_INDICATOR)
{
throw new IllegalArgumentException("Unknown URI " + uri);
}
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
Long now = Long.valueOf(System.currentTimeMillis());
// Make sure that the fields are all set
if (values.containsKey(ContactColumns.CREATED_DATE) == false)
{
values.put(ContactColumns.CREATED_DATE, now);
}
if (values.containsKey(ContactColumns.MODIFIED_DATE) == false)
{
values.put(ContactColumns.MODIFIED_DATE, now);
}
if (values.containsKey(ContactColumns.NAME) == false)
{
throw new SQLException(
"Failed to insert row because Name is needed " + uri);
}
if (values.containsKey(ContactColumns.PHONE_NUMBER) == false) {
values.put(ContactColumns.PHONE_NUMBER, "Unknown phone number");
}
if (values.containsKey(ContactColumns.EMAIL) == false) {
values.put(ContactColumns.EMAIL, "Unknown email");
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(TABLE_NAME,
ContactColumns.NAME, values);
if (rowId > 0) {
Uri insertedUri =
ContentUris.withAppendedId(
CONTENT_URI, rowId);
return insertedUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
update:
update data using contentvalues and where arguments.
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
case INCOMING_CONTACT_COLLECTION_URI_INDICATOR:
count = db.update(TABLE_NAME,
values, where, whereArgs);
break;
case INCOMING_SINGLE_CONTACT_URI_INDICATOR:
String rowId = uri.getPathSegments().get(1);
count = db.update(TABLE_NAME,
values, ContactColumns._ID + "=" + rowId
+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""),
whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
return count;
}
Now we will create ContectDataSouce class for contact operations like addContact, deleteContact.
ContactDataSource.java
public class ContactDataSource{
Context mContext;
public ContactDataSource(Context context){
mContext = context;
}
private static final String TAG = "ContactDataSource";
// Adding new contact
public void addContact(ContactsData contact){
Log.d(TAG,"Adding a book");
ContentValues values = new ContentValues();
values.put(ContactColumns.NAME, contact.mName); // Contact Name
values.put(ContactColumns.PHONE_NUMBER, contact.mPhoneNumber); // Contact phone number
values.put(ContactColumns.EMAIL, contact.mEmail); // Contact email
values.put(ContactColumns.ADDRESS, contact.mAddress); // Contact address
values.put(ContactColumns.DOB, contact.mDob); // Contact address
// Inserting Row
ContentResolver cr = mContext.getContentResolver();
Uri uri = ContactProvider.CONTENT_URI;
Log.d(TAG,"book insert uri:" + uri);
Uri insertedUri = cr.insert(uri, values);
Log.d(TAG,"inserted uri:" + insertedUri);
}
// Adding new contact
public ContactsData getContact(long contactId){
Log.d(TAG,"Adding a book");
ContentResolver cr = mContext.getContentResolver();
Uri uri = ContactProvider.CONTENT_URI;
Uri selUri = Uri.withAppendedPath(uri, Long.toString(contactId));
Cursor c = cr.query(selUri, null, null, null, null);
c.moveToFirst();
ContactsData contact = new ContactsData();
contact.mName = c.getString(c.getColumnIndex(ContactColumns.NAME));
contact.mPhoneNumber = c.getString(c.getColumnIndex(ContactColumns.PHONE_NUMBER));
contact.mEmail = c.getString(c.getColumnIndex(ContactColumns.EMAIL));
contact.mAddress = c.getString(c.getColumnIndex(ContactColumns.ADDRESS));
contact.mDob = c.getLong(c.getColumnIndex(ContactColumns.DOB));
return contact;
}
// Getting All Contacts
public List getAllContacts() {
List contactList = new ArrayList();
Uri uri = ContactProvider.CONTENT_URI;
Activity a = (Activity)mContext;
Cursor cursor = a.managedQuery(uri,
null, //projection
null, //selection string
null, //selection args array of strings
null); //sort order
while (cursor.moveToNext()) {
ContactsData contact = new ContactsData(Integer.parseInt(
cursor.getString(cursor.getColumnIndex(ContactColumns._ID))),
cursor.getString(cursor.getColumnIndex(ContactColumns.NAME)),
cursor.getString(cursor.getColumnIndex(ContactColumns.PHONE_NUMBER)),
cursor.getString(cursor.getColumnIndex(ContactColumns.EMAIL)),
cursor.getString(cursor.getColumnIndex(ContactColumns.ADDRESS)),
cursor.getLong(cursor.getColumnIndex(ContactColumns.DOB))
);
// Adding contact to list
contactList.add(contact);
}
// return contact list
return contactList;
}
// Getting contacts Count
public int getContactsCount() {
Uri uri = ContactProvider.CONTENT_URI;
Activity a = (Activity)mContext;
Cursor c = a.managedQuery(uri,
null, //projection
null, //selection string
null, //selection args array of strings
null); //sort order
int numberOfRecords = c.getCount();
c.close();
return numberOfRecords;
}
// Deleting single contact
public void deleteContact(long contactId) {
ContentResolver cr = mContext.getContentResolver();
Uri uri = ContactProvider.CONTENT_URI;
Uri delUri = Uri.withAppendedPath(uri, Long.toString(contactId));
Log.d(TAG, "Del Uri:" + delUri);
cr.delete(delUri, null, null);
}
}
Complete code of ContactProvider.java
public class ContactProvider extends ContentProvider {
private static final String TAG = "ContactProvider";
public static final String DATABASE_NAME = "contact.db";
public static final String TABLE_NAME = "contacts";
public static final String AUTHORITY = "com.vimviv.provider";
//uri and MIME type definitions
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/contacts");
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/vnd.com.vimviv.provider.contact";
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/vnd.com.vimviv.provider.contact";
//version 2: added dob column
public static final int DATABASE_VERSION = 2;
// Setup projection Map
// Projection maps are similar to "as" (column alias) construct
// in an sql statement where by you can rename the
// columns.
private static HashMap sContactsProjectionMap;
static
{
sContactsProjectionMap = new HashMap();
sContactsProjectionMap.put(ContactColumns._ID,
ContactColumns._ID);
// name, phone number, email,address
sContactsProjectionMap.put(ContactColumns.NAME,
ContactColumns.NAME);
sContactsProjectionMap.put(ContactColumns.PHONE_NUMBER,
ContactColumns.PHONE_NUMBER);
sContactsProjectionMap.put(ContactColumns.ADDRESS,
ContactColumns.ADDRESS);
sContactsProjectionMap.put(ContactColumns.EMAIL,
ContactColumns.EMAIL);
sContactsProjectionMap.put(ContactColumns.DOB,
ContactColumns.DOB);
// created date, modified date
sContactsProjectionMap.put(ContactColumns.CREATED_DATE,
ContactColumns.CREATED_DATE);
sContactsProjectionMap.put(ContactColumns.MODIFIED_DATE,
ContactColumns.MODIFIED_DATE);
}
// Setup URIs
// Provide a mechanism to identify
// all the incoming uri patterns.
private static final UriMatcher sUriMatcher;
private static final int INCOMING_CONTACT_COLLECTION_URI_INDICATOR = 1;
private static final int INCOMING_SINGLE_CONTACT_URI_INDICATOR = 2;
static {
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(AUTHORITY, "contacts",
INCOMING_CONTACT_COLLECTION_URI_INDICATOR);
sUriMatcher.addURI(AUTHORITY, "contacts/#",
INCOMING_SINGLE_CONTACT_URI_INDICATOR);
}
/**
* Setup/Create Database This class helps open, create, and upgrade the
* database file.
*/
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context,
DATABASE_NAME,
null,
DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db)
{
Log.d(TAG, "inner oncreate called");
db.execSQL("CREATE TABLE " + TABLE_NAME + " ("
+ ContactColumns._ID + " INTEGER PRIMARY KEY,"
+ ContactColumns.NAME + " TEXT,"
+ ContactColumns.PHONE_NUMBER + " TEXT,"
+ ContactColumns.EMAIL + " TEXT,"
+ ContactColumns.ADDRESS + " TEXT,"
+ ContactColumns.DOB + " INTEGER,"
+ ContactColumns.CREATED_DATE + " INTEGER,"
+ ContactColumns.MODIFIED_DATE + " INTEGER"
+ ");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
Log.d(TAG, "inner onupgrade called");
Log.w(TAG, "Upgrading database from version "
+ oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " +
TABLE_NAME);
onCreate(db);
}
}
private DatabaseHelper mOpenHelper;
@Override
public boolean onCreate() {
Log.d(TAG, "main onCreate called");
mOpenHelper = new DatabaseHelper(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
switch (sUriMatcher.match(uri)) {
case INCOMING_CONTACT_COLLECTION_URI_INDICATOR:
qb.setTables(TABLE_NAME);
qb.setProjectionMap(sContactsProjectionMap);
break;
case INCOMING_SINGLE_CONTACT_URI_INDICATOR:
qb.setTables(TABLE_NAME);
qb.setProjectionMap(sContactsProjectionMap);
qb.appendWhere(ContactColumns._ID + "="
+ uri.getLastPathSegment());
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
// If no sort order is specified use the default
String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
orderBy = ContactColumns.DEFAULT_SORT_ORDER;
} else {
orderBy = sortOrder;
}
// Get the database and run the query
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection,
selectionArgs, null, null, orderBy);
return c;
}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
case INCOMING_CONTACT_COLLECTION_URI_INDICATOR:
count = db.delete(TABLE_NAME,
where, whereArgs);
break;
case INCOMING_SINGLE_CONTACT_URI_INDICATOR:
String rowId = uri.getPathSegments().get(1);
count = db.delete(TABLE_NAME,
ContactColumns._ID + "=" + rowId
+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""),
whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
return count;
}
@Override
public String getType(Uri uri) {
switch (sUriMatcher.match(uri)) {
case INCOMING_CONTACT_COLLECTION_URI_INDICATOR:
return CONTENT_TYPE;
case INCOMING_SINGLE_CONTACT_URI_INDICATOR:
return CONTENT_ITEM_TYPE;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
// Validate the requested uri
if (sUriMatcher.match(uri)
!= INCOMING_CONTACT_COLLECTION_URI_INDICATOR)
{
throw new IllegalArgumentException("Unknown URI " + uri);
}
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
Long now = Long.valueOf(System.currentTimeMillis());
// Make sure that the fields are all set
if (values.containsKey(ContactColumns.CREATED_DATE) == false)
{
values.put(ContactColumns.CREATED_DATE, now);
}
if (values.containsKey(ContactColumns.MODIFIED_DATE) == false)
{
values.put(ContactColumns.MODIFIED_DATE, now);
}
if (values.containsKey(ContactColumns.NAME) == false)
{
throw new SQLException(
"Failed to insert row because Name is needed " + uri);
}
if (values.containsKey(ContactColumns.PHONE_NUMBER) == false) {
values.put(ContactColumns.PHONE_NUMBER, "Unknown phone number");
}
if (values.containsKey(ContactColumns.EMAIL) == false) {
values.put(ContactColumns.EMAIL, "Unknown email");
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId = db.insert(TABLE_NAME,
ContactColumns.NAME, values);
if (rowId > 0) {
Uri insertedUri =
ContentUris.withAppendedId(
CONTENT_URI, rowId);
return insertedUri;
}
throw new SQLException("Failed to insert row into " + uri);
}
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
case INCOMING_CONTACT_COLLECTION_URI_INDICATOR:
count = db.update(TABLE_NAME,
values, where, whereArgs);
break;
case INCOMING_SINGLE_CONTACT_URI_INDICATOR:
String rowId = uri.getPathSegments().get(1);
count = db.update(TABLE_NAME,
values, ContactColumns._ID + "=" + rowId
+ (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""),
whereArgs);
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
return count;
}
}